-- Copyright © 2024 @Groick (Twitch). All rights reserved.
local function L(key, ...) return EasyOptions:Localize(key, ...) end
function EasyOptions:CreateTextBox(parent, customControlName, value, minValue, maxValue, formatString, labelText, tooltipTitleText, tooltipText, positionInfo, customSize, isNumeric, isMultiLine, delimiterInfo, customFontInfo, customTextColor, onTextChangedHandler, onTextAcceptedHandler, onEscapePressedHandler)
  -- Create the control and set properties.
  local controlTypeName
  if (isNumeric) then
    controlTypeName = "ValueBox"
  elseif (isMultiLine) then
    controlTypeName = "MultiLineTextBox"
  else
    controlTypeName = "TextBox"
  end
  local controlNameDefault = controlTypeName
  local controlName = EasyOptions:GetControlName(controlNameDefault, customControlName)
  local control
  if (not isMultiLine) then
    control = CreateFrame("EditBox", controlName, parent, "InputBoxTemplate")
    control.TooltipOwner = control
  else
    local showBackground = true
    local showBorder = true
    local positionScrollFrameInsideBorderFrame = false
    local customScrollFramePadding = {6, 0, 0, 0} -- {topLeftPaddingX, topLeftPaddingY, bottomRightPaddingX, bottomRightPaddingY}
    local onScrollHandler = function(scrollFrame, offset, isHorizontalScroll)
      local control = scrollFrame.Control
      control:SetControlPointToFillBorderFrameWidth() -- IMPORTANT: This is required because scrolling a control seems to clear the control's anchor points.
    end
    local controlLocal, borderFrame, scrollFrame = EasyOptions:CreateControlContainedInScrollFrame("EditBox", parent, controlName, showBackground, showBorder, 1, 1, positionScrollFrameInsideBorderFrame, customScrollFramePadding, onScrollHandler) -- NOTE: The control's size is set later in this method (see below).
    control = controlLocal
    borderFrame.TextBox = control
    scrollFrame.TextBox = control
    control.TooltipOwner = borderFrame -- IMPORTANT: We anchor the tooltip to borderFrame rather than to the MultiLineTextBox because if we anchor the tooltip to the MultiLineTextBox then the tooltip's position will be above the desired position if the user scrolls the MultiLineTextBox's scroll bar down (i.e. the tooltip will appear where the MultiLineTextBox's top would be if it was not contained in a scroll frame).
  end
  control.Name = controlName
  control.Type = controlTypeName
  control.BaseType = "TextBox"
  control.DelimiterInfo = delimiterInfo
  control.NumberOfDimensionsOfTextBoxValue = EasyOptions:GetNumberOfDimensionsOfTextBoxValue(controlTypeName, delimiterInfo)
  EasyOptions:SetDefaultTextColor(control, false) -- Set the current text color as the default text color.
  -- control:SetMotionScriptsWhileDisabled(true)
  control:EnableMouse(true)
  control:SetAutoFocus(false) -- Set whether or not the editbox will attempt to get input focus when it gets shown (default = true).
  control:SetAltArrowKeyMode(false) -- Make only alt+arrow keys work for navigating the edit box, not arrow keys alone.
  control:SetFontObject("ChatFontNormal") -- ChatFontSmall
  -- control:SetJustifyH("LEFT") -- "LEFT", "RIGHT", "CENTER"
  -- control:SetJustifyV("MIDDLE") -- "TOP", "BOTTOM", "MIDDLE"
  --
  control.isNumeric = isNumeric -- IMPORTANT: We don't use the control:SetNumeric(isNumeric) method because doing so prevents decimal points (i.e., "."), minus signs (i.e., "-"), and other symbols from being entered in to the EditBox.
  if (isNumeric) then
    control.MinValue = minValue
    control.MaxValue = maxValue
    control.FormatString = formatString
  end
  --
  control.SetValue = function(self, value)
    EasyOptions:SetTextBoxValue(self, value)
  end
  control:SetValue(value) -- IMPORTANT: This must be called after control.isNumeric is set (see above).
  EasyOptions:SetTooltip(control, control.TooltipOwner, tooltipTitleText, tooltipText)
  -- if (isMultiLine) then -- NOTE: This code is not implemented because it is handled by hooking to the OnEnter and OnLeave events of control.ContainerControl (see below) which has the advantage of showing the TextBox's tooltip directly to ensure that the displayed tooltip is always up-to-date.
  --   EasyOptions:SetTooltip(control.ContainerControl, control.TooltipOwner, tooltipTitleText, tooltipText) -- IMPORTANT: This is required because the ScrollFrame that contains the TextBox (i.e., control.ScrollFrame) can cause the TextBox's height to be set to a small value after the ScrollFrame is scrolled and then the text of the TextBox is subsequently cleared. So, we must show the TextBox's tooltip if the user moves the mouse cursor over a region in the container that does not contain the TextBox.
  -- end
  
  -- Create the label for the control.
  local labelOffsetX
  local labelOffsetY
  local labelAnchorControl
  if (not isMultiLine) then
    labelOffsetX = -6
    labelOffsetY = 7
    labelAnchorControl = control
  else
    labelOffsetX = 0
    labelOffsetY = 3
    labelAnchorControl = control.BorderFrame
  end
  local labelPositionInfo = {"BOTTOMLEFT", "TOPLEFT", labelOffsetX, labelOffsetY} -- "LEFT", "RIGHT", "TOP", "BOTTOM", "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT", "CENTER"
  local fontObjectName = EasyOptions.Fields.DefaultLabelFontObjectName
  local labelControl, label, labelControlRegionWidth, labelControlRegionHeight = EasyOptions:CreateLabelForControl(parent, control, labelAnchorControl, controlName, labelText, tooltipTitleText, tooltipText, labelPositionInfo, fontObjectName, customFontInfo, customTextColor)
  label:SetJustifyH("LEFT") -- "LEFT", "RIGHT", "CENTER"
  label:SetJustifyV("MIDDLE") -- "TOP", "BOTTOM", "MIDDLE"
  local labelRegionWidth = 0 -- init
  local labelRegionHeight = 0 -- init
  local hasLabelText = (labelText ~= nil) and (labelText ~= "")
  if (hasLabelText) then
    if (not isMultiLine) then
      labelRegionWidth = labelControlRegionWidth + 0 -- + labelOffsetX
      labelRegionHeight = labelControlRegionHeight + labelOffsetY - 1
    else
      labelRegionWidth = labelControlRegionWidth + 0 -- + labelOffsetX
      labelRegionHeight = labelControlRegionHeight + labelOffsetY + 2
    end
  end

  -- Set the bounds of the control.
  local defaultWidth
  local defaultHeight
  local defaultDefaultHeight = 16
  local widthMaxAllowed
  local ignoreCustomHeight
  local allowFillWidthByDefault
  control:SetMultiLine(isMultiLine) -- Sets the EditBox's multi-line state.
  if (not isMultiLine) then
    if (not isNumeric) then
      defaultWidth = EasyOptions:GetDefaultTextBoxWidth()
      defaultHeight = defaultDefaultHeight
      widthMaxAllowed = EasyOptions.Fields.ControlSizeMaxAllowed_Large
      ignoreCustomHeight = true
      control:SetMaxLetters(256)
      allowFillWidthByDefault = false
    else
      defaultWidth = EasyOptions:GetDefaultValueBoxWidth()
      defaultHeight = defaultDefaultHeight
      widthMaxAllowed = EasyOptions.Fields.ControlSizeMaxAllowed_Large
      ignoreCustomHeight = true
      control:SetMaxLetters(15) -- NOTE: 999,999,999,999,999 is the largest value that can be represented by the "%.0f" format string without a loss of precision.
      allowFillWidthByDefault = false
    end
  else
    defaultWidth = EasyOptions:GetDefaultMultiLineTextBoxWidth()
    -- defaultWidth = math.max(EasyOptions.Fields.DefaultWidthMaxAvailable_WithScrollBars - positionInfo[3] + 4, 0.5 * EasyOptions.Fields.DefaultWidthMaxAvailable_WithScrollBars) -- Set to the width to fill the available space by default, but never limit the width to be less than a half the available space.
    defaultHeight = 10 * defaultDefaultHeight
    widthMaxAllowed = EasyOptions.Fields.ControlSizeMaxAllowed_Large
    ignoreCustomHeight = false
    control:SetMaxLetters(32768)
    allowFillWidthByDefault = true
  end
  local width, height, hasWidth, hasHeight, shouldFillWidth = EasyOptions:GetSizeFromCustomSize(customSize, ignoreCustomHeight, defaultWidth, defaultHeight, widthMaxAllowed, EasyOptions.Fields.ControlSizeMaxAllowed_Large, true, false)
  --
  local offsetX
  local offsetY
  if (not isMultiLine) then
    labelRegionHeight = math.max(labelRegionHeight, 1)
    offsetX = 8 -- Offset the x-position of this control type to help it align better with the other controls.
    offsetY = 5 -- Offset the y-position of this control type to help it align better with the other controls.
    control:SetPoint(positionInfo[1], parent, positionInfo[2], positionInfo[3] + offsetX, positionInfo[4] - offsetY - labelRegionHeight + EasyOptions:IIF(hasLabelText, 0, 0))
    if (shouldFillWidth) or ((allowFillWidthByDefault) and (customSize == nil)) then
      EasyOptions:SetControlPointToFillWidth(control, parent, positionInfo, offsetX, 4)
    end
    control:SetSize(width, height)
    -- control:SetTextInsets(1, 0, 0, 0) -- Left, Right, Top, Bottom. Defaults are 0, 0, 0, 0
  else
    offsetX = 2 -- Offset the x-position of this control type to help it align better with the other controls.
    offsetY = 2 + EasyOptions:IIF(hasLabelText, 0, 0) -- Offset the y-position of this control type to help it align better with the other controls.
    local controlSizePadding = 4
    control.BorderFrame:SetPoint(positionInfo[1], parent, positionInfo[2], positionInfo[3] + offsetX, positionInfo[4] - offsetY - labelRegionHeight + EasyOptions:IIF(hasLabelText, 0, 0))
    if (shouldFillWidth) or ((allowFillWidthByDefault) and (customSize == nil)) then
      EasyOptions:SetControlPointToFillWidth(control.BorderFrame, parent, positionInfo, offsetX, EasyOptions.Fields.DefaultScrollBarRegionWidth + 2)
    end
    control.BorderFrame:SetSize(width, height)
    control.SetControlPointToFillBorderFrameWidth = function(self)
      local control = self
      control:SetPoint("RIGHT", control.BorderFrame, "RIGHT", -6, 0) -- "LEFT", "RIGHT", "TOP", "BOTTOM", "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT", "CENTER"
    end 
    control:SetSize(1, 1)
    control:SetControlPointToFillBorderFrameWidth()
    -- control.DesiredSize = {math.max(width - 2 * controlSizePadding, 0), math.max(height - 2 * controlSizePadding, 0)} -- NOTE: We store the initial desired size of the TextBox because the ScrollFrame that contains the TextBox (i.e., control.ScrollFrame) can cause the TextBox's height to be set to a small value after the ScrollFrame is scrolled and then the text of the TextBox is subsequently cleared.
    -- control:SetSize(control.DesiredSize[1], control.DesiredSize[2])
  end

  -- Set event handlers.
  local onTextAcceptedHandlerCustom = onTextAcceptedHandler
  local onEditFocusLostHandler = function(self)
    self:ClearHighlightText() -- This is required to prevent an API bug that can prevent the TextBox's highlighted text from being cleared when the TextBox loses focus.
    if (onTextAcceptedHandlerCustom ~= nil) then
      onTextAcceptedHandlerCustom(self)
    end
  end
  local onEnterPressedHandler = function(self)
    if (onTextAcceptedHandlerCustom ~= nil) then
      onTextAcceptedHandlerCustom(self)
    end
  end
  -- control:SetScript("OnEnter", onEnterHandler)
  -- control:SetScript("OnLeave", onLeaveHandler)
  -- control:SetScript("OnEditFocusGained", onEditFocusGainedHandler)
  control:SetScript("OnEditFocusLost", onEditFocusLostHandler)
  if (not isMultiLine) then
    control:SetScript("OnEnterPressed", onEnterPressedHandler)
    -- control:SetScript("OnTabPressed", function(this) this:Insert("    ") end) -- Manually insert 4 spaces.
  else
    -- control:SetScript("OnEnterPressed", function(this) this:Insert("\n") end) -- Manually insert a new line.
    control:SetScript("OnTabPressed", function(this) this:Insert("    ") end) -- Manually insert 4 spaces.
  end
  control:SetScript("OnEscapePressed", onEscapePressedHandler)
  -- control:SetScript("OnSpacePressed", onSpacePressedHandler)
  control:SetScript("OnTextChanged", onTextChangedHandler)
  -- control:SetScript("OnTextSet", onTextSetHandler)
  -- control:SetScript("OnClick", onClickHandler)
  --
  if (isMultiLine) then
    control.ContainerControl:HookScript("OnEnter", function(self)
      local control = self
      local textBox = control.TextBox
      if (textBox.OnEnterTooltipFunction ~= nil) then
        textBox:OnEnterTooltipFunction() -- IMPORTANT: This is required because the ScrollFrame that contains the TextBox (i.e., control.ScrollFrame) can cause the TextBox's height to be set to a small value after the ScrollFrame is scrolled and then the text of the TextBox is subsequently cleared. So, we must show the TextBox's tooltip if the user moves the mouse cursor over a region in the container that does not contain the TextBox.
      end
    end)
    control.ContainerControl:HookScript("OnLeave", function(self)
      local control = self
      local textBox = control.TextBox
      if (textBox.OnLeaveTooltipFunction ~= nil) then
        textBox:OnLeaveTooltipFunction()
      end
    end)
    control.ContainerControl:HookScript("OnMouseDown", function(self, button)
      -- if (button == "LeftButton") or (button == "RightButton") then
      local control = self
      local textBox = control.TextBox
      textBox:SetFocus() -- IMPORTANT: This is required because the ScrollFrame that contains the TextBox (i.e., control.ScrollFrame) can cause the TextBox's height to be set to a small value after the ScrollFrame is scrolled and then the text of the TextBox is subsequently cleared. Therefore, we simulate a mouse click in the TextBox if the border frame that contains the TextBox is clicked.
      textBox:SetCursorPosition(textBox:GetNumLetters()) -- Set the caret to the last character in the TextBox.
      -- end
    end)
  end
  --
  control.OnEnabledChangedHandler = function(self)
    if (self.IsEnabled == nil) or (self:IsEnabled()) then -- Assume that the control is enabled if its IsEnabled method is nil.
      EasyOptions:SetToDefaultTextColor(self)
    else
      EasyOptions:SetToDisabledTextColor(self)
    end
  end
  EasyOptions:AddSupportForEnabledState(control)

  local widthEffective
  if (not isMultiLine) then
    widthEffective = width + 5 -- Add padding to make the control align with other controls.
  else
    widthEffective = width + 17 -- Add padding to make the control align with other controls.
  end
  --
  local heightEffective
  if (not isMultiLine) then
    heightEffective = height + offsetY + labelRegionHeight - 3 -- Add padding to make the control align with other controls.
  else
    heightEffective = height + offsetY + labelRegionHeight - 5 -- Add padding to make the control align with other controls.
  end
  
  local controlRegionWidth = math.max(widthEffective, labelRegionWidth) -- Add padding to make the control align with other controls.
  local controlRegionHeight = heightEffective
  control.ControlRegionWidth = controlRegionWidth
  control.ControlRegionHeight = controlRegionHeight
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:CreateOptionTextBoxBase(parent, positionInfo, optionItem, isNumeric, isMultiLine)
  local optionName = optionItem.Name
  local labelText = EasyOptions:GetControlText(optionItem) -- Specifies the text for the label of the TextBox, not the text of the TextBox.
  local value = EasyOptions:GetOptionValueOrDefaultValue(optionName, optionItem.DefaultValue) -- Specifies the text of the TextBox or the value of the ValueBox.
  local minValue = optionItem.MinValue
  local maxValue = optionItem.MaxValue
  local formatString = optionItem.FormatString
  local tooltipTitleText = optionItem.TooltipTitleText or labelText
  local tooltipText = optionItem.TooltipText
  local customSize = optionItem.Size or optionItem.Width
  local delimiterInfo = optionItem.DelimiterInfo
  local customFontInfo = optionItem.LabelFontInfo
  local customTextColor = optionItem.LabelTextColor
  local onTextAcceptedHandlerCustom = optionItem.OnTextAcceptedHandler
  local onTextAcceptedHandler = function(self)
    EasyOptions:OptionTextBox_OnTextAcceptedHandler(self)
    if (onTextAcceptedHandlerCustom ~= nil) then
      onTextAcceptedHandlerCustom(self)
    end
  end
  local onTextChangedHandlerCustom = optionItem.OnTextChangedHandler
  local onTextChangedHandler = function(self)
    EasyOptions:OptionTextBox_OnTextChangedHandler(self)
    if (onTextChangedHandlerCustom ~= nil) then
      onTextChangedHandlerCustom(self)
    end
  end
  local onEscapePressedHandler = function(self)
    EasyOptions:OptionTextBox_OnEscapePressedHandler(self)
  end

  local control, controlRegionWidth, controlRegionHeight = EasyOptions:CreateTextBox(parent, nil, value, minValue, maxValue, formatString, labelText, tooltipTitleText, tooltipText, positionInfo, customSize, isNumeric, isMultiLine, delimiterInfo, customFontInfo, customTextColor, onTextChangedHandler, onTextAcceptedHandler, onEscapePressedHandler)
  
  EasyOptions:AddRightButtonDownHandlerForOptionControl(control)
  if (isMultiLine) then
    EasyOptions:AddRightButtonDownHandlerForOptionControl(control.ContainerControl) -- IMPORTANT: This is required because the ScrollFrame that contains the TextBox (i.e., control.ScrollFrame) can cause the TextBox's height to be set to a small value after the ScrollFrame is scrolled and then the text of the TextBox is subsequently cleared. So, we must handle the right button click event if the user clicks the mouse over a region in the container that does not contain the TextBox.
  end

  optionItem.Control = control
  control.OptionItem = optionItem
  if (isMultiLine) then
    control.BorderFrame.OptionItem = optionItem
    control.ScrollFrame.OptionItem = optionItem
  end
  if (optionName ~= nil) and (optionName ~= "") then
    EasyOptions.Current.OptionControls[optionName] = control
  end
  
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:CreateOptionTextBox(parent, positionInfo, optionItem)
  local isNumeric = false
  local isMultiLine = false
  local control, controlRegionWidth, controlRegionHeight = EasyOptions:CreateOptionTextBoxBase(parent, positionInfo, optionItem, isNumeric, isMultiLine)
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:CreateOptionValueBox(parent, positionInfo, optionItem)
  local isNumeric = true
  local isMultiLine = false
  local control, controlRegionWidth, controlRegionHeight = EasyOptions:CreateOptionTextBoxBase(parent, positionInfo, optionItem, isNumeric, isMultiLine)
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:CreateOptionMultiLineTextBox(parent, positionInfo, optionItem)
  local isNumeric = false
  local isMultiLine = true
  local control, controlRegionWidth, controlRegionHeight = EasyOptions:CreateOptionTextBoxBase(parent, positionInfo, optionItem, isNumeric, isMultiLine)
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:OptionTextBox_OnTextAcceptedHandler(control)
  local optionItem = control.OptionItem
  local addonDB = nil -- init
  local optionName = nil -- init
  if (optionItem ~= nil) then
    addonDB = optionItem.AddonDB
    optionName = optionItem.Name
  end
  
  local alternateValue
  if (addonDB ~= nil) and (optionName ~= nil) then
    alternateValue = addonDB[optionName] -- Use the current value in Options as the alternate value if the text of the TextBox is invalid (e.g., control is a ValueBox but its text does not represent a numeric value).
  else
    alternateValue = optionItem.DefaultValue -- Use the default value as the alternate value if the text of the TextBox is invalid (e.g., control is a ValueBox but its text does not represent a numeric value).
  end
  local value = EasyOptions:GetTextBoxValueFromText(control, alternateValue)
  control:SetValue(value)
  
  if (control.OnEnterTooltipFunction ~= nil) and (control.ContainerControl ~= nil) and ((EasyOptions:IsTooltipOwner(control)) or (EasyOptions:IsTooltipOwner(control.TooltipOwner))) then
    control:OnEnterTooltipFunction() -- IMPORTANT: This is required because the ScrollFrame that contains the TextBox (i.e., control.ScrollFrame) can cause the TextBox's height to be set to a small value after the ScrollFrame is scrolled and then the text of the TextBox is subsequently cleared. So, we must update TextBox's tooltip if the user clicks the TextBox's container region after editing the TextBox's text which then causes the TextBox's text to change which then requires the TextBox's tooltip to be updated (e.g., the TextBox's option value changes from its default value to some other value so the tooltip must be updated with the "Right click to reset" text).
  end
end
function EasyOptions:OptionTextBox_OnEscapePressedHandler(control)
  local optionItem = control.OptionItem
  local addonDB = nil -- init
  local optionName = nil -- init
  if (optionItem ~= nil) then
    addonDB = optionItem.AddonDB
    optionName = optionItem.Name
  end
  
  if (addonDB ~= nil) and (optionName ~= nil) then
    control:SetValue(addonDB[optionName])
  end
end
function EasyOptions:OptionTextBox_OnTextChangedHandler(control)
  -- Reserved.
end
function EasyOptions:SetTextBoxValue(control, value)
  local optionValue
  if (not control.isNumeric) then
    local valueLocal = EasyOptions:ConvertTextBoxValueToAppropriateNumberOfDimensions(value, control.Type, control.DelimiterInfo, true)
    if (control.NumberOfDimensionsOfTextBoxValue == 0) then
      if (valueLocal ~= nil) and (type(valueLocal) ~= "string") then
        EasyOptions:PrintErrorMessageWithOptionItemInfo(L("has an invalid entry for a TextBox value. The value is of type {1}, but must be of type string.", type(valueLocal)))
        return
      end
      local textLocal = EasyOptions:IIF(valueLocal ~= nil, valueLocal, "")
      control:SetText(textLocal)
    else
      local textLocal = EasyOptions:ConvertListToString(valueLocal, control.DelimiterInfo, control.NumberOfDimensionsOfTextBoxValue)
      control:SetText(textLocal)
    end
    optionValue = valueLocal
  else -- control.isNumeric == true
    if (type(value) ~= "number") then
      EasyOptions:PrintErrorMessageWithOptionItemInfo(L("has an invalid entry for a ValueBox value. The value is of type {1}, but must be of type number.", type(value)))
      return
    end
    local valueLimited = EasyOptions:LimitValue(value, control.MinValue, control.MaxValue, EasyOptions.Fields.DefaultUseDefaultLimitValuesIfNoCustomLimitValues)
    local textLocal = EasyOptions:FormatValue(valueLimited, control.FormatString)
    control.Value = valueLimited
    control:SetText(textLocal) -- control:SetNumber(value)
    optionValue = valueLimited
  end
  control:SetCursorPosition(0) -- IMPORTANT: We must set the cursor position to 0 in order to avoid a bug that prevents the text from being visible when the EditBox is shown due to the text being left of the visible region of the EditBox.
  
  -- Update Options (if required).
  if (not EasyOptions.Current.IsBuildingOptionFrame) then
    local allowUpdateControl = false
    EasyOptions:SetOptionValueFromControlInfo(control, optionValue, allowUpdateControl)
  end
end
function EasyOptions:GetTextBoxValueFromText(control, alternateValue)
  if (not control.isNumeric) then
    if (control.NumberOfDimensionsOfTextBoxValue == 0) then
      return control:GetText()
    else
      local isTextFromWoWAPI = true
      local returnNilIfIsEmptyString = true
      return EasyOptions:SplitStringAsList(control:GetText(), control.DelimiterInfo, control.NumberOfDimensionsOfTextBoxValue, isTextFromWoWAPI, returnNilIfIsEmptyString)
    end
  else -- control.isNumeric == true
    local setTextToAlternateValueIfInvalid = true -- Set the text of the TextBox to the current value in Options if the value associated with the TextBox is invalid.
    return EasyOptions:GetValueFromTextAndUpdateValueBox(control, alternateValue, setTextToAlternateValueIfInvalid)
  end
end
function EasyOptions:GetValueFromTextAndUpdateValueBox(control, alternateValue, setTextToAlternateValueIfInvalid) -- Returns the numeric value represented by the specified TextBox, or the specified alternate value if the TextBox does not represent a numeric value. Also has the option to set the text of the TextBox to the alternate value if the TextBox does not represent a numeric value.
  local value = tonumber(control:GetText()) -- control.Value -- control:GetNumber()
  local useAlternateValue = (value == nil) -- NOTE: tonumber will return nil if the string does not represent a numeric value.
  local result
  if (not useAlternateValue) then
    result = value
  else
    result = alternateValue
  end
  result = EasyOptions:LimitValue(result, control.MinValue, control.MaxValue, EasyOptions.Fields.DefaultUseDefaultLimitValuesIfNoCustomLimitValues)
  if (useAlternateValue) and (setTextToAlternateValueIfInvalid) then
    control:SetValue(result) -- Set the control's text to the alternate value if the TextBox does not represent a numeric value.
  end
  return result
end
function EasyOptions:GetDefaultTextBoxWidth()
  return EasyOptions:GetWidthForNumberOfColumns(4) -- Allow room for 4 adjacent controls including the default padding between the controls.
end
function EasyOptions:GetDefaultValueBoxWidth()
  return 50 -- Allows room for 6 digits and a decimal point.
end
function EasyOptions:GetDefaultMultiLineTextBoxWidth()
  return EasyOptions:GetWidthForNumberOfColumns(2) - EasyOptions.Fields.DefaultScrollBarRegionWidth -- Allow room for two adjacent controls including the default padding between the two controls.
end
