"""
This module provides some frequently useful convenience input widgets for nodes.
It replaces what was previously the dtypes system.
"""
from typing import Tuple

from ryvencore import Data

from ryvencore_qt import NodeInputWidget

from qtpy.QtWidgets import QLineEdit, QSpinBox, QCheckBox, QSlider
from qtpy.QtGui import QFont, QFontMetrics, Qt


class StdInputWidgetBase(NodeInputWidget):
    def __init__(self, params):
        super().__init__(params)

        self._prevent_update = PreventUpdateCtx(False)

    @property
    def val(self) -> Data:
        raise NotImplementedError

    def load_from(self, val: Data):
        raise NotImplementedError

    def on_widget_val_changed(self, val: Data):
        """
        This will update the input's default value, and also update
        the node unless it is called in a `with self._prevent_update`
        block.
        """
        self.update_node_input(val, silent=self._prevent_update.blocked)

    def get_state(self) -> dict:
        return {'val': self.val}

    def set_state(self, data: dict):
        with self._prevent_update:
            self.load_from(data['val'])


class PreventUpdateCtx:
    def __init__(self, initial: bool):
        self._blocked: bool = initial

    @property
    def blocked(self) -> bool:
        return self._blocked

    def __enter__(self):
        self._blocked = True

    def __exit__(self, exc_type, exc_val, exc_tb):
        self._blocked = False


class Builder:
    """
    Provides functions for creating parametrized input widgets.
    """

    @staticmethod
    def evaled_line_edit(init=None, size='m', descr: str = '', resizing: bool = True, data_type: type[Data] = Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """

        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:  # size == 'l':
            base_width = 150
        max_width = base_width * 3 if resizing else base_width

        class StdInpWidget_EvaledLineEdit(StdInputWidgetBase, QLineEdit):
            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)

                # set size
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)

                # font
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())

                # tooltip
                self.setToolTip(self.__doc__)

                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)

                # initial value
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15  # add some buffer
                    self.setFixedWidth(min(
                        max(new_width, self.base_width),
                        self.max_width
                    ))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) -> data_type:
                try:
                    return data_type(eval(self.text()))
                except:
                    return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                # represent incoming data
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")

        StdInpWidget_EvaledLineEdit.__doc__ = descr

        return StdInpWidget_EvaledLineEdit

    @staticmethod
    def str_line_edit(init: str = '', size='m', descr: str = '', resizing: bool = True, data_type: type[Data] = Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """

        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:  # size == 'l':
            base_width = 150
        max_width = base_width * 3 if resizing else base_width

        class StdInpWidget_StrLineEdit(StdInputWidgetBase, QLineEdit):
            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)

                # set size
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)

                # font
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())

                # tooltip
                self.setToolTip(self.__doc__)

                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)

                # initial value
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15  # add some buffer
                    self.setFixedWidth(min(
                        max(new_width, self.base_width),
                        self.max_width
                    ))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) -> data_type:
                return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                # represent incoming data
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")

        StdInpWidget_StrLineEdit.__doc__ = descr

        return StdInpWidget_StrLineEdit

    @staticmethod
    def int_spinbox(init: int = 0, range: Tuple[int, int] = (0, 99), descr: str = '', data_type: type[Data] = Data):
        """
        Creates a spinbox input widget for integers.
        :param init: the initial value shown in the widget
        :param range: the range of the spinbox
        :param descr: the description of the input
        """

        class StdInpWidget_IntSpinBox(StdInputWidgetBase, QSpinBox):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSpinBox.__init__(self)

                # there is no 'valueEdited' signal, only a 'valueChanged' signal
                # so we need to block the signal when a new value is coming from
                # a connection
                self._prevent_update = PreventUpdateCtx(False)

                # tooltip
                self.setToolTip(self.__doc__)

                self.valueChanged.connect(self.value_changed)

                # initial value and rage
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)


            @property
            def val(self) -> data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    with self._prevent_update:
                        self.setValue(val.payload)

        StdInpWidget_IntSpinBox.__doc__ = descr

        return StdInpWidget_IntSpinBox

    @staticmethod
    def int_slider(init: int = 0, range: Tuple[int, int] = (0, 10), descr: str = '', data_type: type[Data] = Data):
        """
        Creates a slider input widget for ints.
        :param init: the initial value shown in the widget
        :param range: the range of the slider
        :param descr: the description of the input
        """

        class StdInpWidget_IntSlider(StdInputWidgetBase, QSlider):
            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSlider.__init__(self, Qt.Horizontal)

                # tooltip
                self.setToolTip(self.__doc__)

                self.valueChanged.connect(self.value_changed)

                # initial value and rage
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)

            @property
            def val(self) -> data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    self.setValue(val.payload)

        StdInpWidget_IntSlider.__doc__ = descr

        return StdInpWidget_IntSlider

    @staticmethod
    def bool_checkbox(init: bool = False, descr: str = '', data_type: type[Data] = Data):
        """
        Creates a checkbox input widget for booleans.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        """

        class StdInpWidget_BoolCheckBox(StdInputWidgetBase, QCheckBox):
            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QCheckBox.__init__(self)

                # tooltip
                self.setToolTip(self.__doc__)

                self.stateChanged.connect(self.state_changed)

                # initial value
                with self._prevent_update:
                    self.setChecked(init)

            @property
            def val(self) -> data_type:
                return data_type(self.isChecked())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setChecked(val.payload)

            def state_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, bool):
                    with self._prevent_update:
                        self.setChecked(val.payload)

        StdInpWidget_BoolCheckBox.__doc__ = descr

        return StdInpWidget_BoolCheckBox

68f223538daa53d0ff7bddc3ab4beaed680ca122
<mask token>


class PreventUpdateCtx:
    <mask token>
    <mask token>
    <mask token>
    <mask token>


class Builder:
    """
    Provides functions for creating parametrized input widgets.
    """

    @staticmethod
    def evaled_line_edit(init=None, size='m', descr: str='', resizing: bool
        =True, data_type: type[Data]=Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """
        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:
            base_width = 150
        max_width = base_width * 3 if resizing else base_width


        class StdInpWidget_EvaledLineEdit(StdInputWidgetBase, QLineEdit):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())
                self.setToolTip(self.__doc__)
                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15
                    self.setFixedWidth(min(max(new_width, self.base_width),
                        self.max_width))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) ->data_type:
                try:
                    return data_type(eval(self.text()))
                except:
                    return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")
        StdInpWidget_EvaledLineEdit.__doc__ = descr
        return StdInpWidget_EvaledLineEdit

    @staticmethod
    def str_line_edit(init: str='', size='m', descr: str='', resizing: bool
        =True, data_type: type[Data]=Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """
        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:
            base_width = 150
        max_width = base_width * 3 if resizing else base_width


        class StdInpWidget_StrLineEdit(StdInputWidgetBase, QLineEdit):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())
                self.setToolTip(self.__doc__)
                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15
                    self.setFixedWidth(min(max(new_width, self.base_width),
                        self.max_width))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) ->data_type:
                return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")
        StdInpWidget_StrLineEdit.__doc__ = descr
        return StdInpWidget_StrLineEdit

    @staticmethod
    def int_spinbox(init: int=0, range: Tuple[int, int]=(0, 99), descr: str
        ='', data_type: type[Data]=Data):
        """
        Creates a spinbox input widget for integers.
        :param init: the initial value shown in the widget
        :param range: the range of the spinbox
        :param descr: the description of the input
        """


        class StdInpWidget_IntSpinBox(StdInputWidgetBase, QSpinBox):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSpinBox.__init__(self)
                self._prevent_update = PreventUpdateCtx(False)
                self.setToolTip(self.__doc__)
                self.valueChanged.connect(self.value_changed)
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)

            @property
            def val(self) ->data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    with self._prevent_update:
                        self.setValue(val.payload)
        StdInpWidget_IntSpinBox.__doc__ = descr
        return StdInpWidget_IntSpinBox

    @staticmethod
    def int_slider(init: int=0, range: Tuple[int, int]=(0, 10), descr: str=
        '', data_type: type[Data]=Data):
        """
        Creates a slider input widget for ints.
        :param init: the initial value shown in the widget
        :param range: the range of the slider
        :param descr: the description of the input
        """


        class StdInpWidget_IntSlider(StdInputWidgetBase, QSlider):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSlider.__init__(self, Qt.Horizontal)
                self.setToolTip(self.__doc__)
                self.valueChanged.connect(self.value_changed)
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)

            @property
            def val(self) ->data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    self.setValue(val.payload)
        StdInpWidget_IntSlider.__doc__ = descr
        return StdInpWidget_IntSlider

    @staticmethod
    def bool_checkbox(init: bool=False, descr: str='', data_type: type[Data
        ]=Data):
        """
        Creates a checkbox input widget for booleans.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        """


        class StdInpWidget_BoolCheckBox(StdInputWidgetBase, QCheckBox):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QCheckBox.__init__(self)
                self.setToolTip(self.__doc__)
                self.stateChanged.connect(self.state_changed)
                with self._prevent_update:
                    self.setChecked(init)

            @property
            def val(self) ->data_type:
                return data_type(self.isChecked())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setChecked(val.payload)

            def state_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, bool):
                    with self._prevent_update:
                        self.setChecked(val.payload)
        StdInpWidget_BoolCheckBox.__doc__ = descr
        return StdInpWidget_BoolCheckBox

<mask token>


class PreventUpdateCtx:

    def __init__(self, initial: bool):
        self._blocked: bool = initial

    @property
    def blocked(self) ->bool:
        return self._blocked

    def __enter__(self):
        self._blocked = True

    def __exit__(self, exc_type, exc_val, exc_tb):
        self._blocked = False


class Builder:
    """
    Provides functions for creating parametrized input widgets.
    """

    @staticmethod
    def evaled_line_edit(init=None, size='m', descr: str='', resizing: bool
        =True, data_type: type[Data]=Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """
        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:
            base_width = 150
        max_width = base_width * 3 if resizing else base_width


        class StdInpWidget_EvaledLineEdit(StdInputWidgetBase, QLineEdit):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())
                self.setToolTip(self.__doc__)
                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15
                    self.setFixedWidth(min(max(new_width, self.base_width),
                        self.max_width))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) ->data_type:
                try:
                    return data_type(eval(self.text()))
                except:
                    return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")
        StdInpWidget_EvaledLineEdit.__doc__ = descr
        return StdInpWidget_EvaledLineEdit

    @staticmethod
    def str_line_edit(init: str='', size='m', descr: str='', resizing: bool
        =True, data_type: type[Data]=Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """
        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:
            base_width = 150
        max_width = base_width * 3 if resizing else base_width


        class StdInpWidget_StrLineEdit(StdInputWidgetBase, QLineEdit):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())
                self.setToolTip(self.__doc__)
                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15
                    self.setFixedWidth(min(max(new_width, self.base_width),
                        self.max_width))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) ->data_type:
                return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")
        StdInpWidget_StrLineEdit.__doc__ = descr
        return StdInpWidget_StrLineEdit

    @staticmethod
    def int_spinbox(init: int=0, range: Tuple[int, int]=(0, 99), descr: str
        ='', data_type: type[Data]=Data):
        """
        Creates a spinbox input widget for integers.
        :param init: the initial value shown in the widget
        :param range: the range of the spinbox
        :param descr: the description of the input
        """


        class StdInpWidget_IntSpinBox(StdInputWidgetBase, QSpinBox):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSpinBox.__init__(self)
                self._prevent_update = PreventUpdateCtx(False)
                self.setToolTip(self.__doc__)
                self.valueChanged.connect(self.value_changed)
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)

            @property
            def val(self) ->data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    with self._prevent_update:
                        self.setValue(val.payload)
        StdInpWidget_IntSpinBox.__doc__ = descr
        return StdInpWidget_IntSpinBox

    @staticmethod
    def int_slider(init: int=0, range: Tuple[int, int]=(0, 10), descr: str=
        '', data_type: type[Data]=Data):
        """
        Creates a slider input widget for ints.
        :param init: the initial value shown in the widget
        :param range: the range of the slider
        :param descr: the description of the input
        """


        class StdInpWidget_IntSlider(StdInputWidgetBase, QSlider):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSlider.__init__(self, Qt.Horizontal)
                self.setToolTip(self.__doc__)
                self.valueChanged.connect(self.value_changed)
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)

            @property
            def val(self) ->data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    self.setValue(val.payload)
        StdInpWidget_IntSlider.__doc__ = descr
        return StdInpWidget_IntSlider

    @staticmethod
    def bool_checkbox(init: bool=False, descr: str='', data_type: type[Data
        ]=Data):
        """
        Creates a checkbox input widget for booleans.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        """


        class StdInpWidget_BoolCheckBox(StdInputWidgetBase, QCheckBox):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QCheckBox.__init__(self)
                self.setToolTip(self.__doc__)
                self.stateChanged.connect(self.state_changed)
                with self._prevent_update:
                    self.setChecked(init)

            @property
            def val(self) ->data_type:
                return data_type(self.isChecked())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setChecked(val.payload)

            def state_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, bool):
                    with self._prevent_update:
                        self.setChecked(val.payload)
        StdInpWidget_BoolCheckBox.__doc__ = descr
        return StdInpWidget_BoolCheckBox

<mask token>


class StdInputWidgetBase(NodeInputWidget):

    def __init__(self, params):
        super().__init__(params)
        self._prevent_update = PreventUpdateCtx(False)

    @property
    def val(self) ->Data:
        raise NotImplementedError

    def load_from(self, val: Data):
        raise NotImplementedError
    <mask token>
    <mask token>
    <mask token>


class PreventUpdateCtx:

    def __init__(self, initial: bool):
        self._blocked: bool = initial

    @property
    def blocked(self) ->bool:
        return self._blocked

    def __enter__(self):
        self._blocked = True

    def __exit__(self, exc_type, exc_val, exc_tb):
        self._blocked = False


class Builder:
    """
    Provides functions for creating parametrized input widgets.
    """

    @staticmethod
    def evaled_line_edit(init=None, size='m', descr: str='', resizing: bool
        =True, data_type: type[Data]=Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """
        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:
            base_width = 150
        max_width = base_width * 3 if resizing else base_width


        class StdInpWidget_EvaledLineEdit(StdInputWidgetBase, QLineEdit):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())
                self.setToolTip(self.__doc__)
                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15
                    self.setFixedWidth(min(max(new_width, self.base_width),
                        self.max_width))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) ->data_type:
                try:
                    return data_type(eval(self.text()))
                except:
                    return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")
        StdInpWidget_EvaledLineEdit.__doc__ = descr
        return StdInpWidget_EvaledLineEdit

    @staticmethod
    def str_line_edit(init: str='', size='m', descr: str='', resizing: bool
        =True, data_type: type[Data]=Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """
        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:
            base_width = 150
        max_width = base_width * 3 if resizing else base_width


        class StdInpWidget_StrLineEdit(StdInputWidgetBase, QLineEdit):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())
                self.setToolTip(self.__doc__)
                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15
                    self.setFixedWidth(min(max(new_width, self.base_width),
                        self.max_width))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) ->data_type:
                return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")
        StdInpWidget_StrLineEdit.__doc__ = descr
        return StdInpWidget_StrLineEdit

    @staticmethod
    def int_spinbox(init: int=0, range: Tuple[int, int]=(0, 99), descr: str
        ='', data_type: type[Data]=Data):
        """
        Creates a spinbox input widget for integers.
        :param init: the initial value shown in the widget
        :param range: the range of the spinbox
        :param descr: the description of the input
        """


        class StdInpWidget_IntSpinBox(StdInputWidgetBase, QSpinBox):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSpinBox.__init__(self)
                self._prevent_update = PreventUpdateCtx(False)
                self.setToolTip(self.__doc__)
                self.valueChanged.connect(self.value_changed)
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)

            @property
            def val(self) ->data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    with self._prevent_update:
                        self.setValue(val.payload)
        StdInpWidget_IntSpinBox.__doc__ = descr
        return StdInpWidget_IntSpinBox

    @staticmethod
    def int_slider(init: int=0, range: Tuple[int, int]=(0, 10), descr: str=
        '', data_type: type[Data]=Data):
        """
        Creates a slider input widget for ints.
        :param init: the initial value shown in the widget
        :param range: the range of the slider
        :param descr: the description of the input
        """


        class StdInpWidget_IntSlider(StdInputWidgetBase, QSlider):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSlider.__init__(self, Qt.Horizontal)
                self.setToolTip(self.__doc__)
                self.valueChanged.connect(self.value_changed)
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)

            @property
            def val(self) ->data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    self.setValue(val.payload)
        StdInpWidget_IntSlider.__doc__ = descr
        return StdInpWidget_IntSlider

    @staticmethod
    def bool_checkbox(init: bool=False, descr: str='', data_type: type[Data
        ]=Data):
        """
        Creates a checkbox input widget for booleans.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        """


        class StdInpWidget_BoolCheckBox(StdInputWidgetBase, QCheckBox):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QCheckBox.__init__(self)
                self.setToolTip(self.__doc__)
                self.stateChanged.connect(self.state_changed)
                with self._prevent_update:
                    self.setChecked(init)

            @property
            def val(self) ->data_type:
                return data_type(self.isChecked())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setChecked(val.payload)

            def state_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, bool):
                    with self._prevent_update:
                        self.setChecked(val.payload)
        StdInpWidget_BoolCheckBox.__doc__ = descr
        return StdInpWidget_BoolCheckBox

<mask token>


class StdInputWidgetBase(NodeInputWidget):

    def __init__(self, params):
        super().__init__(params)
        self._prevent_update = PreventUpdateCtx(False)

    @property
    def val(self) ->Data:
        raise NotImplementedError

    def load_from(self, val: Data):
        raise NotImplementedError

    def on_widget_val_changed(self, val: Data):
        """
        This will update the input's default value, and also update
        the node unless it is called in a `with self._prevent_update`
        block.
        """
        self.update_node_input(val, silent=self._prevent_update.blocked)
    <mask token>
    <mask token>


class PreventUpdateCtx:

    def __init__(self, initial: bool):
        self._blocked: bool = initial

    @property
    def blocked(self) ->bool:
        return self._blocked

    def __enter__(self):
        self._blocked = True

    def __exit__(self, exc_type, exc_val, exc_tb):
        self._blocked = False


class Builder:
    """
    Provides functions for creating parametrized input widgets.
    """

    @staticmethod
    def evaled_line_edit(init=None, size='m', descr: str='', resizing: bool
        =True, data_type: type[Data]=Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """
        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:
            base_width = 150
        max_width = base_width * 3 if resizing else base_width


        class StdInpWidget_EvaledLineEdit(StdInputWidgetBase, QLineEdit):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())
                self.setToolTip(self.__doc__)
                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15
                    self.setFixedWidth(min(max(new_width, self.base_width),
                        self.max_width))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) ->data_type:
                try:
                    return data_type(eval(self.text()))
                except:
                    return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")
        StdInpWidget_EvaledLineEdit.__doc__ = descr
        return StdInpWidget_EvaledLineEdit

    @staticmethod
    def str_line_edit(init: str='', size='m', descr: str='', resizing: bool
        =True, data_type: type[Data]=Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """
        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:
            base_width = 150
        max_width = base_width * 3 if resizing else base_width


        class StdInpWidget_StrLineEdit(StdInputWidgetBase, QLineEdit):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())
                self.setToolTip(self.__doc__)
                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15
                    self.setFixedWidth(min(max(new_width, self.base_width),
                        self.max_width))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) ->data_type:
                return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")
        StdInpWidget_StrLineEdit.__doc__ = descr
        return StdInpWidget_StrLineEdit

    @staticmethod
    def int_spinbox(init: int=0, range: Tuple[int, int]=(0, 99), descr: str
        ='', data_type: type[Data]=Data):
        """
        Creates a spinbox input widget for integers.
        :param init: the initial value shown in the widget
        :param range: the range of the spinbox
        :param descr: the description of the input
        """


        class StdInpWidget_IntSpinBox(StdInputWidgetBase, QSpinBox):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSpinBox.__init__(self)
                self._prevent_update = PreventUpdateCtx(False)
                self.setToolTip(self.__doc__)
                self.valueChanged.connect(self.value_changed)
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)

            @property
            def val(self) ->data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    with self._prevent_update:
                        self.setValue(val.payload)
        StdInpWidget_IntSpinBox.__doc__ = descr
        return StdInpWidget_IntSpinBox

    @staticmethod
    def int_slider(init: int=0, range: Tuple[int, int]=(0, 10), descr: str=
        '', data_type: type[Data]=Data):
        """
        Creates a slider input widget for ints.
        :param init: the initial value shown in the widget
        :param range: the range of the slider
        :param descr: the description of the input
        """


        class StdInpWidget_IntSlider(StdInputWidgetBase, QSlider):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSlider.__init__(self, Qt.Horizontal)
                self.setToolTip(self.__doc__)
                self.valueChanged.connect(self.value_changed)
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)

            @property
            def val(self) ->data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    self.setValue(val.payload)
        StdInpWidget_IntSlider.__doc__ = descr
        return StdInpWidget_IntSlider

    @staticmethod
    def bool_checkbox(init: bool=False, descr: str='', data_type: type[Data
        ]=Data):
        """
        Creates a checkbox input widget for booleans.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        """


        class StdInpWidget_BoolCheckBox(StdInputWidgetBase, QCheckBox):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QCheckBox.__init__(self)
                self.setToolTip(self.__doc__)
                self.stateChanged.connect(self.state_changed)
                with self._prevent_update:
                    self.setChecked(init)

            @property
            def val(self) ->data_type:
                return data_type(self.isChecked())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setChecked(val.payload)

            def state_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, bool):
                    with self._prevent_update:
                        self.setChecked(val.payload)
        StdInpWidget_BoolCheckBox.__doc__ = descr
        return StdInpWidget_BoolCheckBox

"""
This module provides some frequently useful convenience input widgets for nodes.
It replaces what was previously the dtypes system.
"""
from typing import Tuple

from ryvencore import Data

from ryvencore_qt import NodeInputWidget

from qtpy.QtWidgets import QLineEdit, QSpinBox, QCheckBox, QSlider
from qtpy.QtGui import QFont, QFontMetrics, Qt


class StdInputWidgetBase(NodeInputWidget):
    def __init__(self, params):
        super().__init__(params)

        self._prevent_update = PreventUpdateCtx(False)

    @property
    def val(self) -> Data:
        raise NotImplementedError

    def load_from(self, val: Data):
        raise NotImplementedError

    def on_widget_val_changed(self, val: Data):
        """
        This will update the input's default value, and also update
        the node unless it is called in a `with self._prevent_update`
        block.
        """
        self.update_node_input(val, silent=self._prevent_update.blocked)

    def get_state(self) -> dict:
        return {'val': self.val}

    def set_state(self, data: dict):
        with self._prevent_update:
            self.load_from(data['val'])


class PreventUpdateCtx:
    def __init__(self, initial: bool):
        self._blocked: bool = initial

    @property
    def blocked(self) -> bool:
        return self._blocked

    def __enter__(self):
        self._blocked = True

    def __exit__(self, exc_type, exc_val, exc_tb):
        self._blocked = False


class Builder:
    """
    Provides functions for creating parametrized input widgets.
    """

    @staticmethod
    def evaled_line_edit(init=None, size='m', descr: str = '', resizing: bool = True, data_type: type[Data] = Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """

        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:  # size == 'l':
            base_width = 150
        max_width = base_width * 3 if resizing else base_width

        class StdInpWidget_EvaledLineEdit(StdInputWidgetBase, QLineEdit):
            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)

                # set size
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)

                # font
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())

                # tooltip
                self.setToolTip(self.__doc__)

                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)

                # initial value
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15  # add some buffer
                    self.setFixedWidth(min(
                        max(new_width, self.base_width),
                        self.max_width
                    ))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) -> data_type:
                try:
                    return data_type(eval(self.text()))
                except:
                    return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                # represent incoming data
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")

        StdInpWidget_EvaledLineEdit.__doc__ = descr

        return StdInpWidget_EvaledLineEdit

    @staticmethod
    def str_line_edit(init: str = '', size='m', descr: str = '', resizing: bool = True, data_type: type[Data] = Data):
        """
        Creates a line edit input widget which evaluates its input.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        :param size: 's', 'm' or 'l'
        """

        if size == 's':
            base_width = 30
        elif size == 'm':
            base_width = 70
        else:  # size == 'l':
            base_width = 150
        max_width = base_width * 3 if resizing else base_width

        class StdInpWidget_StrLineEdit(StdInputWidgetBase, QLineEdit):
            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QLineEdit.__init__(self)

                # set size
                self.base_width = base_width
                self.max_width = max_width
                self.setFixedWidth(self.base_width)

                # font
                self.setFont(QFont('source code pro', 10))
                self.fm = QFontMetrics(self.font())

                # tooltip
                self.setToolTip(self.__doc__)

                self.textChanged.connect(self.text_changed)
                self.returnPressed.connect(self.return_pressed)

                # initial value
                with self._prevent_update:
                    self.setText(str(init))

            def return_pressed(self):
                self.on_widget_val_changed(self.val)

            def text_changed(self, new_text):
                """Manages resizing of the widget to content."""
                if resizing:
                    text_width = self.fm.width(new_text)
                    new_width = text_width + 15  # add some buffer
                    self.setFixedWidth(min(
                        max(new_width, self.base_width),
                        self.max_width
                    ))
                    self.node_gui.update_shape()
                self.on_widget_val_changed(self.val)

            @property
            def val(self) -> data_type:
                return data_type(self.text())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setText(str(val.payload))

            def val_update_event(self, val: Data):
                # represent incoming data
                try:
                    with self._prevent_update:
                        self.setText(str(val.payload))
                except:
                    self.setText("<can't stringify>")

        StdInpWidget_StrLineEdit.__doc__ = descr

        return StdInpWidget_StrLineEdit

    @staticmethod
    def int_spinbox(init: int = 0, range: Tuple[int, int] = (0, 99), descr: str = '', data_type: type[Data] = Data):
        """
        Creates a spinbox input widget for integers.
        :param init: the initial value shown in the widget
        :param range: the range of the spinbox
        :param descr: the description of the input
        """

        class StdInpWidget_IntSpinBox(StdInputWidgetBase, QSpinBox):

            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSpinBox.__init__(self)

                # there is no 'valueEdited' signal, only a 'valueChanged' signal
                # so we need to block the signal when a new value is coming from
                # a connection
                self._prevent_update = PreventUpdateCtx(False)

                # tooltip
                self.setToolTip(self.__doc__)

                self.valueChanged.connect(self.value_changed)

                # initial value and rage
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)


            @property
            def val(self) -> data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    with self._prevent_update:
                        self.setValue(val.payload)

        StdInpWidget_IntSpinBox.__doc__ = descr

        return StdInpWidget_IntSpinBox

    @staticmethod
    def int_slider(init: int = 0, range: Tuple[int, int] = (0, 10), descr: str = '', data_type: type[Data] = Data):
        """
        Creates a slider input widget for ints.
        :param init: the initial value shown in the widget
        :param range: the range of the slider
        :param descr: the description of the input
        """

        class StdInpWidget_IntSlider(StdInputWidgetBase, QSlider):
            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QSlider.__init__(self, Qt.Horizontal)

                # tooltip
                self.setToolTip(self.__doc__)

                self.valueChanged.connect(self.value_changed)

                # initial value and rage
                with self._prevent_update:
                    self.setRange(*range)
                    self.setValue(init)

            @property
            def val(self) -> data_type:
                return data_type(self.value())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setValue(val.payload)

            def value_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, int):
                    self.setValue(val.payload)

        StdInpWidget_IntSlider.__doc__ = descr

        return StdInpWidget_IntSlider

    @staticmethod
    def bool_checkbox(init: bool = False, descr: str = '', data_type: type[Data] = Data):
        """
        Creates a checkbox input widget for booleans.
        :param init: the initial value shown in the widget
        :param descr: the description of the input
        """

        class StdInpWidget_BoolCheckBox(StdInputWidgetBase, QCheckBox):
            def __init__(self, params):
                StdInputWidgetBase.__init__(self, params)
                QCheckBox.__init__(self)

                # tooltip
                self.setToolTip(self.__doc__)

                self.stateChanged.connect(self.state_changed)

                # initial value
                with self._prevent_update:
                    self.setChecked(init)

            @property
            def val(self) -> data_type:
                return data_type(self.isChecked())

            def load_from(self, val: Data):
                with self._prevent_update:
                    self.setChecked(val.payload)

            def state_changed(self, _):
                self.on_widget_val_changed(self.val)

            def val_update_event(self, val: Data):
                if isinstance(val.payload, bool):
                    with self._prevent_update:
                        self.setChecked(val.payload)

        StdInpWidget_BoolCheckBox.__doc__ = descr

        return StdInpWidget_BoolCheckBox
