initial commit
This commit is contained in:
266
Core/UI/Elements/Slider.lua
Normal file
266
Core/UI/Elements/Slider.lua
Normal file
@@ -0,0 +1,266 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- https://tradeskillmaster.com --
|
||||
-- All Rights Reserved - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
--- Slider UI Element Class.
|
||||
-- A slider allows for selecting a numerical range. It is a subclass of the @{Element} class.
|
||||
-- @classmod Slider
|
||||
|
||||
local _, TSM = ...
|
||||
local Math = TSM.Include("Util.Math")
|
||||
local NineSlice = TSM.Include("Util.NineSlice")
|
||||
local ScriptWrapper = TSM.Include("Util.ScriptWrapper")
|
||||
local Theme = TSM.Include("Util.Theme")
|
||||
local Slider = TSM.Include("LibTSMClass").DefineClass("Slider", TSM.UI.Element)
|
||||
local UIElements = TSM.Include("UI.UIElements")
|
||||
UIElements.Register(Slider)
|
||||
TSM.UI.Slider = Slider
|
||||
local private = {}
|
||||
local THUMB_WIDTH = 8
|
||||
local INPUT_WIDTH = 50
|
||||
local INPUT_AREA_SPACE = 128
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Public Class Methods
|
||||
-- ============================================================================
|
||||
|
||||
function Slider.__init(self)
|
||||
local frame = UIElements.CreateFrame(self, "Frame")
|
||||
frame:EnableMouse(true)
|
||||
ScriptWrapper.Set(frame, "OnMouseDown", private.FrameOnMouseDown, self)
|
||||
ScriptWrapper.Set(frame, "OnMouseUp", private.FrameOnMouseUp, self)
|
||||
ScriptWrapper.Set(frame, "OnUpdate", private.FrameOnUpdate, self)
|
||||
|
||||
self.__super:__init(frame)
|
||||
|
||||
-- create the textures
|
||||
frame.barTexture = frame:CreateTexture(nil, "BACKGROUND", nil, 1)
|
||||
frame.activeBarTexture = frame:CreateTexture(nil, "BACKGROUND", nil, 2)
|
||||
frame.thumbTextureLeft = frame:CreateTexture(nil, "ARTWORK")
|
||||
frame.thumbTextureRight = frame:CreateTexture(nil, "ARTWORK")
|
||||
|
||||
frame.inputLeft = CreateFrame("EditBox", nil, frame, nil)
|
||||
frame.inputLeft:SetJustifyH("CENTER")
|
||||
frame.inputLeft:SetWidth(INPUT_WIDTH)
|
||||
frame.inputLeft:SetHeight(24)
|
||||
frame.inputLeft:SetAutoFocus(false)
|
||||
frame.inputLeft:SetNumeric(true)
|
||||
ScriptWrapper.Set(frame.inputLeft, "OnEscapePressed", private.InputOnEscapePressed)
|
||||
ScriptWrapper.Set(frame.inputLeft, "OnEnterPressed", private.LeftInputOnEnterPressed, self)
|
||||
|
||||
|
||||
frame.dash = UIElements.CreateFontString(self, frame)
|
||||
frame.dash:SetJustifyH("CENTER")
|
||||
frame.dash:SetJustifyV("MIDDLE")
|
||||
frame.dash:SetWidth(12)
|
||||
|
||||
frame.inputRight = CreateFrame("EditBox", nil, frame, nil)
|
||||
frame.inputRight:SetJustifyH("CENTER")
|
||||
frame.inputRight:SetWidth(INPUT_WIDTH)
|
||||
frame.inputRight:SetHeight(24)
|
||||
frame.inputRight:SetNumeric(true)
|
||||
frame.inputRight:SetAutoFocus(false)
|
||||
ScriptWrapper.Set(frame.inputRight, "OnEscapePressed", private.InputOnEscapePressed)
|
||||
ScriptWrapper.Set(frame.inputRight, "OnEnterPressed", private.RightInputOnEnterPressed, self)
|
||||
|
||||
self._inputLeftNineSlice = NineSlice.New(frame.inputLeft)
|
||||
self._inputRightNineSlice = NineSlice.New(frame.inputRight)
|
||||
self._leftValue = nil
|
||||
self._rightValue = nil
|
||||
self._minValue = nil
|
||||
self._maxValue = nil
|
||||
self._dragging = nil
|
||||
end
|
||||
|
||||
function Slider.Release(self)
|
||||
self._leftValue = nil
|
||||
self._rightValue = nil
|
||||
self._minValue = nil
|
||||
self._maxValue = nil
|
||||
self._dragging = nil
|
||||
self.__super:Release()
|
||||
end
|
||||
|
||||
--- Set the extends of the possible range.
|
||||
-- @tparam Slider self The slider object
|
||||
-- @tparam number minValue The minimum value
|
||||
-- @tparam number maxValue The maxmimum value
|
||||
-- @treturn Slider The slider object
|
||||
function Slider.SetRange(self, minValue, maxValue)
|
||||
self._minValue = minValue
|
||||
self._maxValue = maxValue
|
||||
self._leftValue = minValue
|
||||
self._rightValue = maxValue
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets the current value.
|
||||
-- @tparam Slider self The slider object
|
||||
-- @tparam number leftValue The lower end of the range
|
||||
-- @tparam number rightValue The upper end of the range
|
||||
-- @treturn Slider The slider object
|
||||
function Slider.SetValue(self, leftValue, rightValue)
|
||||
assert(leftValue < rightValue and leftValue >= self._minValue and rightValue <= self._maxValue)
|
||||
self._leftValue = leftValue
|
||||
self._rightValue = rightValue
|
||||
return self
|
||||
end
|
||||
|
||||
--- Gets the current value
|
||||
-- @tparam Slider self The slider object
|
||||
-- @treturn number The lower end of the range
|
||||
-- @treturn number The upper end of the range
|
||||
function Slider.GetValue(self)
|
||||
return self._leftValue, self._rightValue
|
||||
end
|
||||
|
||||
function Slider.Draw(self)
|
||||
self.__super:Draw()
|
||||
local frame = self:_GetBaseFrame()
|
||||
|
||||
local inputColor = Theme.GetColor("ACTIVE_BG")
|
||||
self._inputLeftNineSlice:SetStyle("rounded")
|
||||
self._inputRightNineSlice:SetStyle("rounded")
|
||||
self._inputLeftNineSlice:SetVertexColor(inputColor:GetFractionalRGBA())
|
||||
self._inputRightNineSlice:SetVertexColor(inputColor:GetFractionalRGBA())
|
||||
|
||||
local sliderHeight = self:_GetDimension("HEIGHT") / 2
|
||||
local width = self:_GetDimension("WIDTH") - INPUT_AREA_SPACE
|
||||
local leftPos = Math.Scale(self._leftValue, self._minValue, self._maxValue, 0, width - THUMB_WIDTH)
|
||||
local rightPos = Math.Scale(self._rightValue, self._minValue, self._maxValue, 0, width - THUMB_WIDTH)
|
||||
local fontPath, fontHeight = Theme.GetFont("BODY_BODY1"):GetWowFont()
|
||||
local textColor = Theme.GetColor("TEXT")
|
||||
|
||||
-- wow renders the font slightly bigger than the designs would indicate, so subtract one from the font height
|
||||
frame.inputRight:SetFont(fontPath, fontHeight)
|
||||
frame.inputRight:SetTextColor(textColor:GetFractionalRGBA())
|
||||
frame.inputRight:SetPoint("RIGHT", 0)
|
||||
frame.inputRight:SetNumber(self._rightValue)
|
||||
|
||||
frame.dash:SetFont(fontPath, fontHeight)
|
||||
frame.dash:SetTextColor(textColor:GetFractionalRGBA())
|
||||
frame.dash:SetText("-")
|
||||
frame.dash:SetPoint("RIGHT", frame.inputRight, "LEFT", 0, 0)
|
||||
|
||||
-- wow renders the font slightly bigger than the designs would indicate, so subtract one from the font height
|
||||
frame.inputLeft:SetFont(fontPath, fontHeight)
|
||||
frame.inputLeft:SetTextColor(textColor:GetFractionalRGBA())
|
||||
frame.inputLeft:SetPoint("RIGHT", frame.dash, "LEFT", 0)
|
||||
frame.inputLeft:SetNumber(self._leftValue)
|
||||
|
||||
frame.barTexture:ClearAllPoints()
|
||||
frame.barTexture:SetPoint("LEFT", 0, 0)
|
||||
frame.barTexture:SetPoint("RIGHT", frame.inputLeft, "LEFT", -16, 0)
|
||||
frame.barTexture:SetHeight(sliderHeight / 3)
|
||||
frame.barTexture:SetColorTexture(Theme.GetColor("FRAME_BG"):GetFractionalRGBA())
|
||||
|
||||
TSM.UI.TexturePacks.SetTextureAndSize(frame.thumbTextureLeft, "iconPack.14x14/Circle")
|
||||
frame.thumbTextureLeft:SetPoint("LEFT", frame.barTexture, leftPos, 0)
|
||||
|
||||
TSM.UI.TexturePacks.SetTextureAndSize(frame.thumbTextureRight, "iconPack.14x14/Circle")
|
||||
frame.thumbTextureRight:SetPoint("LEFT", frame.barTexture, rightPos, 0)
|
||||
|
||||
frame.activeBarTexture:SetPoint("LEFT", frame.thumbTextureLeft, "CENTER")
|
||||
frame.activeBarTexture:SetPoint("RIGHT", frame.thumbTextureRight, "CENTER")
|
||||
frame.activeBarTexture:SetHeight(sliderHeight / 3)
|
||||
frame.activeBarTexture:SetColorTexture(Theme.GetColor("TEXT"):GetFractionalRGBA())
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Private Class Methods
|
||||
-- ============================================================================
|
||||
|
||||
function Slider._GetCursorPositionValue(self)
|
||||
local frame = self:_GetBaseFrame()
|
||||
local x = GetCursorPosition() / frame:GetEffectiveScale()
|
||||
local left = frame:GetLeft() + THUMB_WIDTH / 2
|
||||
local right = frame:GetRight() - THUMB_WIDTH - INPUT_AREA_SPACE * 2 / 2
|
||||
x = min(max(x, left), right)
|
||||
local value = Math.Scale(x, left, right, self._minValue, self._maxValue)
|
||||
return min(max(Math.Round(value), self._minValue), self._maxValue)
|
||||
end
|
||||
|
||||
function Slider._UpdateLeftValue(self, value)
|
||||
local newValue = max(min(value, self._rightValue), self._minValue)
|
||||
if newValue == self._leftValue then
|
||||
return
|
||||
end
|
||||
self._leftValue = newValue
|
||||
self:Draw()
|
||||
end
|
||||
|
||||
function Slider._UpdateRightValue(self, value)
|
||||
local newValue = min(max(value, self._leftValue), self._maxValue)
|
||||
if newValue == self._rightValue then
|
||||
return
|
||||
end
|
||||
self._rightValue = newValue
|
||||
self:Draw()
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Local Script Handlers
|
||||
-- ============================================================================
|
||||
|
||||
function private.InputOnEscapePressed(input)
|
||||
input:ClearFocus()
|
||||
end
|
||||
|
||||
function private.LeftInputOnEnterPressed(self)
|
||||
local input = self:_GetBaseFrame().inputLeft
|
||||
self:_UpdateLeftValue(input:GetNumber())
|
||||
end
|
||||
|
||||
function private.RightInputOnEnterPressed(self)
|
||||
local input = self:_GetBaseFrame().inputRight
|
||||
self:_UpdateRightValue(input:GetNumber())
|
||||
end
|
||||
|
||||
function private.FrameOnMouseDown(self)
|
||||
local frame = self:_GetBaseFrame()
|
||||
frame.inputLeft:ClearFocus()
|
||||
frame.inputRight:ClearFocus()
|
||||
local value = self:_GetCursorPositionValue()
|
||||
local leftDiff = abs(value - self._leftValue)
|
||||
local rightDiff = abs(value - self._rightValue)
|
||||
if value < self._leftValue then
|
||||
-- clicked to the left of the left thumb, so drag that
|
||||
self._dragging = "left"
|
||||
elseif value > self._rightValue then
|
||||
-- clicked to the right of the right thumb, so drag that
|
||||
self._dragging = "right"
|
||||
elseif self._leftValue == self._rightValue then
|
||||
-- just ignore this click since they clicked on both thumbs
|
||||
elseif leftDiff < rightDiff then
|
||||
-- clicked closer to the left thumb, so drag that
|
||||
self._dragging = "left"
|
||||
else
|
||||
-- clicked closer to the right thumb (or right in the middle), so drag that
|
||||
self._dragging = "right"
|
||||
end
|
||||
end
|
||||
|
||||
function private.FrameOnMouseUp(self)
|
||||
self._dragging = nil
|
||||
end
|
||||
|
||||
function private.FrameOnUpdate(self)
|
||||
if not self._dragging then
|
||||
return
|
||||
end
|
||||
if self._dragging == "left" then
|
||||
self:_UpdateLeftValue(self:_GetCursorPositionValue())
|
||||
elseif self._dragging == "right" then
|
||||
self:_UpdateRightValue(self:_GetCursorPositionValue())
|
||||
else
|
||||
error("Unexpected dragging: "..tostring(self._dragging))
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user