# (C) Copyright 2004-2021 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

""" Defines the various range editors for the wxPython user interface toolkit.
"""


import sys
import wx

from math import log10

from traits.api import TraitError, Str, Float, Any, Bool

from .editor_factory import TextEditor

from .editor import Editor

from .constants import OKColor, ErrorColor

from .helper import TraitsUIPanel, Slider


if not hasattr(wx, "wx.wxEVT_SCROLL_ENDSCROLL"):
    wxEVT_SCROLL_ENDSCROLL = wx.wxEVT_SCROLL_CHANGED
else:
    wxEVT_SCROLL_ENDSCROLL = wx.wxEVT_SCROLL_ENDSCROLL


# -------------------------------------------------------------------------
#  'BaseRangeEditor' class:
# -------------------------------------------------------------------------


class BaseRangeEditor(Editor):
    """ The base class for Range editors. Using an evaluate trait, if specified,
        when assigning numbers the object trait.
    """

    # -------------------------------------------------------------------------
    #  Trait definitions:
    # -------------------------------------------------------------------------

    #: Function to evaluate floats/ints
    evaluate = Any()

    def _set_value(self, value):
        if self.evaluate is not None:
            value = self.evaluate(value)
        Editor._set_value(self, value)


class SimpleSliderEditor(BaseRangeEditor):
    """ Simple style of range editor that displays a slider and a text field.

    The user can set a value either by moving the slider or by typing a value
    in the text field.
    """

    # -------------------------------------------------------------------------
    #  Trait definitions:
    # -------------------------------------------------------------------------

    #: Low value for the slider range
    low = Any()

    #: High value for the slider range
    high = Any()

    #: Formatting string used to format value and labels
    format = Str()

    #: Flag indicating that the UI is in the process of being updated
    ui_changing = Bool(False)

    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        factory = self.factory
        if not factory.low_name:
            self.low = factory.low

        if not factory.high_name:
            self.high = factory.high

        self.format = factory.format

        self.evaluate = factory.evaluate
        self.sync_value(factory.evaluate_name, "evaluate", "from")

        size = wx.DefaultSize
        if factory.label_width > 0:
            size = wx.Size(factory.label_width, 20)

        self.sync_value(factory.low_name, "low", "from")
        self.sync_value(factory.high_name, "high", "from")
        self.control = panel = TraitsUIPanel(parent, -1)
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        fvalue = self.value

        if not (self.low <= fvalue <= self.high):
            fvalue_text = ""
            fvalue = self.low
        else:
            try:
                fvalue_text = self.format % fvalue
            except (ValueError, TypeError) as e:
                fvalue_text = ""

        ivalue = self._convert_to_slider(fvalue)

        self._label_lo = wx.StaticText(
            panel,
            -1,
            "999999",
            size=size,
            style=wx.ALIGN_RIGHT | wx.ST_NO_AUTORESIZE,
        )
        sizer.Add(self._label_lo, 0, wx.ALIGN_CENTER)
        panel.slider = slider = Slider(
            panel,
            -1,
            ivalue,
            0,
            10000,
            size=wx.Size(80, 20),
            style=wx.SL_HORIZONTAL | wx.SL_AUTOTICKS,
        )
        slider.SetTickFreq(1000)
        slider.SetValue(1)
        slider.SetPageSize(1000)
        slider.SetLineSize(100)
        slider.Bind(wx.EVT_SCROLL, self.update_object_on_scroll)
        sizer.Add(slider, 1, wx.EXPAND)
        self._label_hi = wx.StaticText(panel, -1, "999999", size=size)
        sizer.Add(self._label_hi, 0, wx.ALIGN_CENTER)

        panel.text = text = wx.TextCtrl(
            panel,
            -1,
            fvalue_text,
            size=wx.Size(56, 20),
            style=wx.TE_PROCESS_ENTER,
        )
        panel.Bind(wx.EVT_TEXT_ENTER, self.update_object_on_enter, id=text.GetId())
        text.Bind(wx.EVT_KILL_FOCUS, self.update_object_on_enter)

        sizer.Add(text, 0, wx.LEFT | wx.EXPAND, 4)

        low_label = factory.low_label
        if factory.low_name != "":
            low_label = self.format % self.low

        high_label = factory.high_label
        if factory.high_name != "":
            high_label = self.format % self.high

        self._label_lo.SetLabel(low_label)
        self._label_hi.SetLabel(high_label)
        self.set_tooltip(slider)
        self.set_tooltip(self._label_lo)
        self.set_tooltip(self._label_hi)
        self.set_tooltip(text)

        # Set-up the layout:
        panel.SetSizerAndFit(sizer)

    def update_object_on_scroll(self, event):
        """ Handles the user changing the current slider value.
        """
        value = self._convert_from_slider(event.GetPosition())
        event_type = event.GetEventType()
        if (
            (event_type == wxEVT_SCROLL_ENDSCROLL)
            or (
                self.factory.auto_set
                and (event_type == wx.wxEVT_SCROLL_THUMBTRACK)
            )
            or (
                self.factory.enter_set
                and (event_type == wx.wxEVT_SCROLL_THUMBRELEASE)
            )
        ):
            try:
                self.ui_changing = True
                self.control.text.SetValue(self.format % value)
                self.value = value
            except TraitError:
                pass
            finally:
                self.ui_changing = False

    def update_object_on_enter(self, event):
        """ Handles the user pressing the Enter key in the text field.
        """
        if isinstance(event, wx.FocusEvent):
            event.Skip()

        # There are cases where this method is called with self.control ==
        # None.
        if self.control is None:
            return

        try:
            try:
                value = self.control.text.GetValue().strip()
                if self.factory.is_float:
                    value = float(value)
                else:
                    value = int(value)
            except Exception as ex:
                # The user entered something that didn't eval as a number (e.g., 'foo').
                # Pretend it didn't happen (i.e. do not change self.value).
                value = self.value
                self.control.text.SetValue(str(value))
                # for compound editor, value may be non-numeric
                if not isinstance(value, (int, float)):
                    return

            self.value = value
            if not self.ui_changing:
                self.control.slider.SetValue(
                    self._convert_to_slider(self.value)
                )
            self.control.text.SetBackgroundColour(OKColor)
            self.control.text.Refresh()
            if self._error is not None:
                self._error = None
                self.ui.errors -= 1
        except TraitError:
            pass

    def error(self, excp):
        """ Handles an error that occurs while setting the object's trait value.
        """
        if self._error is None:
            self._error = True
            self.ui.errors += 1
            super().error(excp)
        self.set_error_state(True)

    def update_editor(self):
        """ Updates the editor when the object trait changes externally to the
            editor.
        """
        value = self.value
        try:
            text = self.format % value
            1 // (self.low <= value <= self.high)
        except:
            text = ""
            value = self.low

        ivalue = self._convert_to_slider(value)
        self.control.text.SetValue(text)
        self.control.slider.SetValue(ivalue)

    def _convert_to_slider(self, value):
        """ Returns the slider setting corresponding to the user-supplied value.
        """
        if self.high > self.low:
            ivalue = int(
                (float(value - self.low) / (self.high - self.low)) * 10000.0
            )
        else:
            ivalue = self.low
        return ivalue

    def _convert_from_slider(self, ivalue):
        """ Returns the float or integer value corresponding to the slider
        setting.
        """
        value = self.low + ((float(ivalue) / 10000.0) * (self.high - self.low))
        if not self.factory.is_float:
            value = int(round(value))
        return value

    def get_error_control(self):
        """ Returns the editor's control for indicating error status.
        """
        return self.control.text

    def _low_changed(self, low):
        if self.value < low:
            if self.factory.is_float:
                self.value = float(low)
            else:
                self.value = int(low)

        if self._label_lo is not None:
            self._label_lo.SetLabel(self.format % low)
            self.update_editor()

    def _high_changed(self, high):
        if self.value > high:
            if self.factory.is_float:
                self.value = float(high)
            else:
                self.value = int(high)

        if self._label_hi is not None:
            self._label_hi.SetLabel(self.format % high)
            self.update_editor()


# -------------------------------------------------------------------------
class LogRangeSliderEditor(SimpleSliderEditor):
    # -------------------------------------------------------------------------
    """ A slider editor for log-spaced values
    """

    def _convert_to_slider(self, value):
        """ Returns the slider setting corresponding to the user-supplied value.
        """
        value = max(value, self.low)
        ivalue = int(
            (log10(value) - log10(self.low))
            / (log10(self.high) - log10(self.low))
            * 10000.0
        )
        return ivalue

    def _convert_from_slider(self, ivalue):
        """ Returns the float or integer value corresponding to the slider
        setting.
        """
        value = float(ivalue) / 10000.0 * (log10(self.high) - log10(self.low))
        # Do this to handle floating point errors, where fvalue may exceed
        # self.high.
        fvalue = min(self.low * 10 ** (value), self.high)
        if not self.factory.is_float:
            fvalue = int(round(fvalue))
        return fvalue


class LargeRangeSliderEditor(BaseRangeEditor):
    """ A slider editor for large ranges.

       The editor displays a slider and a text field. A subset of the total
       range is displayed in the slider; arrow buttons at each end of the
       slider let the user move the displayed range higher or lower.
    """

    # -------------------------------------------------------------------------
    #  Trait definitions:
    # -------------------------------------------------------------------------

    #: Low value for the slider range
    low = Any(0)

    #: High value for the slider range
    high = Any(1)

    #: Low end of displayed range
    cur_low = Float()

    #: High end of displayed range
    cur_high = Float()

    #: Flag indicating that the UI is in the process of being updated
    ui_changing = Bool(False)

    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        factory = self.factory

        # Initialize using the factory range defaults:
        self.low = factory.low
        self.high = factory.high
        self.evaluate = factory.evaluate

        # Hook up the traits to listen to the object.
        self.sync_value(factory.low_name, "low", "from")
        self.sync_value(factory.high_name, "high", "from")
        self.sync_value(factory.evaluate_name, "evaluate", "from")

        self.init_range()
        low = self.cur_low
        high = self.cur_high

        self._set_format()
        self.control = panel = TraitsUIPanel(parent, -1)
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        fvalue = self.value
        try:
            fvalue_text = self._format % fvalue
            1 // (low <= fvalue <= high)
        except:
            fvalue_text = ""
            fvalue = low

        if high > low:
            ivalue = int((float(fvalue - low) / (high - low)) * 10000)
        else:
            ivalue = low

        # Lower limit label:
        label_lo = wx.StaticText(panel, -1, "999999")
        panel.label_lo = label_lo
        sizer.Add(label_lo, 2, wx.ALIGN_CENTER)

        # Lower limit button:
        bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_BACK, size=(15, 15))
        button_lo = wx.BitmapButton(
            panel,
            -1,
            bitmap=bmp,
            size=(-1, 20),
            style=wx.BU_EXACTFIT | wx.NO_BORDER,
        )
        panel.button_lo = button_lo
        button_lo.Bind(wx.EVT_BUTTON, self.reduce_range)
        sizer.Add(button_lo, 1, wx.ALIGN_CENTER)

        # Slider:
        panel.slider = slider = Slider(
            panel,
            -1,
            ivalue,
            0,
            10000,
            size=wx.Size(80, 20),
            style=wx.SL_HORIZONTAL | wx.SL_AUTOTICKS,
        )
        slider.SetTickFreq(1000)
        slider.SetValue(1)
        slider.SetPageSize(1000)
        slider.SetLineSize(100)
        slider.Bind(wx.EVT_SCROLL, self.update_object_on_scroll)
        sizer.Add(slider, 6, wx.EXPAND)

        # Upper limit button:
        bmp = wx.ArtProvider.GetBitmap(wx.ART_GO_FORWARD, size=(15, 15))
        button_hi = wx.BitmapButton(
            panel,
            -1,
            bitmap=bmp,
            size=(-1, 20),
            style=wx.BU_EXACTFIT | wx.NO_BORDER,
        )
        panel.button_hi = button_hi
        button_hi.Bind(wx.EVT_BUTTON, self.increase_range)
        sizer.Add(button_hi, 1, wx.ALIGN_CENTER)

        # Upper limit label:
        label_hi = wx.StaticText(panel, -1, "999999")
        panel.label_hi = label_hi
        sizer.Add(label_hi, 2, wx.ALIGN_CENTER)

        # Text entry:
        panel.text = text = wx.TextCtrl(
            panel,
            -1,
            fvalue_text,
            size=wx.Size(56, 20),
            style=wx.TE_PROCESS_ENTER,
        )
        panel.Bind(wx.EVT_TEXT_ENTER, self.update_object_on_enter, id=text.GetId())
        text.Bind(wx.EVT_KILL_FOCUS, self.update_object_on_enter)

        sizer.Add(text, 0, wx.LEFT | wx.EXPAND, 4)

        # Set-up the layout:
        panel.SetSizerAndFit(sizer)
        label_lo.SetLabel(str(low))
        label_hi.SetLabel(str(high))
        self.set_tooltip(slider)
        self.set_tooltip(label_lo)
        self.set_tooltip(label_hi)
        self.set_tooltip(text)

        # Update the ranges and button just in case.
        self.update_range_ui()

    def update_object_on_scroll(self, event):
        """ Handles the user changing the current slider value.
        """
        low = self.cur_low
        high = self.cur_high
        value = low + ((float(event.GetPosition()) / 10000.0) * (high - low))
        self.control.text.SetValue(self._format % value)
        event_type = event.GetEventType()
        try:
            self.ui_changing = True
            if (
                (event_type == wxEVT_SCROLL_ENDSCROLL)
                or (
                    self.factory.auto_set
                    and (event_type == wx.wxEVT_SCROLL_THUMBTRACK)
                )
                or (
                    self.factory.enter_set
                    and (event_type == wx.wxEVT_SCROLL_THUMBRELEASE)
                )
            ):
                if self.factory.is_float:
                    self.value = value
                else:
                    self.value = int(value)
        finally:
            self.ui_changing = False

    def update_object_on_enter(self, event):
        """ Handles the user pressing the Enter key in the text field.
        """
        if isinstance(event, wx.FocusEvent):
            event.Skip()
        # It is possible the event is processed after the control is removed
        # from the editor
        if self.control is None:
            return
        try:
            value = self.control.text.GetValue().strip()
            try:
                if self.factory.is_float:
                    value = float(value)
                else:
                    value = int(value)
            except Exception as ex:
                # The user entered something that didn't eval as a number (e.g., 'foo').
                # Pretend it didn't happen (i.e. do not change self.value).
                value = self.value
                self.control.text.SetValue(str(value))

            self.value = value
            self.control.text.SetBackgroundColour(OKColor)
            self.control.text.Refresh()
            # Update the slider range.
            # Set ui_changing to True to avoid recursion:
            # the update_range_ui method will try to set the value in the text
            # box, which will again fire this method if auto_set is True.
            if not self.ui_changing:
                self.ui_changing = True
                self.init_range()
                self.update_range_ui()
                self.ui_changing = False
            if self._error is not None:
                self._error = None
                self.ui.errors -= 1
        except TraitError as excp:
            pass

    def error(self, excp):
        """ Handles an error that occurs while setting the object's trait value.
        """
        if self._error is None:
            self._error = True
            self.ui.errors += 1
            super().error(excp)
        self.set_error_state(True)

    def update_editor(self):
        """ Updates the editor when the object trait changes externally to the
            editor.
        """
        value = self.value
        low = self.low
        high = self.high
        try:
            text = self._format % value
            1 / (low <= value <= high)
        except:
            value = low
        self.value = value

        if not self.ui_changing:
            self.init_range()
            self.update_range_ui()

    def update_range_ui(self):
        """ Updates the slider range controls.
        """
        low, high = self.cur_low, self.cur_high
        value = self.value
        self._set_format()
        self.control.label_lo.SetLabel(self._format % low)
        self.control.label_hi.SetLabel(self._format % high)
        if high > low:
            ivalue = int((float(value - low) / (high - low)) * 10000.0)
        else:
            ivalue = low
        self.control.slider.SetValue(ivalue)
        text = self._format % self.value
        self.control.text.SetValue(text)
        factory = self.factory
        f_low, f_high = self.low, self.high

        if low == f_low:
            self.control.button_lo.Disable()
        else:
            self.control.button_lo.Enable()

        if high == f_high:
            self.control.button_hi.Disable()
        else:
            self.control.button_hi.Enable()

    def init_range(self):
        """ Initializes the slider range controls.
        """
        value = self.value
        factory = self.factory
        low, high = self.low, self.high
        if (high is None) and (low is not None):
            high = -low

        mag = abs(value)
        if mag <= 10.0:
            cur_low = max(value - 10, low)
            cur_high = min(value + 10, high)
        else:
            d = 0.5 * (10 ** int(log10(mag) + 1))
            cur_low = max(low, value - d)
            cur_high = min(high, value + d)

        self.cur_low, self.cur_high = cur_low, cur_high

    def reduce_range(self, event):
        """ Reduces the extent of the displayed range.
        """
        factory = self.factory
        low, high = self.low, self.high
        if abs(self.cur_low) < 10:
            self.cur_low = max(-10, low)
            self.cur_high = min(10, high)
        elif self.cur_low > 0:
            self.cur_high = self.cur_low
            self.cur_low = max(low, self.cur_low / 10)
        else:
            self.cur_high = self.cur_low
            self.cur_low = max(low, self.cur_low * 10)

        self.ui_changing = True
        self.value = min(max(self.value, self.cur_low), self.cur_high)
        self.ui_changing = False
        self.update_range_ui()

    def increase_range(self, event):
        """ Increased the extent of the displayed range.
        """
        factory = self.factory
        low, high = self.low, self.high
        if abs(self.cur_high) < 10:
            self.cur_low = max(-10, low)
            self.cur_high = min(10, high)
        elif self.cur_high > 0:
            self.cur_low = self.cur_high
            self.cur_high = min(high, self.cur_high * 10)
        else:
            self.cur_low = self.cur_high
            self.cur_high = min(high, self.cur_high / 10)

        self.ui_changing = True
        self.value = min(max(self.value, self.cur_low), self.cur_high)
        self.ui_changing = False
        self.update_range_ui()

    def _set_format(self):
        self._format = "%d"
        factory = self.factory
        low, high = self.cur_low, self.cur_high
        diff = high - low
        if factory.is_float:
            if diff > 99999:
                self._format = "%.2g"
            elif diff > 1:
                self._format = "%%.%df" % max(0, 4 - int(log10(high - low)))
            else:
                self._format = "%.3f"

    def get_error_control(self):
        """ Returns the editor's control for indicating error status.
        """
        return self.control.text

    def _low_changed(self, low):
        if self.control is not None:
            if self.value < low:
                if self.factory.is_float:
                    self.value = float(low)
                else:
                    self.value = int(low)

            self.update_editor()

    def _high_changed(self, high):
        if self.control is not None:
            if self.value > high:
                if self.factory.is_float:
                    self.value = float(high)
                else:
                    self.value = int(high)

            self.update_editor()


class SimpleSpinEditor(BaseRangeEditor):
    """ A simple style of range editor that displays a spin box control.
    """

    # -------------------------------------------------------------------------
    #  Trait definitions:
    # -------------------------------------------------------------------------

    #: Low value for the slider range
    low = Any()

    #: High value for the slider range
    high = Any()

    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        factory = self.factory
        if not factory.low_name:
            self.low = factory.low

        if not factory.high_name:
            self.high = factory.high

        self.sync_value(factory.low_name, "low", "from")
        self.sync_value(factory.high_name, "high", "from")
        low = self.low
        high = self.high
        self.control = wx.SpinCtrl(
            parent, -1, self.str_value, min=low, max=high, initial=self.value
        )
        parent.Bind(wx.EVT_SPINCTRL, self.update_object, id=self.control.GetId())
        self.set_tooltip()

    def update_object(self, event):
        """ Handles the user selecting a new value in the spin box.
        """
        if self.control is None:
            return
        self._locked = True
        try:
            self.value = self.control.GetValue()
        finally:
            self._locked = False

    def update_editor(self):
        """ Updates the editor when the object trait changes externally to the
            editor.
        """
        if not self._locked:
            try:
                self.control.SetValue(int(self.value))
            except:
                pass

    def _low_changed(self, low):
        if self.value < low:
            if self.factory.is_float:
                self.value = float(low)
            else:
                self.value = int(low)
        if self.control:
            self.control.SetRange(self.low, self.high)
            self.control.SetValue(int(self.value))

    def _high_changed(self, high):
        if self.value > high:
            if self.factory.is_float:
                self.value = float(high)
            else:
                self.value = int(high)
        if self.control:
            self.control.SetRange(self.low, self.high)
            self.control.SetValue(int(self.value))


class RangeTextEditor(TextEditor):
    """ Editor for ranges that displays a text field. If the user enters a
        value that is outside the allowed range, the background of the field
        changes color to indicate an error.
    """

    # -------------------------------------------------------------------------
    #  Trait definitions:
    # -------------------------------------------------------------------------

    #: Low value for the slider range
    low = Any()

    #: High value for the slider range
    high = Any()

    #: Function to evaluate floats/ints
    evaluate = Any()

    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        if not self.factory.low_name:
            self.low = self.factory.low

        if not self.factory.high_name:
            self.high = self.factory.high

        self.sync_value(self.factory.low_name, "low", "from")
        self.sync_value(self.factory.high_name, "high", "from")

        if self.factory.enter_set:
            control = wx.TextCtrl(
                parent, -1, self.str_value, style=wx.TE_PROCESS_ENTER
            )
            parent.Bind(wx.EVT_TEXT_ENTER, self.update_object, id=control.GetId())
        else:
            control = wx.TextCtrl(parent, -1, self.str_value)

        control.Bind(wx.EVT_KILL_FOCUS, self.update_object)

        if self.factory.auto_set:
            parent.Bind(wx.EVT_TEXT, self.update_object, id=control.GetId())

        self.evaluate = self.factory.evaluate
        self.sync_value(self.factory.evaluate_name, "evaluate", "from")

        self.control = control
        self.set_tooltip()

    def update_object(self, event):
        """ Handles the user entering input data in the edit control.
        """
        if isinstance(event, wx.FocusEvent):
            event.Skip()

        # It is possible the event is processed after the control is removed
        # from the editor
        if self.control is None:
            return

        value = self.control.GetValue()

        # Try to convert the string value entered by the user to a numerical
        # value.
        try:
            if self.evaluate is not None:
                value = self.evaluate(value)
            else:
                if self.factory.is_float:
                    value = float(value)
                else:
                    value = int(value)
        except Exception as excp:
            # The conversion failed.
            self.error(excp)
            return

        if value < self.low or value > self.high:
            self.set_error_state(True)
            return

        # Try to assign the numerical value to the trait.
        # This may fail because of constraints on the trait.
        try:
            self.value = value
            self.control.SetBackgroundColour(OKColor)
            self.control.Refresh()
            if self._error is not None:
                self._error = None
                self.ui.errors -= 1
        except TraitError as excp:
            pass

    def error(self, excp):
        """ Handles an error that occurs while setting the object's trait value.
        """
        if self._error is None:
            self._error = True
            self.ui.errors += 1
            super().error(excp)
        self.set_error_state(True)

    def _low_changed(self, low):
        if self.value < low:
            if self.factory.is_float:
                self.value = float(low)
            else:
                self.value = int(low)
        if self.control:
            self.control.SetValue(int(self.value))

    def _high_changed(self, high):
        if self.value > high:
            if self.factory.is_float:
                self.value = float(high)
            else:
                self.value = int(high)
        if self.control:
            self.control.SetValue(int(self.value))


def SimpleEnumEditor(
    parent, factory, ui, object, name, description, **kwargs
):
    return CustomEnumEditor(
        parent, factory, ui, object, name, description, "simple"
    )


def CustomEnumEditor(
    parent, factory, ui, object, name, description, style="custom", **kwargs
):
    """ Factory adapter that returns a enumeration editor of the specified
        style.
    """
    if factory._enum is None:
        import traitsui.editors.enum_editor as enum_editor

        factory._enum = enum_editor.EnumEditor(
            values=list(range(factory.low, factory.high + 1)),
            cols=factory.cols,
        )

    if style == "simple":
        return factory._enum.simple_editor(
            ui, object, name, description, parent
        )

    return factory._enum.custom_editor(ui, object, name, description, parent)


# -------------------------------------------------------------------------
#  Defines the mapping between editor factory 'mode's and Editor classes:
# -------------------------------------------------------------------------

# Mapping between editor factory modes and simple editor classes
SimpleEditorMap = {
    "slider": SimpleSliderEditor,
    "xslider": LargeRangeSliderEditor,
    "spinner": SimpleSpinEditor,
    "enum": SimpleEnumEditor,
    "text": RangeTextEditor,
    "logslider": LogRangeSliderEditor,
}
# Mapping between editor factory modes and custom editor classes
CustomEditorMap = {
    "slider": SimpleSliderEditor,
    "xslider": LargeRangeSliderEditor,
    "spinner": SimpleSpinEditor,
    "enum": CustomEnumEditor,
    "text": RangeTextEditor,
    "logslider": LogRangeSliderEditor,
}
