Guest User

Untitled

a guest
Jan 8th, 2015
19
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 106.65 KB | None | 0 0
  1. --****************************************************************************************
  2. --
  3. -- ====================================================================
  4. -- Corona SDK Widget Module
  5. -- ====================================================================
  6. --
  7. -- File: widget.lua
  8. --
  9. -- Copyright © 2013 Corona Labs Inc. All Rights Reserved.
  10. --
  11. -- Redistribution and use in source and binary forms, with or without
  12. -- modification, are permitted provided that the following conditions are met:
  13. --
  14. --    * Redistributions of source code must retain the above copyright
  15. --      notice, this list of conditions and the following disclaimer.
  16. --    * Redistributions in binary form must reproduce the above copyright
  17. --      notice, this list of conditions and the following disclaimer in the
  18. --      documentation and/or other materials provided with the distribution.
  19. --    * Neither the name of the company nor the names of its contributors
  20. --      may be used to endorse or promote products derived from this software
  21. --      without specific prior written permission.
  22. --
  23. -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  24. -- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  25. -- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  26. -- DISCLAIMED. IN NO EVENT SHALL CORONA LABS INC. BE LIABLE FOR ANY
  27. -- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  28. -- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  29. -- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  30. -- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31. -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  32. -- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. --
  34. --****************************************************************************************
  35.  
  36. local modname = ...
  37. local widget = {}
  38. package.loaded[modname] = widget
  39. widget.version = "0.7"
  40.  
  41. -- cached locals
  42. local mAbs = math.abs
  43. local mFloor = math.floor
  44.  
  45. -- defaults
  46. local scrollFriction = 0.935
  47. local pickerFriction = 0.88
  48.  
  49. -- modify factory function to ensure widgets are properly cleaned on group removal
  50. local cached_displayNewGroup = display.newGroup
  51. function display.newGroup()
  52.     local g = cached_displayNewGroup()
  53.    
  54.     -- function to find/remove widgets within group
  55.     local function removeWidgets( group )
  56.         if group.numChildren then
  57.             for i=group.numChildren,1,-1 do
  58.                 if group[i]._isWidget then
  59.                     group[i]:removeSelf()
  60.                
  61.                 elseif not group[i]._isWidget and group[i].numChildren then
  62.                     -- nested group (that is not a widget)
  63.                     removeWidgets( group[i] )
  64.                 end
  65.             end
  66.         end
  67.     end
  68.    
  69.     -- store reference to original removeSelf method
  70.     local cached_removeSelf = g.removeSelf
  71.    
  72.     -- subclass removeSelf method
  73.     function g:removeSelf()
  74.         removeWidgets( self )   -- remove widgets first
  75.        
  76.         -- continue removing group as usual
  77.         if self.parent and self.parent.remove then
  78.             self.parent:remove( self )
  79.         end
  80.     end
  81.     return g
  82. end
  83.  
  84. -- set current theme from external .lua module
  85. function widget.setTheme( themeModule )
  86.     widget.theme = require( themeModule )   -- should return table w/ theme data
  87. end
  88.  
  89. -- add 'setText()' method to display.newText (to be consistent with display.newEmbossed text)
  90. local cached_newText = display.newText
  91. function display.newText( ... )
  92.     local text = cached_newText( ... )
  93.  
  94.     function text:setText( newString )
  95.         self.text = newString
  96.     end
  97.  
  98.     return text
  99. end
  100.  
  101. -- creates very sharp text for high resolution/high density displays
  102. function widget.retinaText( ... )
  103.     text = display.newText( ... );
  104.     return text
  105. end; display.newRetinaText = display.newText --widget.retinaText
  106.  
  107. -- creates sharp (retina) text with an embossed/inset effect
  108. function widget.embossedText( ... )
  109.     local arg = { ... }
  110.    
  111.     -- parse arguments
  112.     local parentG, w, h
  113.     local argOffset = 0
  114.    
  115.     -- determine if a parentGroup was specified
  116.     if arg[1] and type(arg[1]) == "table" then
  117.         parentG = arg[1]; argOffset = 1
  118.     end
  119.    
  120.     local string = arg[1+argOffset] or ""
  121.     local x = arg[2+argOffset] or 0
  122.     local y = arg[3+argOffset] or 0
  123.     local w, h = 0, 0
  124.    
  125.     local newOffset = 3+argOffset
  126.     if type(arg[4+argOffset]) == "number" then w = arg[4+argOffset]; newOffset=newOffset+1; end
  127.     if w and #arg >= 7+argOffset then h = arg[5+argOffset]; newOffset=newOffset+1; end
  128.    
  129.     local font = arg[1+newOffset] or native.systemFont
  130.     local size = arg[2+newOffset] or 12
  131.     local color = { 0, 0, 0, 255 }
  132.    
  133.     ---------------------------------------------
  134.    
  135.     local r, g, b, a = color[1], color[2], color[3], color[4]
  136.     local textBrightness = ( r + g + b ) / 3
  137.    
  138.     local highlight = display.newText( string, 0.5, 1, w, h, font, size )
  139.     if ( textBrightness > 127) then
  140.         highlight:setTextColor( 255, 255, 255, 20 )
  141.     else
  142.         highlight:setTextColor( 255, 255, 255, 140 )
  143.     end
  144.    
  145.     local shadow = display.newText( string, -0.5, -1, w, h, font, size )
  146.     if ( textBrightness > 127) then
  147.         shadow:setTextColor( 0, 0, 0, 128 )
  148.     else
  149.         shadow:setTextColor( 0, 0, 0, 20 )
  150.     end
  151.    
  152.     local label = display.newText( string, 0, 0, w, h, font, size )
  153.     label:setTextColor( r, g, b, a )
  154.    
  155.     -- create display group, insert all embossed text elements, and position it
  156.     local text = display.newGroup()
  157.     text:insert( highlight ); text.highlight = highlight
  158.     text:insert( shadow ); text.shadow = shadow
  159.     text:insert( label ); text.label = label
  160.     text.x, text.y = x, y
  161.     text:setReferencePoint( display.CenterReferencePoint )
  162.    
  163.     -- setTextColor method
  164.     function text:setTextColor( ... )
  165.         local r, g, b, a; local arg = { ... }
  166.        
  167.         if #arg == 4 then
  168.             r = arg[1]; g = arg[2]; b = arg[3]; a = arg[4]
  169.         elseif #arg == 3 then
  170.             r = arg[1]; g = arg[2]; b = arg[3]; a = 255
  171.         elseif #arg == 2 then
  172.             r = arg[1]; g = r; b = r; a = arg[2]
  173.         elseif #arg == 1 then
  174.             if type(arg[1]) == "number" then
  175.                 r = arg[1]; g = r; b = r; a = 255
  176.             end
  177.         end
  178.        
  179.         local textBrightness = ( r + g + b ) / 3
  180.         if ( textBrightness > 127) then
  181.             self.highlight:setTextColor( 255, 255, 255, 20 )
  182.             self.shadow:setTextColor( 0, 0, 0, 128 )
  183.         else
  184.             self.highlight:setTextColor( 255, 255, 255, 140 )
  185.             self.shadow:setTextColor( 0, 0, 0, 20 )
  186.         end
  187.         self.label:setTextColor( r, g, b, a )
  188.     end
  189.    
  190.     -- setText method
  191.     function text:setText( newString )
  192.         local newString = newString or self.text
  193.         self.highlight.text = newString
  194.         self.shadow.text = newString
  195.         self.label.text = newString
  196.         self.text = newString
  197.     end
  198.    
  199.     -- setSize method
  200.     function text:setSize ( newSize )
  201.         local newSize = newSize or size
  202.         self.highlight.size = newSize
  203.         self.shadow.size = newSize
  204.         self.label.size = newSize
  205.         self.size = newSize
  206.     end
  207.    
  208.     if parentG then parentG:insert( text ) end
  209.     text.text = string
  210.     return text
  211. end; display.newEmbossedText = widget.embossedText
  212.  
  213. -----------------------------------------------------------------------------------------
  214. -----------------------------------------------------------------------------------------
  215. --
  216. -- button widget
  217. --
  218. -----------------------------------------------------------------------------------------
  219. -----------------------------------------------------------------------------------------
  220.  
  221. function widget.newButton( options )
  222.    
  223.     local function onButtonTouch( self, event ) -- self == button
  224.         local result = true
  225.         local phase = event.phase
  226.         event.name = "buttonEvent"
  227.         event.target = self
  228.  
  229.         if phase == "began" then
  230.             display.getCurrentStage():setFocus( self, event.id )
  231.             self.isFocus = true
  232.  
  233.             event.phase = "press"
  234.             if self.onEvent then
  235.                 result = self.onEvent( event )
  236.             elseif self.onPress then
  237.                 result = self.onPress( event )
  238.             end
  239.  
  240.             self.default.isVisible = false
  241.             self.over.isVisible = true
  242.             local r, g, b, a = self.label.color.over[1] or 0, self.label.color.over[2] or self.label.color.over[1], self.label.color.over[3] or self.label.color.over[1], self.label.color.over[4] or 255
  243.             self.label:setFillColor( r, g, b, a )
  244.  
  245.         elseif self.isFocus then
  246.             local bounds = self.contentBounds
  247.             local x, y = event.x, event.y
  248.             local isWithinBounds = bounds.xMin <= x and bounds.xMax >= x and bounds.yMin <= y and bounds.yMax >= y
  249.            
  250.             if phase == "moved" then
  251.                 if not isWithinBounds then
  252.                     self.default.isVisible = true
  253.                     self.over.isVisible = false
  254.  
  255.                     local r, g, b, a = self.label.color.default[1] or 0, self.label.color.default[2] or self.label.color.default[1], self.label.color.default[3] or self.label.color.default[1], self.label.color.default[4] or 255
  256.                     self.label:setFillColor( r, g, b, a )
  257.                 else
  258.                     self.default.isVisible = false
  259.                     self.over.isVisible = true
  260.  
  261.                     local r, g, b, a = self.label.color.over[1] or 0, self.label.color.over[2] or self.label.color.over[1], self.label.color.over[3] or self.label.color.over[1], self.label.color.over[4] or 255
  262.                     self.label:setFillColor( r, g, b, a )
  263.                 end
  264.  
  265.                 if self.onEvent then
  266.                     result = self.onEvent( event )
  267.                 elseif self.onDrag then
  268.                     result = self.onDrag( event )
  269.                 end
  270.  
  271.             elseif phase == "ended" or phase == "cancelled" then
  272.                 if self.default and self.over then
  273.                     self.default.isVisible = true
  274.                     self.over.isVisible = false
  275.                     local r, g, b, a = self.label.color.default[1] or 0, self.label.color.default[2] or self.label.color.default[1], self.label.color.default[3] or self.label.color.default[1], self.label.color.default[4] or 255
  276.                     self.label:setFillColor( r, g, b, a )
  277.                 end
  278.                
  279.                 -- trigger appropriate event listener if released within bounds of button
  280.                 if isWithinBounds then
  281.                     event.phase = "release"
  282.                     if self.onEvent then
  283.                         result = self.onEvent( event )
  284.                     elseif self.onRelease then
  285.                         result = self.onRelease( event )
  286.                     end
  287.                 end
  288.  
  289.                 -- remove focus from button
  290.                 display.getCurrentStage():setFocus( self, nil )
  291.                 self.isFocus = false
  292.             end
  293.         end
  294.  
  295.         return result
  296.     end
  297.    
  298.     local function setLabel( self, newLabel )   -- self == button
  299.         if not newLabel then return; end
  300.        
  301.         if self.label.setText then
  302.             self.label:setText( newLabel )
  303.         else
  304.             self.label.text = newLabel
  305.         end
  306.        
  307.         -- re-center label on button
  308.         self.label:setReferencePoint( display.CenterReferencePoint )
  309.         self.label.x = (self.contentWidth*0.5) + self.label.xOffset
  310.         self.label.y = (self.contentHeight*0.5) + self.label.yOffset
  311.     end
  312.    
  313.     local function getLabel( self )
  314.         return self.label.text
  315.     end
  316.    
  317.     local function removeSelf( self )   -- self == button
  318.         -- check to see if there is a clean method; if so, call it
  319.         if self.clean then self:clean(); end
  320.        
  321.         -- remove all children of default and over
  322.         if self.default and self.default.numChildren then
  323.             for i=self.default.numChildren,1,-1 do
  324.                 display.remove( self.default.numChildren[i] )
  325.             end
  326.             display.remove( self.default )
  327.         end
  328.        
  329.         if self.over and self.over.numChildren then
  330.             for i=self.over.numChildren,1,-1 do
  331.                 display.remove( self.over.numChildren[i] )
  332.             end
  333.             display.remove( self.over )
  334.         end
  335.        
  336.         -- remove label
  337.         display.remove( self.label )
  338.        
  339.         -- remove button group
  340.         self:cached_removeSelf()
  341.     end
  342.    
  343.     local function createButton( options, theme )
  344.         local   defaultBtnWidth = 124
  345.         local   defaultBtnHeight = 42
  346.        
  347.         local   options = options or {}
  348.         local   theme = theme or {}
  349.         local   id = options.id or "widget_button"
  350.         local   left = options.left or 0
  351.         local   top = options.top or 0
  352.         local   xOffset = options.xOffset or theme.xOffset or 0     -- offsets x value of the label text
  353.         local   yOffset = options.yOffset or options.offset or theme.yOffset or theme.offset or 0       -- offsets y value of the label text
  354.         local   label = options.label or ""
  355.         local   font = options.font or theme.font or native.systemFont
  356.         local   fontSize = options.fontSize or theme.fontSize or 14
  357.         local   emboss = options.emboss or theme.emboss
  358.         local   textFunction = display.newText; if emboss then textFunction = widget.embossedText; end
  359.         local   labelColor = options.labelColor or theme.labelColor or { default={ 0 }, over={ 255 } }
  360.         local   onPress = options.onPress
  361.         local   onRelease = options.onRelease
  362.         local   onDrag = options.onDrag
  363.         local   onEvent = options.onEvent
  364.         local   default = options.default or theme.default
  365.         local   defaultColor = options.defaultColor or theme.defaultColor
  366.         local   over = options.over or theme.over
  367.         local   overColor = options.overColor or theme.overColor
  368.         local   strokeColor = options.strokeColor or theme.strokeColor
  369.         local   strokeWidth = options.strokeWidth or theme.strokeWidth
  370.         local   cornerRadius = options.cornerRadius or theme.cornerRadius
  371.         local   width = options.width or theme.width
  372.         local   height = options.height or theme.height
  373.         local   sheet = options.sheet or theme.sheet
  374.         local   defaultIndex = options.defaultIndex or theme.defaultIndex
  375.         local   overIndex = options.overIndex or theme.overIndex
  376.         local   baseDir = options.baseDir or theme.baseDir or system.ResourceDirectory
  377.        
  378.         local button = display.newGroup()
  379.        
  380.         if default or sheet then
  381.  
  382.             -- user-provided image for default and over state
  383.             if sheet and defaultIndex and width and height then
  384.                 -- image sheet option
  385.                 button.default = display.newImageRect( button, sheet, defaultIndex, width, height )
  386.                 button.default:setReferencePoint( display.TopLeftReferencePoint )
  387.                 button.default.x, button.default.y = 0, 0
  388.  
  389.                 local over = overIndex or defaultIndex
  390.                 button.over = display.newImageRect( button, sheet, over, width, height )
  391.                 button.over:setReferencePoint( display.TopLeftReferencePoint )
  392.                 button.over.x, button.over.y = 0, 0
  393.  
  394.             elseif width and height then
  395.                 -- display.newImageRect() option (non)
  396.                 button.default = display.newImageRect( button, default, baseDir, width, height )
  397.                 button.default:setReferencePoint( display.TopLeftReferencePoint )
  398.                 button.default.x, button.default.y = 0, 0
  399.  
  400.                 local over = over or default
  401.                 button.over = display.newImageRect( button, over, baseDir, width, height )
  402.                 button.over:setReferencePoint( display.TopLeftReferencePoint )
  403.                 button.over.x, button.over.y = 0, 0
  404.             else
  405.                 -- display.newImage() option
  406.                 button.default = display.newImage( button, default, baseDir, true )
  407.                 button.default:setReferencePoint( display.TopLeftReferencePoint )
  408.                 button.default.x, button.default.y = 0, 0
  409.  
  410.                 local over = over or default
  411.                 button.over = display.newImage( button, over, baseDir, true )
  412.                 button.over:setReferencePoint( display.TopLeftReferencePoint )
  413.                 button.over.x, button.over.y = 0, 0
  414.                
  415.                 width, height = button.default.contentWidth, button.default.contentHeight
  416.             end
  417.  
  418.             if defaultColor then
  419.                 if defaultColor[1] then
  420.                     button.default:setFillColor( defaultColor[1], defaultColor[2] or defaultColor[1], defaultColor[3] or defaultColor[1], defaultColor[4] or 255 )
  421.                 end
  422.             end
  423.  
  424.             if overColor then
  425.                 if overColor[1] then
  426.                     button.over:setFillColor( overColor[1], overColor[2] or overColor[1], overColor[3] or overColor[1], overColor[4] or 255 )
  427.                 end
  428.             end
  429.         else
  430.             -- no images; construct button using newRoundedRect
  431.             if not width then width = defaultBtnWidth; end
  432.             if not height then height = defaultBtnHeight; end
  433.             if not cornerRadius then cornerRadius = 8; end
  434.  
  435.             button.default = display.newRoundedRect( button, 0, 0, width, height, cornerRadius )
  436.             button.over = display.newRoundedRect( button, 0, 0, width, height, cornerRadius )
  437.  
  438.             if defaultColor and defaultColor[1] then
  439.                 button.default:setFillColor( defaultColor[1], defaultColor[2] or defaultColor[1], defaultColor[3] or defaultColor[1], defaultColor[4] or 255 )
  440.             else
  441.                 button.default:setFillColor( 255 )
  442.             end
  443.  
  444.             if overColor and overColor[1] then
  445.                 button.over:setFillColor( overColor[1], overColor[2] or overColor[1], overColor[3] or overColor[1], overColor[4] or 255 )
  446.             else
  447.                 button.over:setFillColor( 128 )
  448.             end
  449.  
  450.             if strokeColor and strokeColor[1] then
  451.                 button.default:setStrokeColor( strokeColor[1], strokeColor[2] or strokeColor[1], strokeColor[3] or strokeColor[1], strokeColor[4] or 255 )
  452.                 button.over:setStrokeColor( strokeColor[1], strokeColor[2] or strokeColor[1], strokeColor[3] or strokeColor[1], strokeColor[4] or 255 )
  453.             else
  454.                 button.default:setStrokeColor( 0 )
  455.                 button.over:setStrokeColor( 0 )
  456.             end
  457.  
  458.             if not strokeWidth then
  459.                 button.default.strokeWidth = 1
  460.                 button.over.strokeWidth = 1
  461.             else
  462.                 button.default.strokeWidth = strokeWidth
  463.                 button.over.strokeWidth = strokeWidth
  464.             end
  465.         end
  466.         button.over.isVisible = false   -- hide "down/over" state of button
  467.        
  468.         -- create the label
  469.         if not labelColor then labelColor = {}; end
  470.         if not labelColor.default then labelColor.default = { 0 }; end
  471.         if not labelColor.over then labelColor.over = { 255 }; end
  472.         local r, g, b, a = labelColor.default[1] or 0, labelColor.default[2] or labelColor.default[1], labelColor.default[3] or labelColor.default[1], labelColor.default[4] or 255
  473.  
  474.         button.label = textFunction( button, label, 0, 0, font, fontSize )
  475.         button.label:setFillColor( r, g, b, a )
  476.         button.label:setReferencePoint( display.CenterReferencePoint )
  477.         button.label.x = (button.contentWidth * 0.5) + xOffset
  478.         button.label.y = (button.contentHeight * 0.5) + yOffset
  479.         button.label.color = labelColor
  480.         button.label.xOffset = xOffset
  481.         button.label.yOffset = yOffset
  482.        
  483.         -- set properties and methods
  484.         button._isWidget = true
  485.         button.id = id
  486.         button.onPress = onPress
  487.         button.onDrag = onDrag
  488.         button.onRelease = onRelease
  489.         button.onEvent = onEvent
  490.         button.touch = onButtonTouch; button:addEventListener( "touch", button )
  491.         button.cached_removeSelf = button.removeSelf
  492.         button.removeSelf = removeSelf
  493.         button.setLabel = setLabel
  494.         button.getLabel = getLabel
  495.        
  496.         -- position the button
  497.         button:setReferencePoint( display.TopLeftReferencePoint )
  498.         button.x, button.y = left, top
  499.         button:setReferencePoint( display.CenterReferencePoint )
  500.        
  501.         return button
  502.     end
  503.    
  504.     -- this widget supports visual customization via themes
  505.     local themeOptions
  506.     if widget.theme then
  507.         local buttonTheme = widget.theme.button
  508.        
  509.         if buttonTheme then
  510.             if options and options.style then   -- style parameter optionally set by user
  511.                
  512.                 -- for themes that support various "styles" per widget
  513.                 local style = buttonTheme[options.style]
  514.                
  515.                 if style then themeOptions = style; end
  516.             else
  517.                 -- if no style parameter set, use default style specified by theme
  518.                 themeOptions = buttonTheme
  519.             end
  520.         end
  521.     end
  522.    
  523.     return createButton( options, themeOptions )
  524. end
  525.  
  526. -----------------------------------------------------------------------------------------
  527. -----------------------------------------------------------------------------------------
  528. --
  529. -- slider widget
  530. --
  531. -----------------------------------------------------------------------------------------
  532. -----------------------------------------------------------------------------------------
  533.  
  534. function widget.newSlider( options )
  535.    
  536.     -- set slider value from 0 to 100
  537.     local function setSliderValue( self, value )    -- self == slider
  538.         -- make sure value is not less than 0 or greater than 100
  539.         if value < 0 then
  540.             value = 0
  541.         elseif value > 100 then
  542.             value = 100
  543.         else
  544.             value = mFloor( value ) -- round to the nearest whole number
  545.         end
  546.        
  547.         local width = self.max - self.min
  548.        
  549.         -- calculate percentage based on slidable width
  550.         local percent = value / 100
  551.        
  552.         -- move handle to new position
  553.         local x = (width * percent) + self.min
  554.         self.handle.x = x
  555.        
  556.         -- stretch fill image from left side to handle
  557.         local fillScaleX = (self.handle.x - self.min) / self.fillWidth
  558.         if fillScaleX <= 0 then fillScaleX = 0.1; end
  559.         self.fill.xScale = fillScaleX
  560.        
  561.         -- update reference to value
  562.         self.value = value
  563.     end
  564.    
  565.     -- dispatch slider event
  566.     local function dispatchSliderEvent( self )  -- self == slider
  567.         if self.listener then
  568.             local e = {}
  569.             e.name = "sliderEvent"
  570.             e.type = "sliderMoved"
  571.             e.target = self
  572.             e.value = self.value
  573.            
  574.             self.listener( e )
  575.         end
  576.     end
  577.    
  578.     -- slider touch event
  579.     local function onSliderTouch( self, event ) -- self == slider
  580.         if event.phase == "began" then
  581.             display.getCurrentStage():setFocus( self )
  582.             self.isFocus = true
  583.             self:setReferencePoint( display.CenterReferencePoint )
  584.            
  585.             local sliderX = (self.contentBounds.xMin + (self.contentWidth*0.5))
  586.             local x = event.x - sliderX
  587.             local width = self.max - self.min
  588.             local percent = mFloor(((( (width*0.5) + x) * 100) / width))
  589.             self:setValue( percent )
  590.            
  591.             dispatchSliderEvent( self )
  592.            
  593.         elseif self.isFocus then
  594.             local isWithinBounds = self.min <= event.x and self.max >= event.x
  595.            
  596.             if event.phase == "moved" then
  597.                
  598.                 local sliderX = (self.contentBounds.xMin + (self.contentWidth*0.5))
  599.                 local x = event.x - sliderX
  600.                 local width = self.max - self.min
  601.                 local percent = mFloor(((( (width*0.5) + x) * 100) / width))
  602.                 self:setValue( percent )
  603.                
  604.                 dispatchSliderEvent( self )
  605.            
  606.             elseif event.phase == "ended" or event.phase == "cancelled" then
  607.                
  608.                 display.getCurrentStage():setFocus( nil )
  609.                 self.isFocus = nil
  610.             end
  611.         end
  612.        
  613.         return true
  614.     end
  615.    
  616.     -- removeSelf() method for slider widget
  617.     local function removeSelf( self )
  618.         if self.clean and type(self.clean) == "function" then self:clean(); end
  619.        
  620.         if self.fill then self.fill:removeSelf(); self.fill = nil; end
  621.         if self.handle then self.handle:removeSelf(); self.handle = nil; end
  622.         self.fillWidth = nil
  623.         self.value = nil
  624.        
  625.         self:cached_removeSelf()
  626.     end
  627.    
  628.     local function createSlider( options, theme )
  629.         local options = options or {}
  630.         local theme = theme or {}
  631.         local id = options.id or "widget_slider"
  632.        
  633.         local left = options.left or 0
  634.         local top = options.top or 0
  635.         local width = options.width or theme.width or 200
  636.         local height = options.height or theme.height or 10
  637.         local background = options.background or theme.background
  638.         local handleImage = options.handle or theme.handle
  639.         local handleWidth = options.handleWidth or theme.handleWidth
  640.         local handleHeight = options.handleHeight or theme.handleHeight
  641.         local leftImage = options.leftImage or theme.leftImage
  642.         local leftWidth = options.leftWidth or theme.leftWidth or 16
  643.         local fillImage = options.fillImage or theme.fillImage
  644.         local fillWidth = options.fillWidth or theme.fillWidth or 2
  645.         local cornerRadius = options.cornerRadius or theme.cornerRadius or 5
  646.         local value = options.value or 50
  647.         local listener = options.listener or options.callback
  648.         local baseDir = options.baseDir or theme.baseDir or system.ResourceDirectory
  649.        
  650.         local fillColor = options.fillColor or theme.fillColor or {}
  651.                 fillColor[1] = fillColor[1] or 0
  652.                 fillColor[2] = fillColor[2] or 100
  653.                 fillColor[3] = fillColor[3] or 230
  654.                 fillColor[4] = fillColor[4] or 255
  655.        
  656.         local handleColor = options.handleColor or theme.handleColor or {}
  657.                 handleColor[1] = handleColor[1] or 189
  658.                 handleColor[2] = handleColor[2] or 189
  659.                 handleColor[3] = handleColor[3] or 189
  660.                 handleColor[4] = handleColor[4] or 255
  661.        
  662.         local handleStroke = options.handleStroke or theme.handleStroke or {}
  663.                 handleStroke[1] = handleStroke[1] or 143
  664.                 handleStroke[2] = handleStroke[2] or 143
  665.                 handleStroke[3] = handleStroke[3] or 143
  666.                 handleStroke[4] = handleStroke[4] or 255
  667.        
  668.         local bgFill = options.bgFill or theme.bgFill or {}
  669.                 bgFill[1] = bgFill[1] or 225
  670.                 bgFill[2] = bgFill[2] or 225
  671.                 bgFill[3] = bgFill[3] or 225
  672.                 bgFill[4] = bgFill[4] or 255
  673.        
  674.         local bgStroke = options.bgStroke or theme.bgStroke or {}
  675.                 bgStroke[1] = bgStroke[1] or 102
  676.                 bgStroke[2] = bgStroke[2] or 102
  677.                 bgStroke[3] = bgStroke[3] or 102
  678.                 bgStroke[4] = bgStroke[4] or 255
  679.        
  680.         -- construct slider widget based on provided parameters (or defaults)
  681.         local slider = display.newGroup()
  682.         local bg, leftSide, fill, handle
  683.        
  684.         if not background and not fillImage then       
  685.             bg = display.newRoundedRect( slider, 0, 0, width, height, cornerRadius )
  686.             bg.strokeWidth = 1
  687.             bg:setStrokeColor( bgStroke[1], bgStroke[2], bgStroke[3], bgStroke[4] )
  688.             bg:setFillColor( bgFill[1], bgFill[2], bgFill[3], bgFill[4] )
  689.            
  690.             leftSide = display.newRoundedRect( slider, 0, 0, leftWidth, height, cornerRadius )
  691.             leftSide:setReferencePoint( display.CenterReferencePoint )
  692.             leftSide:setFillColor( fillColor[1], fillColor[2], fillColor[3], fillColor[4] )
  693.            
  694.             fill = display.newRect( slider, leftWidth*0.5, 0, fillWidth, height )
  695.             fill:setReferencePoint( display.CenterLeftReferencePoint )
  696.             fill:setFillColor( fillColor[1], fillColor[2], fillColor[3], fillColor[4] )
  697.        
  698.         elseif background and fillImage then
  699.             bg = display.newImageRect( slider, background, baseDir, width, height )
  700.             bg:setReferencePoint( display.TopLeftReferencePoint )
  701.             bg.x, bg.y = 0, 0
  702.            
  703.             fill = display.newImageRect( slider, fillImage, baseDir, fillWidth, height )
  704.             fill:setReferencePoint( display.CenterLeftReferencePoint )
  705.             fill.x, fill.y = leftWidth, height * 0.5
  706.         else
  707.             if background and not fillImage then
  708.                 print( "WARNING: You must also specify a fillImage when using a custom background with the slider widget." )
  709.                 return
  710.             elseif fillImage and not background then
  711.                 print( "WARNING: You must specify a custom background when using a custom fillImage with the slider widget." )
  712.                 return
  713.             end
  714.         end
  715.        
  716.         slider.fill = fill
  717.         slider.fillWidth = fillWidth
  718.        
  719.         if not handleImage or not handleWidth or not handleHeight then
  720.             handle = display.newCircle( slider, width*0.5, height*0.5, height )
  721.             handle:setReferencePoint( display.CenterReferencePoint )
  722.             handle:setFillColor( handleColor[1], handleColor[2], handleColor[3], handleColor[4] )
  723.             handle.strokeWidth = 1
  724.             handle:setStrokeColor( handleStroke[1], handleStroke[2], handleStroke[3], handleStroke[4] )
  725.         else
  726.             handle = display.newImageRect( slider, handleImage, handleWidth, handleHeight )
  727.             handle:setReferencePoint( display.CenterReferencePoint )
  728.             handle.x, handle.y = width*0.5, height*0.5
  729.         end
  730.         slider.handle = handle
  731.        
  732.         -- properties and methods
  733.         slider._isWidget = true
  734.         slider.id = id
  735.         slider.min = leftWidth*0.5
  736.         slider.max = width - (leftWidth * 0.5)
  737.         slider.setValue = setSliderValue
  738.         slider.touch = onSliderTouch
  739.         slider:addEventListener( "touch", slider )
  740.         slider.listener = listener
  741.        
  742.         slider.cached_removeSelf = slider.removeSelf
  743.         slider.removeSelf = removeSelf
  744.        
  745.         local fillScaleX = (handle.x - slider.min) / fillWidth
  746.         fill.xScale = fillScaleX
  747.        
  748.         -- position the widget and set reference point to center
  749.         slider.x, slider.y = left, top
  750.         slider:setReferencePoint( display.CenterReferencePoint )
  751.        
  752.         -- set initial value
  753.         slider:setValue( value )
  754.        
  755.         return slider
  756.     end
  757.    
  758.     -- this widget supports visual customization via themes
  759.     local themeOptions
  760.     if widget.theme then
  761.         local sliderTheme = widget.theme.slider
  762.        
  763.         if sliderTheme then
  764.             if options and options.style then   -- style parameter optionally set by user
  765.                
  766.                 -- for themes that support various "styles" per widget
  767.                 local style = sliderTheme[options.style]
  768.                
  769.                 if style then themeOptions = style; end
  770.             else
  771.                 -- if no style parameter set, use default style specified by theme
  772.                 themeOptions = sliderTheme
  773.             end
  774.         end
  775.     end
  776.    
  777.     return createSlider( options, themeOptions )
  778. end
  779.  
  780. -----------------------------------------------------------------------------------------
  781. -----------------------------------------------------------------------------------------
  782. --
  783. -- pickerWheel widget
  784. --
  785. -----------------------------------------------------------------------------------------
  786. -----------------------------------------------------------------------------------------
  787.  
  788. --Function to handle the soft-landing of the picker wheel
  789. local function pickerSoftLand( self )
  790.     local target = self.parent
  791.    
  792.     --Variables that equal the ones used in picker.getValues
  793.     local height = self.height
  794.     local selectionHeight = self.selectionHeight
  795.     local top = self.parent.parent.parent.y --Get the actual pickers groups y position to use as the top position
  796.     local selectionTop = target.topPadding
  797.    
  798.     --Index to scroll to                           
  799.     local index = nil
  800.    
  801.     --Get row using same system at picker.getValues uses
  802.     if target:getRowAtCoordinate( top + selectionTop + ( selectionHeight * 0.5 ) ) ~= nil then
  803.         index = target:getRowAtCoordinate( top + selectionTop + ( selectionHeight * 0.5 ) ).index
  804.     end
  805.    
  806.     --If there is an index, scroll to it to give the impression of soft landing
  807.     if index ~= nil then
  808.         target:scrollToIndex( index, 400 )
  809.     end
  810. end
  811.  
  812. function widget.newPickerWheel( options )
  813.     -- get selection values of pickerWheel columns (returns table)
  814.     local function getValues( self )    -- self == pickerWheel
  815.         local columnValues = {}
  816.         local columns = self.columns
  817.         local top = self.y
  818.         local selectionTop = self.selectionTop or 255
  819.         local selectionHeight = self.selectionHeight or 46
  820.        
  821.         --print( selectionTop)
  822.                        
  823.         for i=1,columns.numChildren do
  824.             local col = columns[i]
  825.             local realSelectionY = top + selectionTop + (selectionHeight*0.5)
  826.             local row = col:getRowAtCoordinate( realSelectionY )
  827.                        
  828.             if row and row.value and row.index then
  829.                 columnValues[i] = {}
  830.                 columnValues[i].value = row.value
  831.                 columnValues[i].index = row.index
  832.             end
  833.         end
  834.        
  835.         return columnValues
  836.     end
  837.    
  838.    
  839.     -- creates new pickerWheel column
  840.     local function newPickerColumn( pickerWheel, parentGroup, columnData, params )
  841.         local column = widget.newTableView( params )
  842.        
  843.         -- create individual 'rows' for the column
  844.         for i=1,#columnData do
  845.             local labelX = 14
  846.             local ref = display.CenterLeftReferencePoint
  847.            
  848.             if columnData.alignment and columnData.alignment ~= "left" then
  849.                 if columnData.alignment == "center" then
  850.                     labelX = params.width * 0.5
  851.                     ref = display.CenterReferencePoint
  852.                 elseif columnData.alignment == "right" then
  853.                     labelX = params.width - 14
  854.                     ref = display.CenterRightReferencePoint
  855.                 end
  856.             end
  857.            
  858.             local function renderRow( event )
  859.                 local row = event.row
  860.                 local view = event.view
  861.                 local fc = params.fontColor
  862.                
  863.                 local label = display.newText( columnData[i], 0, 0, params.font, params.fontSize )
  864.                 label:setTextColor( fc[1], fc[2], fc[3], fc[4] )
  865.                 label:setReferencePoint( ref )
  866.                 label.x = labelX
  867.                 label.y = row.height * 0.5
  868.                
  869.                 row.value = columnData[i]
  870.                 view:insert( label )
  871.             end
  872.            
  873.            
  874.             column:insertRow{
  875.                 onRender = renderRow,
  876.                 width = params.width,
  877.                 height = params.rowHeight or 32,
  878.                 rowColor = params.bgColor or { 255, 255, 255, 255 },
  879.                 lineColor = params.bgColor or { 255, 255, 255, 255 },
  880.                 skipRender = true,
  881.             }
  882.         end
  883.        
  884.         parentGroup:insert( column )
  885.        
  886.         return column
  887.     end
  888.    
  889.     -- subclassed removeSelf method for pickerWheel
  890.     local function removeSelf( self )   -- self == pickerWheel
  891.         -- check to see if there is a clean method; if so, call it
  892.         if self.clean then self:clean(); end
  893.        
  894.         -- remove mask if it exists
  895.         if self.mask then
  896.             self.columns:setMask( nil )
  897.             self.mask = nil
  898.         end
  899.        
  900.         -- remove each column one by one
  901.         for i=self.columns.numChildren,1,-1 do
  902.             self.columns[i]:removeSelf()
  903.         end
  904.         self.columns = nil
  905.                
  906.         -- remove pickerWheel widget
  907.         self:cached_removeSelf()
  908.     end
  909.    
  910.     local function createPickerWheel( options, themeOptions )
  911.         local options = options or {}
  912.         local theme = themeOptions or {}
  913.  
  914.         -- parse parameters (options) or set defaults (or theme defaults)
  915.         local id = options.id or "widget_pickerWheel"
  916.         local left = options.left or 0
  917.         local top = options.top or 0
  918.         local width = options.width or theme.width or 296
  919.         local height = options.height or theme.height or 222
  920.         local bgWidth = options.bgWidth or options.totalWidth or theme.bgWidth or theme.totalWidth or display.contentWidth
  921.         local selectionTop = options.selectionTop or theme.selectionTop or 90
  922.         local selectionHeight = options.selectionHeight or theme.selectionHeight or 46
  923.         local font = options.font or theme.font or system.nativeFontBold
  924.         local fontSize = options.fontSize or theme.fontSize or 22
  925.         local fontColor = options.fontColor or theme.fontColor or {}
  926.             fontColor[1] = fontColor[1] or 0
  927.             fontColor[2] = fontColor[2] or fontColor[1]
  928.             fontColor[3] = fontColor[3] or fontColor[1]
  929.             fontColor[4] = fontColor[4] or 255
  930.         local columnColor = options.columnColor or theme.columnColor or {}
  931.             columnColor[1] = columnColor[1] or 255
  932.             columnColor[2] = columnColor[2] or columnColor[1]
  933.             columnColor[3] = columnColor[3] or columnColor[1]
  934.             columnColor[4] = columnColor[4] or 255
  935.         local columns = options.columns or { { "One", "Two", "Three", "Four", "Five" } }
  936.         local maskFile = options.maskFile or theme.maskFile
  937.         local bgImage = options.bgImage or options.background or theme.bgImage or theme.background
  938.         local bgImageWidth = options.bgImageWidth or options.backgroundWidth or theme.bgImageWidth or theme.backgroundWidth
  939.         local bgImageHeight = options.bgImageHeight or options.backgroundHeight or theme.bgImageHeight or theme.backgroundHeight or height
  940.         local overlayImage = options.overlayImage or options.glassFile or theme.overlayImage or theme.glassFile
  941.         local overlayWidth = options.overlayWidth or options.glassWidth or theme.overlayWidth or theme.glassWidth
  942.         local overlayHeight = options.overlayHeight or options.glassHeight or theme.overlayHeight or theme.glassHeight
  943.         local separator = options.separator or theme.separator
  944.         local separatorWidth = options.separatorWidth or theme.separatorWidth
  945.         local separatorHeight = options.separatorHeight or theme.separatorHeight
  946.         local baseDir = options.baseDir or theme.baseDir or system.ResourceDirectory
  947.        
  948.         local pickerWheel = display.newGroup()
  949.         local columnGroup = display.newGroup()  -- will hold all column groups (tableViews)
  950.        
  951.         -- create background image
  952.         if bgImage then
  953.             local bg = display.newImageRect( pickerWheel, bgImage, baseDir, bgImageWidth, bgImageHeight )
  954.             bg:setReferencePoint( display.TopLeftReferencePoint )
  955.             bg.x, bg.y = 0, 0
  956.             bg.xScale = bgWidth / bg.contentWidth
  957.            
  958.             local function disableTouchLeak() return true; end
  959.             bg.touch = disableTouchLeak
  960.             bg:addEventListener( "touch", bg )
  961.         end
  962.        
  963.         -- insert the columns group into the pickerWheel widget group
  964.         pickerWheel:insert( columnGroup )
  965.         columnGroup.x = (bgWidth * 0.5) - width * 0.5
  966.         columnGroup.y = 0
  967.        
  968.         local currentX = 0  -- variable that used for x-location of each column
  969.        
  970.         -- create all columns
  971.         for i=1,#columns do
  972.             local col = columns[i]
  973.            
  974.             -- set up tableView options (each column is a tableView widget)
  975.             local params = {}
  976.             -- tableView specific parameters
  977.             params.id = "pickerColumn_" .. i
  978.             params.renderThresh = (height - selectionTop) + selectionHeight
  979.             params.left = 0
  980.             params.top = 0
  981.             params.topPadding = selectionTop
  982.             params.bottomPadding = height - (selectionTop+selectionHeight)
  983.             params.width = col.width or width/#columns
  984.             params.height = height
  985.             params.bgColor = columnColor
  986.             params.friction = pickerFriction
  987.             params.keepRowsPastTopVisible = true
  988.             params.hideScrollBar = true
  989.            
  990.             --Used for controlling the pickers softlanding
  991.             params.selectionHeight = selectionHeight
  992.             params.isPicker = true
  993.             params.pickerTop = top
  994.            
  995.             -- if last column, ensure width fills remaining space
  996.             if i == #columns then params.width = width - currentX; end
  997.            
  998.             -- picker-specific parameters
  999.             params.rowHeight = selectionHeight
  1000.             params.font = font
  1001.             params.fontSize = fontSize
  1002.             params.fontColor = fontColor
  1003.            
  1004.             -- create line separator that goes between the rows
  1005.             local separatorLine
  1006.             if separator and i ~= #columns then
  1007.                 separatorLine = display.newImageRect( pickerWheel, separator, baseDir, separatorWidth, separatorHeight )
  1008.                 separatorLine:setReferencePoint( display.TopLeftReferencePoint )
  1009.                 separatorLine.x = (currentX + params.width) + columnGroup.x
  1010.                 separatorLine.y = 0
  1011.                 separatorLine.yScale = height / separatorLine.height
  1012.             end
  1013.            
  1014.             -- create the column
  1015.             local pickerColumn = newPickerColumn( pickerWheel, columnGroup, col, params )
  1016.             pickerColumn.x = currentX
  1017.             if #col <= 2 then pickerColumn.content.shortList = true; end
  1018.            
  1019.             currentX = currentX + params.width
  1020.            
  1021.             -- scroll to startIndex if specified
  1022.             if col.startIndex and col.startIndex > 1 then
  1023.                 pickerColumn:scrollToIndex( col.startIndex )
  1024.             else
  1025.                 pickerColumn:scrollToIndex( 1 )
  1026.             end
  1027.         end
  1028.        
  1029.         -- apply mask to columnGroup
  1030.         if maskFile then
  1031.             pickerWheel.mask = graphics.newMask( maskFile )
  1032.             columnGroup:setMask( pickerWheel.mask )
  1033.             columnGroup.maskX = columnGroup.width * 0.5
  1034.             columnGroup.maskY = height * 0.5
  1035.             columnGroup.isHitTestMasked = false
  1036.         end
  1037.        
  1038.         -- create overlay to go above columns
  1039.         if overlayImage then
  1040.             local overlay
  1041.             if overlayWidth and overlayHeight then
  1042.                 overlay = display.newImageRect( pickerWheel, overlayImage, overlayWidth, overlayHeight )
  1043.             else
  1044.                 overlay = display.newImage( pickerWheel, overlayImage, true )
  1045.             end
  1046.             overlay:setReferencePoint( display.CenterReferencePoint )
  1047.             overlay.x = bgWidth * 0.5
  1048.             overlay.y = height * 0.5
  1049.         end
  1050.        
  1051.         -- properties and methods
  1052.         pickerWheel._isWidget = true
  1053.         pickerWheel._isPicker = true
  1054.         pickerWheel.id = id
  1055.         pickerWheel.columns = columnGroup
  1056.         pickerWheel.getValues = getValues
  1057.         pickerWheel.selectionTop = selectionTop
  1058.         pickerWheel.cached_removeSelf = pickerWheel.removeSelf
  1059.         pickerWheel.removeSelf = removeSelf
  1060.        
  1061.         -- position the widget
  1062.         pickerWheel.x, pickerWheel.y = left, top
  1063.        
  1064.         return pickerWheel
  1065.     end
  1066.    
  1067.     -- this widget requires visual customization via themes to work properly
  1068.     local themeOptions
  1069.     if widget.theme then
  1070.         local pickerTheme = widget.theme.pickerWheel
  1071.        
  1072.         if pickerTheme then
  1073.             if options and options.style then   -- style parameter optionally set by user
  1074.                
  1075.                 -- for themes that support various "styles" per widget
  1076.                 local style = pickerTheme[options.style]
  1077.                
  1078.                 if style then themeOptions = style; end
  1079.             else
  1080.                 -- if no style parameter set, use default style specified by theme
  1081.                 themeOptions = pickerTheme
  1082.             end
  1083.            
  1084.             return createPickerWheel( options, themeOptions )
  1085.         else
  1086.             print( "WARNING: The widget theme you are using does not support the pickerWheel widget." )
  1087.             return
  1088.         end
  1089.     else
  1090.         print( "WARNING: The pickerWheel widget requires a visual theme. Use widget.setTheme()." )
  1091.         return
  1092.     end
  1093. end
  1094.  
  1095. -----------------------------------------------------------------------------------------
  1096. -----------------------------------------------------------------------------------------
  1097. --
  1098. -- scrollView widget
  1099. --
  1100. -----------------------------------------------------------------------------------------
  1101. -----------------------------------------------------------------------------------------
  1102.  
  1103. function widget.newScrollView( options )
  1104.     local function dispatchBeganScroll( self, parent_widget )   -- self == content
  1105.         local e = {}
  1106.         e.name = "scrollEvent"
  1107.         e.type = "beganScroll"
  1108.         e.target = parent_widget or self.parent
  1109.         self.hasScrolled = false --Used to set whether the scrollview has actually being scrolled or just pressed
  1110.         if self.listener then self.listener( e ); end
  1111.     end
  1112.    
  1113.     local function dispatchEndedScroll( self )  -- self == content
  1114.         local e = {}
  1115.         e.name = "scrollEvent"
  1116.         e.type = "endedScroll"
  1117.         e.target = self.parent
  1118.         if self.listener then self.listener( e ); end
  1119.         --If the scrollbar isn't hidden
  1120.         if self.hideScrollBar == false then
  1121.             self.parent:hide_scrollbar()
  1122.         end
  1123.        
  1124.        
  1125.     end
  1126.    
  1127.     local function dispatchPickerSoftland( self )
  1128.         local e = {}
  1129.         e.target = self.parent
  1130.        
  1131.         --Make picker wheel softland
  1132.         if e.target._isPicker then
  1133.             pickerSoftLand( self )
  1134.         end
  1135.     end
  1136.    
  1137.     local function limitScrollViewMovement( self, upperLimit, lowerLimit )  -- self == content
  1138.         local function endedScroll()
  1139.             self.tween = nil
  1140.             if self.listener then
  1141.                 --Dispatch the picker soft land
  1142.                 dispatchPickerSoftland( self )
  1143.                
  1144.                 --If the scrollview has scrolled then dispatch the ended scroll event ( this will trigger when the content has stopped moving )
  1145.                 if self.hasScrolled == true then
  1146.                     dispatchEndedScroll( self )
  1147.                     self.hasScrolled = false
  1148.                 end
  1149.             else
  1150.                 --If the scrollbar isn't hidden
  1151.                 if self.hideScrollBar == false then
  1152.                     self.parent:hide_scrollbar()
  1153.                 end
  1154.             end
  1155.         end
  1156.        
  1157.         local tweenContent = function( limit )
  1158.             if self.tween then transition.cancel( self.tween ); end
  1159.             if not self.isFocus then  -- if content is not being touched by user
  1160.                 self.tween = transition.to( self, { time=400, y=limit, transition=easing.outQuad, onComplete=endedScroll } )
  1161.             end
  1162.            
  1163.             --If the scrollbar isn't hidden        
  1164.             if self.hideScrollBar == false then
  1165.                 Runtime:addEventListener( "enterFrame", self.scrollbar_listener )
  1166.             end
  1167.         end
  1168.         local moveProperty = "y"
  1169.         local e = { name="scrollEvent", target=self.parent }
  1170.         local eventMin = "movingToTopLimit"
  1171.         local eventMax = "movingToBottomLimit"
  1172.        
  1173.         if self.moveDirection == "horizontal" then
  1174.             tweenContent = function( limit )
  1175.                 if self.tween then transition.cancel( self.tween ); end
  1176.                 self.tween = transition.to( self, { time=400, x=limit, transition=easing.outQuad, onComplete=endedScroll } )
  1177.             end
  1178.             moveProperty = "x"
  1179.             eventMin = "movingToLeftLimit"
  1180.             eventMax = "movingToRightLimit"
  1181.         end
  1182.        
  1183.         if self[moveProperty] > upperLimit then
  1184.    
  1185.             -- Content has drifted above upper limit of scrollView
  1186.             -- Stop content movement and transition back down to upperLimit
  1187.    
  1188.             self.velocity = 0
  1189.             Runtime:removeEventListener( "enterFrame", self )
  1190.             tweenContent( upperLimit )
  1191.            
  1192.             -- dispatch scroll event
  1193.             if self.listener then
  1194.                 e.type = eventMin
  1195.                 self.listener( e )
  1196.             end
  1197.    
  1198.         elseif self[moveProperty] < lowerLimit and lowerLimit < 0 then
  1199.    
  1200.             -- Content has drifted below lower limit (in case lower limit is above screen bounds)
  1201.             -- Stop content movement and transition back up to lowerLimit
  1202.    
  1203.             self.velocity = 0
  1204.             Runtime:removeEventListener( "enterFrame", self )
  1205.             tweenContent( lowerLimit )
  1206.            
  1207.             -- dispatch scroll event
  1208.             if self.listener then
  1209.                 e.type = eventMax
  1210.                 self.listener( e )
  1211.             end
  1212.            
  1213.         elseif self[moveProperty] < lowerLimit then
  1214.            
  1215.             -- Top of content has went past lower limit (in positive-y direction)
  1216.             -- Stop content movement and transition content back to upperLimit
  1217.            
  1218.             self.velocity = 0
  1219.             Runtime:removeEventListener( "enterFrame", self )
  1220.             if not self.shortList then
  1221.                 tweenContent( upperLimit )
  1222.             else
  1223.                 tweenContent( lowerLimit )
  1224.             end
  1225.            
  1226.             -- dispatch scroll event
  1227.             if self.listener then
  1228.                 e.type = eventMin
  1229.                 self.listener( e )
  1230.             end
  1231.         end
  1232.     end
  1233.    
  1234.     local function onScrollViewUpdate( self, event )    -- self == content
  1235.         if not self.trackVelocity then
  1236.             local time = event.time
  1237.             local timePassed = time - self.lastTime
  1238.             self.lastTime = time
  1239.    
  1240.             -- stop scrolling when velocity gets close to zero
  1241.             if mAbs( self.velocity ) < .01 then
  1242.                 self.velocity = 0
  1243.                
  1244.                 if self.moveDirection ~= "horizontal" then
  1245.                     self.y = mFloor( self.y )
  1246.                
  1247.                     -- if pulled past upper/lower boundaries, tween content properly
  1248.                     limitScrollViewMovement( self, self.upperLimit, self.lowerLimit )
  1249.                 else
  1250.                     self.x = mFloor( self.x )
  1251.                    
  1252.                     -- if pulled past left/right boundaries, tween content properly
  1253.                     limitScrollViewMovement( self, self.leftLimit, self.rightLimit )
  1254.                 end
  1255.                
  1256.                 self.moveDirection = nil
  1257.                 Runtime:removeEventListener( "enterFrame", self )
  1258.                
  1259.                 if self.listener then
  1260.                     --Dispatch the pickers soft land
  1261.                     dispatchPickerSoftland( self )
  1262.                    
  1263.                     -- dispatch an "endedScroll" event.type to user-specified listener
  1264.                     --If the scrollview has scrolled then dispatch the ended scroll event ( this will trigger when the content has stopped moving )
  1265.                     if self.hasScrolled == true then
  1266.                         dispatchEndedScroll( self )
  1267.                         self.hasScrolled = false
  1268.                     end
  1269.                 end
  1270.  
  1271.                 -- self.tween is a transition that occurs when content is above or below lower limits
  1272.                 -- and calls hide_scrollbar(), so the method does not need to be called here if self.tween exists
  1273.                 if not self.tween then
  1274.                     --If the scrollbar isn't hidden
  1275.                     if self.hideScrollBar == false then
  1276.                         self.parent:hide_scrollbar();
  1277.                     end
  1278.                 end
  1279.             else
  1280.                 -- update velocity and content location on every framestep
  1281.                 local moveProperty = "y"
  1282.                 if self.moveDirection == "horizontal" then moveProperty = "x"; end
  1283.                 self.velocity = self.velocity * self.friction
  1284.                 self[moveProperty] = self[moveProperty] + (self.velocity * timePassed)
  1285.                
  1286.                 if moveProperty ~= "x" then
  1287.                     limitScrollViewMovement( self, self.upperLimit, self.lowerLimit )
  1288.                 else
  1289.                     limitScrollViewMovement( self, self.leftLimit, self.rightLimit )
  1290.                 end
  1291.             end
  1292.         else
  1293.             -- for timing how long user has finger held down
  1294.             if self.moveDirection == "vertical" then       
  1295.                 if self.prevY == self.y then
  1296.                     if self.eventStep > 5 then
  1297.                         -- if finger is held down for 5 frames, ensure velocity is reset to 0
  1298.                         self.prevY = self.y
  1299.                         self.velocity = 0
  1300.                         self.eventStep = 0
  1301.                     else
  1302.                         self.eventStep = self.eventStep + 1
  1303.                     end
  1304.                 end
  1305.             elseif self.moveDirection == "horizontal" then
  1306.                 if self.prevX == self.x then
  1307.                     if self.eventStep > 5 then
  1308.                         -- if finger is held down for 5 frames, ensure velocity is reset to 0
  1309.                         self.prevX = self.x
  1310.                         self.velocity = 0
  1311.                         self.eventStep = 0
  1312.                     else
  1313.                         self.eventStep = self.eventStep + 1
  1314.                     end
  1315.                 end
  1316.             end
  1317.         end
  1318.  
  1319.         --If the scrollbar isn't hidden
  1320.         if self.hideScrollBar == false then
  1321.             self.parent:update_scrollbar()
  1322.         end
  1323.     end
  1324.    
  1325.     local function onContentTouch( self, event )    -- self == content
  1326.         local scrollView = self.parent
  1327.         local phase = event.phase
  1328.         local time = event.time
  1329.         local hasScrolled = nil
  1330.        
  1331.         if phase == "began" then
  1332.            
  1333.             -- set focus on scrollView content
  1334.             display.getCurrentStage():setFocus( self )
  1335.             self.isFocus = true
  1336.            
  1337.             -- remove listener for auto-movement based on velocity
  1338.             Runtime:removeEventListener( "enterFrame", self )
  1339.  
  1340.             -- TODO: Restructure code into "transactions" that represent the different states
  1341.             -- of scrolling to "bottleneck" things like the following 'removeEventListener()' call
  1342.             -- so they are not sprinkled all over the place.
  1343.  
  1344.             Runtime:removeEventListener( "enterFrame", scrollView.content.scrollbar_listener )
  1345.             scrollView:cancel_scrollbar_hide()
  1346.            
  1347.             -- set some variables necessary movement/scrolling
  1348.             self.velocity = 0
  1349.             self.prevX = self.x
  1350.             self.prevY = self.y
  1351.             self.prevPositionX = event.x
  1352.             self.prevPositionY = event.y
  1353.             self.trackVelocity = true
  1354.             self.markTime = time
  1355.             self.eventStep = 0
  1356.             self.upperLimit = scrollView.topPadding or 0
  1357.             self.lowerLimit = self.maskHeight - self.contentHeight
  1358.             self.leftLimit = 0
  1359.             self.rightLimit = self.maskWidth - self.contentWidth
  1360.            
  1361.             -- determine whether or not to disable horizontal/vertical scrolling
  1362.             if self.contentWidth <= self.maskWidth or scrollView.isVirtualized then
  1363.                 self.horizontalScrollDisabled = true
  1364.             end
  1365.            
  1366.             -- reset move direction
  1367.             self.moveDirection = nil
  1368.            
  1369.             -- for tableviews:
  1370.             if scrollView.isVirtualized then
  1371.                 self.moveDirection = "vertical"
  1372.             end
  1373.            
  1374.             -- begin enterFrame listener (for velocity calculations)
  1375.             Runtime:addEventListener( "enterFrame", self )
  1376.            
  1377.             -- dispatch scroll event
  1378.            
  1379.             if self.listener then
  1380.                 local event = event
  1381.                 event.name = "scrollEvent"
  1382.                 event.type = "contentTouch"
  1383.                 event.phase = "press"
  1384.                 event.target = scrollView
  1385.                 self.listener( event )
  1386.             end
  1387.            
  1388.            
  1389.             -- change lowerLimit if scrollView is "virtualized" (used for tableViews)
  1390.             if scrollView.isVirtualized and scrollView.virtualContentHeight then
  1391.                 self.lowerLimit = self.maskHeight - scrollView.virtualContentHeight
  1392.                 if scrollView.bottomPadding then
  1393.                     self.lowerLimit = self.lowerLimit - scrollView.bottomPadding
  1394.                 end
  1395.             end
  1396.        
  1397.         elseif self.isFocus then
  1398.             if phase == "moved" and not scrollView.isLocked then
  1399.            
  1400.                 -- ensure content isn't trying to move while user is dragging content
  1401.                 if self.tween then transition.cancel( self.tween ); self.tween = nil; end
  1402.                
  1403.                 -- determine if user is attempting to move content left/right or up/down
  1404.                 if not self.moveDirection then
  1405.                     if not self.verticalScrollDisabled or not self.horizontalScrollDisabled then
  1406.                         local dx = mAbs(event.x - event.xStart)
  1407.                         local dy = mAbs(event.y - event.yStart)
  1408.                         local moveThresh = 8
  1409.                        
  1410.                         if dx > moveThresh or dy > moveThresh then
  1411.                             if dx > dy then
  1412.                                 self.moveDirection = "horizontal"
  1413.                             else
  1414.                                 self.moveDirection = "vertical"
  1415.                             end
  1416.                            
  1417.                             --The content has actually started to scroll so dispatch the beganScroll event
  1418.                             dispatchBeganScroll( self, scrollView )
  1419.                             self.hasScrolled = true
  1420.                         end
  1421.                     end
  1422.                 else
  1423.                    
  1424.                     -- Finger movement and swiping; prevent content from sticking past boundaries
  1425.                    
  1426.                     if self.moveDirection == "vertical" and not self.verticalScrollDisabled then
  1427.                         -- VERTICAL movement
  1428.                        
  1429.                         self.delta = event.y - self.prevPositionY
  1430.                         self.prevPositionY = event.y
  1431.                        
  1432.                         -- do "elastic" effect when finger is dragging content past boundaries
  1433.                         if self.y > self.upperLimit or self.y < self.lowerLimit then
  1434.                             self.y = self.y + self.delta/2
  1435.                         else
  1436.                             self.y = self.y + self.delta
  1437.                         end
  1438.                        
  1439.                         -- modify velocity based on previous move phase
  1440.                         --self.eventStep = 0
  1441.                         self.velocity = (self.y - self.prevY) / (time - self.markTime)
  1442.                         self.markTime = time
  1443.                         self.prevY = self.y
  1444.                    
  1445.                     elseif self.moveDirection == "horizontal" and not self.horizontalScrollDisabled then
  1446.                         -- HORIZONTAL movement
  1447.                        
  1448.                         self.delta = event.x - self.prevPositionX
  1449.                         self.prevPositionX = event.x
  1450.                        
  1451.                         -- do "elastic" effect when finger is dragging content past boundaries
  1452.                         if self.x > self.leftLimit or self.x < self.rightLimit then
  1453.                             self.x = self.x + self.delta/2
  1454.                         else
  1455.                             self.x = self.x + self.delta
  1456.                         end
  1457.                        
  1458.                         -- modify velocity based on previous move phase
  1459.                         --self.eventStep = 0
  1460.                         self.velocity = (self.x - self.prevX) / (time - self.markTime)
  1461.                         self.markTime = time
  1462.                         self.prevX = self.x
  1463.                     end
  1464.                 end
  1465.                
  1466.                 -- dispatch scroll event
  1467.                
  1468.                 -- # NOTE # -
  1469.                
  1470.                 --[[
  1471.                     If we can throttle this a bit the tableviews wouldn't appear to "jerk" when coming to a halt
  1472.                 --]]
  1473.                 if self.listener then
  1474.                     local event = event
  1475.                     event.name = "scrollEvent"
  1476.                     event.type = "contentTouch"
  1477.                     event.target = scrollView
  1478.                     self.listener( event )
  1479.                 end
  1480.                
  1481.            
  1482.             elseif phase == "ended" or phase == "cancelled" then
  1483.                
  1484.                 self.lastTime = time        -- necessary for calculating scroll movement
  1485.                 self.trackVelocity = nil    -- stop tracking velocity
  1486.                 self.markTime = nil
  1487.                
  1488.                 -- dispatch scroll event
  1489.                 if self.listener then
  1490.                     local event = event
  1491.                     event.name = "scrollEvent"
  1492.                     event.type = "contentTouch"
  1493.                     event.phase = "release"
  1494.                     event.target = scrollView
  1495.                     self.listener( event )
  1496.                 end
  1497.                                
  1498.                 -- remove focus from tableView's content
  1499.                 display.getCurrentStage():setFocus( nil )
  1500.                 self.isFocus = nil
  1501.             end
  1502.         end
  1503.    
  1504.         return true
  1505.     end
  1506.    
  1507.     local function onBackgroundTouch( self, event )
  1508.    
  1509.         -- This function allows scrollView to be scrolled when only the background of the
  1510.         -- widget is being touched (rather than having to touch the content itself)
  1511.    
  1512.         if event.phase == "began" then
  1513.             local content = self.parent.content
  1514.             content:touch( event )  -- transfer touch event to content group's touch event
  1515.         end
  1516.     end
  1517.    
  1518.     local function getContentPosition( self )
  1519.         local content = self.content
  1520.         return content.x, content.y
  1521.     end
  1522.    
  1523.     local function takeFocus( self, event )
  1524.         local target = event.target.view or event.target
  1525.        
  1526.         -- if button, restore back to "default" state
  1527.         if target.default and target.over then
  1528.             target.default.isVisible = true
  1529.             target.over.isVisible = false
  1530.             local r, g, b, a = target.label.color.default[1] or 0, target.label.color.default[2] or target.label.color.default[1], target.label.color.default[3] or target.label.color.default[1], target.label.color.default[4] or 255
  1531.             target.label:setTextColor( r, g, b, a )
  1532.         end
  1533.    
  1534.         -- remove focus from target
  1535.         display.getCurrentStage():setFocus( nil )
  1536.         target.isFocus = false
  1537.        
  1538.         -- set event.target to scrollView and start back at "began" phase
  1539.         event.target = self
  1540.         event.phase = "began"
  1541.         self.content.touch( self.content, event )
  1542.     end
  1543.    
  1544.     local function scrollToX( self, x, timeInMs, onComplete )   -- PRIVATE; self == scrollView
  1545.         local content = self.content
  1546.         if not self then print( "WARNING: The correct way to call scrollToX is with a ':' not a '.'" ); return; end
  1547.         if not x then return; end
  1548.         local time = timeInMs or 500
  1549.        
  1550.         if content.tween then transition.cancel( content.tween ); end
  1551.         content.tween = transition.to( content, { x=x, time=time, transition=easing.inOutQuad, onComplete=onComplete } )
  1552.     end
  1553.    
  1554.     local function scrollToY( self, y, timeInMs, onComplete )   -- PRIVATE; self == scrollView
  1555.         local content = self.content
  1556.         if not self then print( "WARNING: The correct way to call scrollToY is with a ':' not a '.'" ); return; end
  1557.         if not y then return; end
  1558.         local time = timeInMs or 500
  1559.        
  1560.         if content.tween then transition.cancel( content.tween ); end
  1561.         content.tween = transition.to( content, { y=y, time=time, transition=easing.inOutQuad, onComplete=onComplete } )
  1562.     end
  1563.    
  1564.     local function scrollToPosition( ... )
  1565.         local self, x, y, timeInMs, onComplete  -- self == scrollView
  1566.        
  1567.         if arg[1] and type(arg[1]) == "table" then
  1568.             self = arg[1]
  1569.         end
  1570.        
  1571.         self = arg[1]
  1572.         x = arg[2]
  1573.         y = arg[3]
  1574.        
  1575.         if arg[4] and type(arg[4]) == "number" then
  1576.             timeInMs = arg[4]
  1577.         elseif arg[4] and type(arg[4]) == "function" then
  1578.             onComplete = arg[4]
  1579.         end
  1580.        
  1581.         if arg[5] and type(arg[5]) == "function" then
  1582.             onComplete = arg[5]
  1583.         end
  1584.    
  1585.         if not self then print( "WARNING: The correct way to call scrollToPosition is with a ':' not a '.'" ); return; end
  1586.         if not x and not y then return; end
  1587.         if x and not y then
  1588.             scrollToX( self, x, timeInMs, onComplete )
  1589.         end
  1590.        
  1591.         if y and not x then
  1592.             scrollToY( self, y, timeInMs, onComplete )
  1593.         end
  1594.        
  1595.         if x and y then
  1596.             local content = self.content
  1597.             timeInMs = timeInMs or 500
  1598.             if content.tween then transition.cancel( content.tween ); end
  1599.             content.tween = transition.to( content, { x=x, y=y, time=timeInMs, transition=easing.inOutQuad, onComplete=onComplete } )
  1600.         end
  1601.     end
  1602.    
  1603.     local function scrollToTop( ... )
  1604.         local self, timeInMs, onComplete
  1605.        
  1606.         self = arg[1]
  1607.         if arg[2] and type(arg[2]) == "number" then
  1608.             timeInMs = arg[2]
  1609.         elseif arg[2] and type(arg[2]) == "function" then
  1610.             onComplete = arg[2]
  1611.         end
  1612.        
  1613.         if arg[3] and type(arg[3]) == "function" then
  1614.             onComplete = arg[3]
  1615.         end
  1616.        
  1617.         local content = self.content
  1618.         timeInMs = timeInMs or 500
  1619.        
  1620.        
  1621.         if content.tween then transition.cancel( content.tween ); end
  1622.         content.tween = transition.to( content, { y=0, time=timeInMs, transition=easing.inOutQuad, onComplete=onComplete } )
  1623.     end
  1624.    
  1625.     local function scrollToBottom( ... )
  1626.         local self, timeInMs, onComplete
  1627.        
  1628.         self = arg[1]
  1629.         if arg[2] and type(arg[2]) == "number" then
  1630.             timeInMs = arg[2]
  1631.         elseif arg[2] and type(arg[2]) == "function" then
  1632.             onComplete = arg[2]
  1633.         end
  1634.        
  1635.         if arg[3] and type(arg[3]) == "function" then
  1636.             onComplete = arg[3]
  1637.         end
  1638.        
  1639.         local content = self.content
  1640.         timeInMs = timeInMs or 500
  1641.         local lowerLimit = content.maskHeight - content.contentHeight
  1642.        
  1643.         if content.tween then transition.cancel( content.tween ); end
  1644.         content.tween = transition.to( content, { y=lowerLimit, time=timeInMs, transition=easing.inOutQuad, onComplete=onComplete } )
  1645.     end
  1646.    
  1647.     local function scrollToLeft( ... )
  1648.         local self, timeInMs, onComplete
  1649.        
  1650.         self = arg[1]
  1651.         if arg[2] and type(arg[2]) == "number" then
  1652.             timeInMs = arg[2]
  1653.         elseif arg[2] and type(arg[2]) == "function" then
  1654.             onComplete = arg[2]
  1655.         end
  1656.        
  1657.         if arg[3] and type(arg[3]) == "function" then
  1658.             onComplete = arg[3]
  1659.         end
  1660.        
  1661.         local content = self.content
  1662.         timeInMs = timeInMs or 500
  1663.        
  1664.         if content.tween then transition.cancel( content.tween ); end
  1665.         content.tween = transition.to( content, { x=0, time=timeInMs, transition=easing.inOutQuad, onComplete=onComplete } )
  1666.     end
  1667.    
  1668.     local function scrollToRight( ... )
  1669.         local self, timeInMs, onComplete
  1670.        
  1671.         self = arg[1]
  1672.         if arg[2] and type(arg[2]) == "number" then
  1673.             timeInMs = arg[2]
  1674.         elseif arg[2] and type(arg[2]) == "function" then
  1675.             onComplete = arg[2]
  1676.         end
  1677.        
  1678.         if arg[3] and type(arg[3]) == "function" then
  1679.             onComplete = arg[3]
  1680.         end
  1681.        
  1682.         local content = self.content
  1683.         timeInMs = timeInMs or 500
  1684.         local rightLimit = content.maskWidth - content.contentWidth
  1685.        
  1686.         if content.tween then transition.cancel( content.tween ); end
  1687.         content.tween = transition.to( content, { x=rightLimit, time=timeInMs, transition=easing.inOutQuad, onComplete=onComplete } )
  1688.     end
  1689.    
  1690.     local function removeSelf( self )
  1691.         -- check to see if there is a clean method; if so, call it
  1692.         if self.clean then self:clean(); end
  1693.        
  1694.         -- remove scrollView content
  1695.         if self.content then
  1696.             -- cancel any active transitions
  1697.             if self.content.tween then transition.cancel( self.content.tween ); self.content.tween = nil; end
  1698.             if self.sb_tween then transition.cancel( self.sb_tween ); self.sb_tween = nil; end
  1699.             if self.sb_timer then timer.cancel( self.sb_timer ); self.sb_timer = nil; end
  1700.            
  1701.             -- remove runtime listener
  1702.             Runtime:removeEventListener( "enterFrame", self.content )
  1703.             Runtime:removeEventListener( "enterFrame", self.content.scrollbar_listener )
  1704.            
  1705.             -- remove all children from content group
  1706.             for i=self.content.numChildren,1,-1 do
  1707.                 display.remove( self.content[i] )
  1708.             end
  1709.            
  1710.             display.remove( self.content )
  1711.             self.content = nil
  1712.         end
  1713.        
  1714.         -- removed fixed (non scrollable) content
  1715.         if self.fixed then
  1716.             -- remove all children from fixed group
  1717.             for i=self.fixed.numChildren,1,-1 do
  1718.                 display.remove( self.fixed[i] )
  1719.             end
  1720.            
  1721.             display.remove( self.fixed )
  1722.             self.fixed = nil
  1723.         end
  1724.        
  1725.         -- remove all children from virtual group
  1726.         if self.virtual then
  1727.             for i=self.virtual.numChildren,1,-1 do
  1728.                 display.remove( self.virtual[i] )
  1729.             end
  1730.            
  1731.             display.remove( self.virtual )
  1732.             self.virtual = nil
  1733.         end
  1734.        
  1735.         -- remove bitmap mask
  1736.         if self.mask then
  1737.             self:setMask( nil )
  1738.             self.mask = nil
  1739.         end
  1740.        
  1741.         -- call original removeSelf method
  1742.         self:cached_removeSelf()
  1743.     end
  1744.  
  1745.     local function createScrollBar( parent, manual_height, options )
  1746.         -- set initial variables
  1747.         local fixed_group = parent.fixed
  1748.         local scrollbar_width = 6
  1749.         local top = 6
  1750.         local min_height = 24
  1751.         local max_height = parent.widgetHeight-(top*2)
  1752.  
  1753.         -- calculate scrollbar height (based on total content height)
  1754.         local sb_height
  1755.         local content_height = manual_height or parent.content.contentHeight
  1756.         local content_bleed = content_height - parent.widgetHeight
  1757.  
  1758.         if content_bleed > parent.widgetHeight then
  1759.             sb_height = min_height
  1760.  
  1761.         elseif content_bleed > 0 then
  1762.  
  1763.             local bleed_percent = content_bleed/parent.widgetHeight
  1764.             sb_height = max_height-(max_height*bleed_percent)
  1765.  
  1766.         else
  1767.             display.remove( parent._scrollbar ); parent._scrollbar = nil
  1768.             return
  1769.         end
  1770.  
  1771.         -- calculate proper location of scrollbar (in case a start_percent wasn't provided)
  1772.         local amount_above_top = content_height-(content_height+parent.content.y)
  1773.         local calculated_percent = (amount_above_top/content_bleed)
  1774.  
  1775.         -- calculate scrollbar height and handle "squish" effect when content goes past boundaries
  1776.         local min_y = top
  1777.         local max_y = (top+max_height)-sb_height
  1778.         local scroll_range = max_y-min_y
  1779.         local scroll_percent = calculated_percent
  1780.         local x = parent.widgetWidth-3
  1781.         local y = top+(scroll_range*scroll_percent)
  1782.         if y < min_y then
  1783.             local difference = min_y - y
  1784.             sb_height = sb_height - difference
  1785.             if sb_height < min_height then sb_height = min_height; end
  1786.  
  1787.             -- don't allow scrollbar to go past minimum y position (even when content goes past boundary)
  1788.             y = min_y
  1789.  
  1790.         elseif y > max_y then
  1791.            
  1792.             local difference = y - max_y
  1793.             sb_height = sb_height - difference
  1794.             if sb_height < min_height then sb_height = min_height; end
  1795.  
  1796.             -- adjust y position since we adjusted scrollbar height
  1797.             y = (top+max_height)-sb_height
  1798.         end
  1799.  
  1800.         -- create the actual scrollbar from a rounded rectangle
  1801.         local sb = parent._scrollbar
  1802.         if not sb then
  1803.             sb = display.newRoundedRect( fixed_group, 0, 0, scrollbar_width, sb_height, 2 )        
  1804.         else
  1805.             sb.height = sb_height
  1806.         end
  1807.         sb:setReferencePoint( display.TopRightReferencePoint )
  1808.        
  1809.         if options and options.scrollBarColor and type( options.scrollBarColor ) == "table" then
  1810.             sb:setFillColor( unpack( options.scrollBarColor ) )
  1811.         else   
  1812.             sb:setFillColor( 0, 128 )
  1813.         end
  1814.        
  1815.         sb.x, sb.y = x, y
  1816.         sb.alpha = 1.0
  1817.  
  1818.         return sb
  1819.     end
  1820.    
  1821.     local function createScrollView( options )
  1822.         -- extract parameters or use defaults
  1823.         local   options = options or {}
  1824.         local   id = options.id or "widget_scrollView"
  1825.         local   left = options.left or 0
  1826.         local   top = options.top or 0
  1827.         local   width = options.width or (display.contentWidth-left)
  1828.         local   height = options.height or (display.contentHeight-top)
  1829.         local   scrollWidth = options.scrollWidth or width
  1830.         local   scrollHeight = options.scrollHeight or height
  1831.         local   friction = options.friction or scrollFriction
  1832.         local   listener = options.listener
  1833.         local   bgColor = options.bgColor or {}
  1834.                 bgColor[1] = bgColor[1] or 255
  1835.                 bgColor[2] = bgColor[2] or bgColor[1]
  1836.                 bgColor[3] = bgColor[3] or bgColor[1]
  1837.                 bgColor[4] = bgColor[4] or 255
  1838.         local   maskFile = options.maskFile
  1839.         local   hideBackground = options.hideBackground
  1840.         local   isVirtualized = options.isVirtualized
  1841.         local   topPadding = options.topPadding
  1842.         local   bottomPadding = options.bottomPadding
  1843.         local   baseDir = options.baseDir or system.ResourceDirectory
  1844.        
  1845.         --Picker (used in the pickers soft landing function)
  1846.         local   isPicker = options.isPicker or nil
  1847.         local   pickerTop = options.pickerTop or nil
  1848.        
  1849.         -- create display groups
  1850.         local scrollView = display.newGroup()   -- display group for widget; will be masked
  1851.         local content = display.newGroup()      -- will contain scrolling content
  1852.         local virtual = display.newGroup()      -- will contain "virtual" content (such as tableView rows)
  1853.         local fixed = display.newGroup()        -- will contain 'fixed' content (that doesn't scroll)
  1854.         scrollView:insert( content )
  1855.         scrollView:insert( virtual )
  1856.         scrollView:insert( fixed )
  1857.        
  1858.         -- important references
  1859.         scrollView.content = content
  1860.         scrollView.virtual = virtual
  1861.         scrollView.fixed = fixed
  1862.        
  1863.         -- set some scrollView properties (private properties attached to content group)
  1864.         scrollView._isWidget = true
  1865.         --Exposed variables for use with picker softlanding function
  1866.         scrollView._isPicker = isPicker
  1867.         scrollView.pickerTop = pickerTop
  1868.         ----------------------------------
  1869.         scrollView.id = id
  1870.         scrollView.widgetWidth = width
  1871.         scrollView.widgetHeight = height
  1872.         scrollView.isVirtualized = isVirtualized
  1873.         scrollView.topPadding = topPadding
  1874.         scrollView.bottomPadding = bottomPadding
  1875.         content.hideScrollBar = options.hideScrollBar or false
  1876.         --Exposed for use with picker softlanding function
  1877.         content.selectionHeight = options.selectionHeight or nil
  1878.         content.maskWidth = width
  1879.         content.maskHeight = height
  1880.         content.friction = friction
  1881.         content.enterFrame = onScrollViewUpdate -- enterFrame listener function
  1882.         content.touch = onContentTouch; content:addEventListener( "touch", content )
  1883.         content.listener = listener
  1884.        
  1885.         -- scrollView methods
  1886.         scrollView.getContentPosition = getContentPosition
  1887.         scrollView.takeFocus = takeFocus
  1888.         scrollView.scrollToPosition = scrollToPosition
  1889.         scrollView.scrollToTop = scrollToTop
  1890.         scrollView.scrollToBottom = scrollToBottom
  1891.         scrollView.scrollToLeft = scrollToLeft
  1892.         scrollView.scrollToRight = scrollToRight
  1893.        
  1894.         -- create background rectangle for widget
  1895.         local bgRect = display.newRect( 0, 0, width, height )
  1896.         bgRect:setFillColor( bgColor[1], bgColor[2], bgColor[3], bgColor[4] )
  1897.         if hideBackground then bgRect.isVisible = false; end
  1898.         bgRect.isHitTestable = true
  1899.         bgRect.touch = onBackgroundTouch; bgRect:addEventListener( "touch", bgRect )
  1900.         scrollView:insert( 1, bgRect )
  1901.        
  1902.         -- create a background for actual content
  1903.         local contentBg = display.newRect( 0, 0, scrollWidth, scrollHeight )
  1904.         contentBg:setFillColor( 255, 100 )
  1905.         contentBg.isVisible = false
  1906.         content:insert( 1, contentBg )
  1907.        
  1908.         -- apply mask (if user set option)
  1909.         if maskFile then
  1910.             scrollView.mask = graphics.newMask( maskFile, baseDir )
  1911.             scrollView:setMask( scrollView.mask )
  1912.    
  1913.             scrollView.maskX = width * 0.5
  1914.             scrollView.maskY = height * 0.5
  1915.             scrollView.isHitTestMasked = false
  1916.         end
  1917.    
  1918.         -- position widget based on left/top options
  1919.         scrollView:setReferencePoint( display.TopLeftReferencePoint )
  1920.         scrollView.x, scrollView.y = left, top
  1921.    
  1922.         -- override removeSelf method for scrollView (to ensure widget is properly removed)
  1923.         scrollView.cached_removeSelf = scrollView.removeSelf
  1924.         scrollView.removeSelf = removeSelf
  1925.  
  1926.         function scrollView.content.scrollbar_listener( event )
  1927.             scrollView:update_scrollbar()
  1928.         end
  1929.        
  1930.         -- override insert method for scrollView to insert into content instead
  1931.         scrollView.cached_insert = scrollView.insert
  1932.         function scrollView:insert( arg1, arg2 )
  1933.             local index, obj
  1934.            
  1935.             if arg1 and type(arg1) == "number" then
  1936.                 index = arg1
  1937.             elseif arg1 and type(arg1) == "table" then
  1938.                 obj = arg1
  1939.             end
  1940.            
  1941.             if arg2 and type(arg2) == "table" then
  1942.                 obj = arg2
  1943.             end
  1944.            
  1945.             if index then
  1946.                 self.content:insert( index, obj )
  1947.             else
  1948.                 self.content:insert( obj )
  1949.             end
  1950.         end
  1951.  
  1952.         -- cancels scrollbar fadeout effect
  1953.         function scrollView:cancel_scrollbar_hide()
  1954.             if self.sb_tween then transition.cancel( self.sb_tween ); self.sb_tween = nil; end
  1955.             if self.sb_timer then timer.cancel( self.sb_timer ); self.sb_timer = nil; end
  1956.         end
  1957.  
  1958.         -- function to update scrollbar height and position
  1959.         function scrollView:update_scrollbar()
  1960.             local content_height = self.virtualContentHeight
  1961.             self._scrollbar = createScrollBar( self, content_height, options )
  1962.         end
  1963.  
  1964.         function scrollView:hide_scrollbar()
  1965.             Runtime:removeEventListener( "enterFrame", self.content.scrollbar_listener )
  1966.             self:cancel_scrollbar_hide()
  1967.  
  1968.             local function fade_out()
  1969.                 local function remove_scrollbar()
  1970.                     display.remove( self._scrollbar )
  1971.                     self._scrollbar = nil
  1972.                     self.sb_tween = nil
  1973.                 end
  1974.                 if self.sb_tween then
  1975.                     transition.cancel( self.sb_tween ); self.sb_tween = nil;
  1976.                     self._scrollbar.alpha = 1.0
  1977.                 end
  1978.                 self.sb_tween = transition.to( self._scrollbar, { time=300, alpha=0, onComplete=remove_scrollbar } )
  1979.             end
  1980.             self.sb_timer = timer.performWithDelay( 300, fade_out, 1 )
  1981.         end
  1982.        
  1983.         return scrollView   -- returns a display group
  1984.     end
  1985.    
  1986.     return createScrollView( options )
  1987. end
  1988.  
  1989. -----------------------------------------------------------------------------------------
  1990. -----------------------------------------------------------------------------------------
  1991. --
  1992. -- tabBar widget
  1993. --
  1994. -----------------------------------------------------------------------------------------
  1995. -----------------------------------------------------------------------------------------
  1996.  
  1997. function widget.newTabBar( options )
  1998.    
  1999.     local function invokeTabButtonSelectionState( button )
  2000.         -- ensure overlay and down graphic are showing
  2001.         button.up.isVisible = false
  2002.         button.down.isVisible = true
  2003.         button.selected = true
  2004.         if button.label then button.label:setTextColor( 255 ); end
  2005.        
  2006.         -- if hideOverlay is not set to true, show overlay graphic (to represent selection)
  2007.         if not button.hideOverlay then
  2008.             button.overlay.isVisible = true
  2009.         end
  2010.     end
  2011.    
  2012.     local function onButtonSelection( self )    -- self == tab button
  2013.         local tabBar = self.parent.parent
  2014.        
  2015.         if tabBar and tabBar.deselectAll then
  2016.             tabBar:deselectAll()    -- deselect all tab buttons
  2017.         end
  2018.  
  2019.         invokeTabButtonSelectionState( self )
  2020.  
  2021.         -- call listener function
  2022.         if self.onPress and type(self.onPress) == "function" then
  2023.             local event = {
  2024.                 name = "tabButtonPress",
  2025.                 target = self,
  2026.             }
  2027.             self.onPress( event )
  2028.         end
  2029.     end
  2030.    
  2031.     local function onButtonTouch( self, event )     -- self == tab button
  2032.         if event.phase == "began" then
  2033.             self:onSelection()  -- see: onButtonSelection()
  2034.         end
  2035.         return true
  2036.     end
  2037.    
  2038.     local function createTabButton( params )
  2039.         local   params = params or {}
  2040.         local   id = params.id
  2041.         local   label = params.label
  2042.         local   labelFont = params.font or native.systemFontBold
  2043.         local   labelFontSize = params.size or 10
  2044.         local   labelColor = params.labelColor or { 124, 124, 124, 255 }
  2045.         local   overlayWidth = params.overlayWidth
  2046.         local   overlayHeight = params.overlayHeight
  2047.         local   disableOverlay = params.disableOverlay
  2048.         local   width = params.width or 32      -- corresponds to up/down image width
  2049.         local   height = params.height or 32    -- corresponds to up/down image height
  2050.         local   cornerRadius = params.cornerRadius or 4
  2051.         local   default = params.default or params.up or params.image   -- params.default is supported; others old/deprecated
  2052.         local   down = params.down or params.over -- params.down is supported; others old/deprecated
  2053.         local   parentObject = params.parent
  2054.         local   selected = params.selected
  2055.         local   onPress = params.onPress
  2056.         local   upGradient = params.upGradient
  2057.         local   downGradient = params.downGradient
  2058.         local   baseDir = params.baseDir or system.ResourceDirectory
  2059.  
  2060.         local button = display.newGroup()
  2061.         button.id = id
  2062.         button.hideOverlay = disableOverlay
  2063.        
  2064.         -- create overlay (which is the highlight when button is selected/down)
  2065.         button.overlay = display.newRoundedRect( button, 0, 0, overlayWidth, overlayHeight, cornerRadius )
  2066.         button.overlay:setFillColor( 255, 25 )
  2067.         button.overlay:setStrokeColor( 0, 75 )
  2068.         button.overlay.strokeWidth = 1
  2069.         button.overlay.isVisible = false
  2070.         button.overlay.isHitTestable = true
  2071.  
  2072.         button.up = display.newImageRect( button, default, baseDir, width, height )
  2073.         button.up:setReferencePoint( display.CenterReferencePoint )
  2074.         button.up.x = button.overlay.width * 0.5
  2075.         button.up.y = button.overlay.height * 0.5
  2076.  
  2077.         if default and not down then down = default; end
  2078.         button.down = display.newImageRect( button, down, baseDir, width, height )
  2079.         button.down:setReferencePoint( display.CenterReferencePoint )
  2080.         button.down.x = button.up.x
  2081.         button.down.y = button.up.y
  2082.         button.down.isVisible = false
  2083.  
  2084.         if label then   -- label is optional
  2085.             -- shift icon up
  2086.             button.up.y = button.up.y - (labelFontSize-3)
  2087.             button.down.y = button.down.y - (labelFontSize-3)
  2088.  
  2089.             -- create label
  2090.             button.label = display.newText( label, 0, 0, labelFont, labelFontSize )
  2091.             local color = { labelColor[1] or 124, labelColor[2] or 124, labelColor[3] or 124, labelColor[4] or 255 }
  2092.             button.label:setTextColor( color[1], color[2], color[3], color[4] )
  2093.             button.label.color = color
  2094.             button.label:setReferencePoint( display.TopCenterReferencePoint )
  2095.             button.label.x = button.up.x
  2096.             button.label.y = button.up.y + (button.up.contentHeight*0.5)    -- button.up's reference point is center
  2097.             button:insert( button.label )
  2098.         end
  2099.  
  2100.         -- if selected, show overlay and 'down' graphic
  2101.         if selected then
  2102.             invokeTabButtonSelectionState( button )
  2103.         end
  2104.  
  2105.         -- touch event
  2106.         button.touch = onButtonTouch
  2107.         button:addEventListener( "touch", button )
  2108.  
  2109.         -- assign onPress event (user-specified listener function)
  2110.         button.onPress = onPress
  2111.        
  2112.         -- selection method to represent button visually and call listener
  2113.         button.onSelection = onButtonSelection
  2114.  
  2115.         return button
  2116.     end
  2117.    
  2118.     local function deselectAllButtons( self )   -- self == tabBar
  2119.         for i=1,self.buttons.numChildren do
  2120.             local button = self.buttons[i]
  2121.  
  2122.             button.overlay.isVisible = false
  2123.             button.down.isVisible = false
  2124.             button.up.isVisible = true
  2125.             button.selected = false
  2126.             if button.label then button.label:setTextColor( button.label.color[1], button.label.color[2], button.label.color[3], button.label.color[4] ); end
  2127.         end
  2128.     end
  2129.    
  2130.     local function pressButton( self, buttonIndex, invokeListener )     -- self == tabBar
  2131.         self:deselectAll()
  2132.         if invokeListener == nil then invokeListener = true; end
  2133.        
  2134.         local button = self.buttons[buttonIndex]
  2135.         if button then
  2136.             invokeTabButtonSelectionState( button )
  2137.            
  2138.             -- call listener function
  2139.             if invokeListener then
  2140.                 if button.onPress and type(button.onPress) == "function" then
  2141.                     local event = {
  2142.                         name = "tabButtonPress",
  2143.                         target = button
  2144.                     }
  2145.                     button.onPress( event )
  2146.                 end
  2147.             end
  2148.         else
  2149.             print( "WARNING: Specified tab button '" .. buttonIndex .. "' does not exist." )
  2150.         end
  2151.     end
  2152.    
  2153.     local function removeSelf( self )   -- self == tabBar
  2154.         -- check to see if there is a clean method; if so, call it
  2155.         if self.clean then self:clean(); end
  2156.        
  2157.         -- remove all buttons
  2158.         for i=self.buttons.numChildren,1,-1 do
  2159.             display.remove( self.buttons[i] )
  2160.         end
  2161.         display.remove( self.buttons )
  2162.        
  2163.         -- remove gradient (if in use)
  2164.         if self.gradientRect then
  2165.             self.gradientRect:setFillColor( 0 ) -- so it's no longer using gradient image
  2166.             if self.gradient then
  2167.                 self.gradient = nil
  2168.             end
  2169.         end
  2170.        
  2171.         -- remove tab bar widget itself
  2172.         self:cached_removeSelf()
  2173.     end
  2174.    
  2175.     local function createTabBar( options, theme )
  2176.         local options = options or {}
  2177.         local theme = theme or {}
  2178.         local id = options.id or "widget_tabBar"
  2179.         local buttons = options.buttons
  2180.         local maxTabWidth = options.maxTabWidth or 120
  2181.         local width = options.width or theme.width or display.contentWidth
  2182.         local height = options.height or theme.height or 50
  2183.         local background = options.background or theme.background
  2184.         local gradient = options.gradient or options.topGradient or theme.gradient  -- gradient must be pre-created using graphics.newGradient
  2185.         local topFill = options.topFill or theme.topFill
  2186.         local bottomFill = options.bottomFill or theme.bottomFill
  2187.         local left = options.left or 0
  2188.         local top = options.top or 0
  2189.         local baseDir = options.baseDir or system.ResourceDirectory
  2190.        
  2191.         local tabBar = display.newGroup()
  2192.         local barBg = display.newGroup(); tabBar:insert( barBg )
  2193.        
  2194.         local halfW = width * 0.5
  2195.         local halfH = height * 0.5
  2196.        
  2197.         -- create tab bar background
  2198.         if topFill and bottomFill then
  2199.             -- background made from two equal halves (2 different fills)
  2200.            
  2201.             topFill[1] = topFill[1] or 0
  2202.             topFill[2] = topFill[2] or topFill[1]
  2203.             topFill[3] = topFill[3] or topFill[1]
  2204.             topFill[4] = topFill[4] or 255
  2205.            
  2206.             bottomFill[1] = bottomFill[1] or 0
  2207.             bottomFill[2] = bottomFill[2] or bottomFill[1]
  2208.             bottomFill[3] = bottomFill[3] or bottomFill[1]
  2209.             bottomFill[4] = bottomFill[4] or 255
  2210.            
  2211.             local topRect = display.newRect( barBg, 0, 0, width, halfH )
  2212.             topRect:setFillColor( topFill[1], topFill[2], topFill[3], topFill[4] )
  2213.            
  2214.             local bottomRect = display.newRect( barBg, 0, halfH, width, halfH )
  2215.             bottomRect:setFillColor( bottomFill[1], bottomFill[2], bottomFill[3], bottomFill[4] )
  2216.        
  2217.         elseif gradient and type(gradient) == "userdata" then
  2218.             -- background made from rectangle w/ gradient fill
  2219.            
  2220.             local bg = display.newRect( barBg, 0, 0, width, height )
  2221.             bg:setFillColor( gradient )
  2222.            
  2223.             if bottomFill then
  2224.                 bottomFill[1] = bottomFill[1] or 0
  2225.                 bottomFill[2] = bottomFill[2] or bottomFill[1]
  2226.                 bottomFill[3] = bottomFill[3] or bottomFill[1]
  2227.                 bottomFill[4] = bottomFill[4] or 255
  2228.                
  2229.                 local bottomRect = display.newRect( barBg, 0, halfH, width, halfH )
  2230.                 bottomRect:setFillColor( bottomFill[1], bottomFill[2], bottomFill[3], bottomFill[4] )
  2231.             end
  2232.            
  2233.         elseif background and type(background) == "string" then
  2234.             -- background made from user-provided image file
  2235.            
  2236.             local bg = display.newImageRect( barBg, background, baseDir, width, height )
  2237.             bg:setReferencePoint( display.TopLeftReferencePoint )
  2238.             bg.x, bg.y = 0, 0
  2239.         else
  2240.             -- no background or fills specified (default)
  2241.            
  2242.             -- create a gradient background
  2243.             tabBar.gradient = graphics.newGradient( { 39 }, { 0 }, "down" )
  2244.             tabBar.gradientRect = display.newRect( barBg, 0, 0, width, height )
  2245.             tabBar.gradientRect:setFillColor( tabBar.gradient )
  2246.            
  2247.             -- create solid black rect for bottom half of background
  2248.             local bottomRect = display.newRect( barBg, 0, halfH, width, halfH )
  2249.             bottomRect:setFillColor( 0, 0, 0, 255 )
  2250.         end
  2251.        
  2252.         -- background created; create tab buttons
  2253.        
  2254.         tabBar.buttons = display.newGroup()
  2255.         tabBar:insert( tabBar.buttons )
  2256.        
  2257.         if buttons and type(buttons) == "table" then
  2258.             local buttonWidth = mFloor((width/#buttons)-4)
  2259.             local buttonHeight = height-4
  2260.             local buttonPadding = mFloor(width/buttonWidth)*2
  2261.            
  2262.             -- ensure button width doesn't exceed maxButtonWidth
  2263.             if buttonWidth > maxTabWidth then buttonWidth = maxTabWidth; end
  2264.            
  2265.             -- construct each button and insert into tabBar.buttons group
  2266.             for i=1,#buttons do
  2267.                 buttons[i].id = buttons[i].id or i
  2268.                 buttons[i].overlayWidth = buttonWidth
  2269.                 buttons[i].overlayHeight = buttonHeight
  2270.                 buttons[i].parent = tabBar
  2271.  
  2272.                 local tab = createTabButton( buttons[i] )
  2273.                 tab:setReferencePoint( display.TopLeftReferencePoint )
  2274.                 tab.x = (buttonWidth*i-buttonWidth) + buttonPadding
  2275.                 tab.y = 0
  2276.                 tabBar.buttons:insert( tab )
  2277.             end
  2278.            
  2279.             -- center the 'tabBar.buttons' group on the widget
  2280.             tabBar.buttons:setReferencePoint( display.CenterReferencePoint )
  2281.             tabBar.buttons.x, tabBar.buttons.y = halfW, halfH
  2282.         end
  2283.        
  2284.         -- position the widget
  2285.         tabBar:setReferencePoint( display.TopLeftReferencePoint )
  2286.         tabBar.x, tabBar.y = left, top
  2287.         tabBar:setReferencePoint( display.CenterReferencePoint )
  2288.        
  2289.         -- prevent touches from going through tabBar background
  2290.         local preventTouches = function( self, event ) return true; end
  2291.         barBg.touch = preventTouches
  2292.         barBg:addEventListener( "touch", barBg )
  2293.        
  2294.         -- override removeSelf method to ensure widget is properly freed from memory
  2295.         tabBar.cached_removeSelf = tabBar.removeSelf
  2296.         tabBar.removeSelf = removeSelf
  2297.        
  2298.         -- additional tabBar methods and properties
  2299.         tabBar._isWidget = true
  2300.         tabBar.id = id
  2301.         tabBar.deselectAll = deselectAllButtons
  2302.         tabBar.makeTabActive = pressButton
  2303.         tabBar.pressButton = pressButton    -- to remain compatible with previous version
  2304.        
  2305.         return tabBar
  2306.     end
  2307.    
  2308.     -- this widget supports visual customization via themes
  2309.     local themeOptions
  2310.     if widget.theme then
  2311.         local tabBarTheme = widget.theme.tabBar
  2312.        
  2313.         if tabBarTheme then
  2314.             if options and options.style then   -- style parameter optionally set by user
  2315.                
  2316.                 -- for themes that support various "styles" per widget
  2317.                 local style = tabBarTheme[options.style]
  2318.                
  2319.                 if style then themeOptions = style; end
  2320.             else
  2321.                 -- if no style parameter set, use default style specified by theme
  2322.                 themeOptions = tabBarTheme
  2323.             end
  2324.         end
  2325.     end
  2326.    
  2327.     return createTabBar( options, themeOptions )
  2328. end
  2329.  
  2330. -----------------------------------------------------------------------------------------
  2331. -----------------------------------------------------------------------------------------
  2332. --
  2333. -- tableView widget (based on scrollView widget)
  2334. --
  2335. -----------------------------------------------------------------------------------------
  2336. -----------------------------------------------------------------------------------------
  2337.  
  2338. function widget.newTableView( options )
  2339.     -- creates group for row, as well as a background and bottom-line
  2340.     local function newRowGroup( rowData )
  2341.         local row = display.newGroup()
  2342.        
  2343.         -- create background
  2344.         local bg = display.newRect( row, 0, 0, rowData.width, rowData.height )
  2345.         bg:setFillColor( rowData.rowColor[1], rowData.rowColor[2], rowData.rowColor[3], rowData.rowColor[4] )
  2346.        
  2347.         -- create bottom-line
  2348.         local line
  2349.        
  2350.         --Only create the line if the user hasn't specified noLines == true in options table.
  2351.         if options and not options.noLines == true or options and not options.noLines or options and options.noLines == false then
  2352.             line = display.newLine( row, 0, rowData.height, rowData.width, rowData.height )
  2353.             line:setColor( rowData.lineColor[1], rowData.lineColor[2], rowData.lineColor[3], rowData.lineColor[4] )
  2354.             row.line = line
  2355.         end
  2356.        
  2357.         row.background = bg
  2358.        
  2359.         --If the user has specified noLines == true then set row.line to nil
  2360.         if type( row.line ) ~= "table" then
  2361.             row.line = nil
  2362.         end
  2363.        
  2364.         return row
  2365.     end
  2366.    
  2367.     -- render row based on index in tableView.content.rows table
  2368.     local function renderRow( self, row )   -- self == tableView
  2369.         local content = self.content
  2370.         if row.view then row.view:removeSelf(); end
  2371.         row.view = newRowGroup( row )
  2372.         self.virtual:insert( row.view )
  2373.        
  2374.         row.view.x = 0
  2375.         row.view.y = row.top
  2376.        
  2377.         if row.onRender then
  2378.             -- set up event table
  2379.             local e = {}
  2380.             e.name = "tableView_rowRender"
  2381.             e.type = "render"
  2382.             e.parent = self -- tableView that this row belongs to
  2383.             e.target = row
  2384.             e.row = row
  2385.             e.id = row.id
  2386.             e.view = row.view
  2387.             e.background = row.view.background
  2388.             e.line = row.view.line
  2389.             e.data = row.data
  2390.             e.phase = "render"      -- phases: render, press, release, swipeLeft, swipeRight
  2391.             e.index = row.index
  2392.            
  2393.             row.onRender( e )
  2394.         end
  2395.     end
  2396.    
  2397.     local function renderCategory( self, row )  -- self == tableView; row should be a table, not an index
  2398.         local content = self.content
  2399.        
  2400.         local newCategoryRender = function()
  2401.             if content.category then display.remove( content.category ); end
  2402.            
  2403.             content.category = newRowGroup( row )
  2404.             content.category.index = row.index
  2405.             content.category.x, content.category.y = 0, 0 ---row.height
  2406.            
  2407.             self.fixed:insert( content.category )   -- insert into tableView's 'fixed' group
  2408.            
  2409.             if row.onRender then
  2410.                 -- set up event table
  2411.                 local e = {}
  2412.                 e.name = "tableView_rowRender"
  2413.                 e.type = "render"
  2414.                 e.parent = self -- tableView that this row belongs to
  2415.                 e.target = row
  2416.                 e.row = row
  2417.                 e.id = row.id
  2418.                 e.view = content.category
  2419.                 e.background = content.category.background
  2420.                 e.line = content.category.line
  2421.                 e.data = row.data
  2422.                 e.phase = "render"      -- phases: render, press, release, swipeLeft, swipeRight
  2423.                 e.index = row.index
  2424.                
  2425.                 row.onRender( e )
  2426.             end
  2427.         end
  2428.        
  2429.         if not content.category then
  2430.             -- new; no category currently rendered
  2431.             newCategoryRender()
  2432.            
  2433.         else
  2434.             -- there is currently a category; render only if it's different
  2435.             if content.category.index ~= row.index then
  2436.                 newCategoryRender()
  2437.             end
  2438.         end
  2439.     end
  2440.    
  2441.     -- renders row if it does not have a 'view' property (display group)
  2442.     local function ensureRowIsRendered( self, row ) -- self == tableView
  2443.         if not row.view then
  2444.             renderRow( self, row )
  2445.         else
  2446.             row.view.y = row.top
  2447.         end
  2448.     end
  2449.    
  2450.     -- render rows within the render range ( -renderThresh <-- widget height --> renderThresh )
  2451.     local function renderVisibleRows( self ) -- self == tableView          
  2452.         local content = self.content
  2453.        
  2454.         -- if widget has been removed during scrolling, be sure to remove certain enterFrame listeners
  2455.         if not content or not content.y then
  2456.             if content then Runtime:removeEventListener( "enterFrame", content ); end
  2457.             Runtime:removeEventListener( "enterFrame", self.rowListener )
  2458.             return
  2459.         end
  2460.        
  2461.         local currentCategoryIndex
  2462.         local rows = content.rows
  2463.        
  2464.         -- ensure all rows that are marked .isRendered are rendered
  2465.         for i=1,#rows do
  2466.             local row = rows[i]
  2467.             -- update top/bottom locations
  2468.             row.top = content.y + row.topOffset
  2469.             row.bottom = row.top + row.height
  2470.            
  2471.             -- category "pushing" effect
  2472.             if content.category and row.isCategory and row.index ~= content.category.index then
  2473.                 if row.top < content.category.contentHeight-3 and row.top >= 0 then
  2474.                     -- push the category upward
  2475.                     content.category.y = row.top - (content.category.contentHeight-3)
  2476.                 end
  2477.             end
  2478.            
  2479.             -- determine which category should be rendered (sticky category at top)
  2480.             if row.isCategory and row.top <= 0 then
  2481.                 currentCategoryIndex = i
  2482.            
  2483.             elseif row.isCategory and row.top >= 0 and content.category and row.index == content.category.index then
  2484.                 -- category moved below top of tableView, render previous category
  2485.                
  2486.                 -- render the previous category, if this is not the first current category
  2487.                 if row.index ~= content.firstCategoryIndex then
  2488.                     currentCategoryIndex = content.categories["cat-" .. row.index]  -- stores reference to previous category index
  2489.                 else
  2490.                     -- remove current category if the first category moved below top of tableView
  2491.                     if content.category then
  2492.                         content.category:removeSelf()
  2493.                         content.category = nil
  2494.                         currentCategoryIndex = nil
  2495.                     end
  2496.                 end
  2497.             end
  2498.            
  2499.             -- check to see if row is within viewable area
  2500.             local belowTopThresh = row.bottom > -self.renderThresh
  2501.             local aboveBottomThresh = row.top < self.widgetHeight+self.renderThresh
  2502.             if belowTopThresh and aboveBottomThresh then
  2503.                 row.isRendered = true
  2504.                
  2505.                 -- ensures rows that are marked for rerendering and within render area get rendered
  2506.                 if row.reRender then
  2507.                     if row.view then row.view:removeSelf(); row.view = nil; end
  2508.                     row.reRender = nil;
  2509.                     row.isRendered = true
  2510.                 end
  2511.                
  2512.                 ensureRowIsRendered( self, row )
  2513.                
  2514.                 -- hide row's view if it happens to be outside of viewable area; show it if in viewable bounds
  2515.                 if row.bottom < 0 or row.top > self.widgetHeight then
  2516.                     if row.view.isVisible then row.view.isVisible = false; end
  2517.                 else
  2518.                     if not row.view.isVisible then row.view.isVisible = true; end
  2519.                 end
  2520.             else
  2521.                 row.isRendered = false
  2522.                 if row.view then row.view:removeSelf(); row.view = nil; end
  2523.             end
  2524.  
  2525.             -- hide row if it is the current category (category item will be rendered at top of widget)
  2526.             if row.index == currentCategoryIndex then
  2527.                 if row.view then row.view.isVisible = false; end
  2528.             end
  2529.         end
  2530.        
  2531.         -- render current category
  2532.         if currentCategoryIndex then
  2533.             renderCategory( self, rows[currentCategoryIndex] )
  2534.         end
  2535.     end
  2536.    
  2537.     -- find the next row insert location (y-coordinate)
  2538.     local function getNextRowTop( content )
  2539.         local rows = content.rows
  2540.         local top, nextIndex = content.y, #rows+1
  2541.        
  2542.         local final = rows[#rows]
  2543.         if final then
  2544.             -- return final row's top location + its height
  2545.             top = final.top + final.height
  2546.         end
  2547.        
  2548.         return top, nextIndex   -- returns next 'top' coordinate, & the next avail. index
  2549.     end
  2550.    
  2551.     -- iterate through all rows and update row data (such as content offset, total height, category info, etc)
  2552.     local function updateRowData( self )    -- self == tableView
  2553.         local content = self.content
  2554.         local rows = content.rows
  2555.         local top = content.y
  2556.         local currentY = top
  2557.         local firstCategory
  2558.         local previousCategory
  2559.         local totalHeight = 0
  2560.         content.categories = {}
  2561.        
  2562.         for i=1,#rows do
  2563.             rows[i].topOffset = currentY - top
  2564.            
  2565.             -- update index while we're at it
  2566.             rows[i].index = i
  2567.            
  2568.             -- popuplate content.categories table
  2569.             if rows[i].isCategory then
  2570.                 if not previousCategory then
  2571.                     content.categories["cat-" .. i] = "first"
  2572.                     previousCategory = i
  2573.                 else
  2574.                     content.categories["cat-" .. i] = previousCategory
  2575.                     previousCategory = i
  2576.                 end
  2577.                
  2578.                 if not firstCategory then
  2579.                     -- store reference to very first category index
  2580.                     firstCategory = i
  2581.                 end
  2582.             end
  2583.             local height = rows[i].height
  2584.             currentY = currentY + height + 1
  2585.             totalHeight = totalHeight + height + 1
  2586.         end
  2587.        
  2588.         -- make reference to first category
  2589.         content.firstCategoryIndex = firstCategory
  2590.        
  2591.         -- force re-calculation of top/bottom most rendered rows
  2592.         --self.topMostRendered = nil
  2593.         --self.bottomMostRendered = nil
  2594.        
  2595.         -- update total height of all rows
  2596.         self.virtualContentHeight = totalHeight
  2597.     end
  2598.    
  2599.     -- used to insert new rows into tableView.content.rows table
  2600.     local function insertRow( self, params )    -- self == tableView
  2601.         local row = {}
  2602.         row.id = params.id      -- custom id
  2603.         row.data = params.data  -- custom data
  2604.         row.width = self.content.maskWidth
  2605.         row.height = params.height or 56    -- default row height is 56
  2606.         row.isCategory = params.isCategory
  2607.         row.onEvent = params.listener or params.onEvent
  2608.         row.onRender = params.onRender
  2609.         local rowColor = params.rowColor or {}
  2610.             rowColor[1] = rowColor[1] or 255
  2611.             rowColor[2] = rowColor[2] or rowColor[1]
  2612.             rowColor[3] = rowColor[3] or rowColor[1]
  2613.             rowColor[4] = rowColor[4] or 255
  2614.         local lineColor = params.lineColor or {}
  2615.             lineColor[1] = lineColor[1] or 128
  2616.             lineColor[2] = lineColor[2] or lineColor[1]
  2617.             lineColor[3] = lineColor[3] or lineColor[1]
  2618.             lineColor[4] = lineColor[4] or 255
  2619.         row.rowColor = rowColor
  2620.         row.lineColor = lineColor
  2621.         row.top, row.index = getNextRowTop( self.content )
  2622.         row.isRendered = false  -- will ensure row gets rendered on next update
  2623.        
  2624.         -- increase renderThresh property of tableView if row height is larger
  2625.         -- renderThresh is the limit above and below widget where rendering occurs
  2626.         if row.height >= self.renderThresh then
  2627.             self.renderThresh = row.height + 10
  2628.         end
  2629.        
  2630.         -- insert as final item in tableView.content.rows table
  2631.         table.insert( self.content.rows, row )
  2632.         updateRowData( self )
  2633.         if not params.skipRender then renderVisibleRows( self ); end    -- refresh locations/rendering
  2634.     end
  2635.    
  2636.     -- finds rendered row at on-screen y-coordinate and returns the row object
  2637.     local function getRowAtCoordinate( self, yPosition )    -- self == tableView
  2638.         local top = self.-- y-coordinate of tableView, from top-left reference point
  2639.         local content = self.content
  2640.         --local firstRenderedRow = content.firstRenderedRow or 1
  2641.         local rows = content.rows
  2642.         local result
  2643.        
  2644.         for i=1,#rows do
  2645.             if rows[i].view then
  2646.                 local viewBounds = rows[i].view.contentBounds
  2647.                 local isWithinBounds = yPosition > viewBounds.yMin and yPosition < viewBounds.yMax
  2648.                
  2649.                 -- if yPosition is within the row's view group, return this row
  2650.                 if isWithinBounds then result = rows[i]; break; end
  2651.             end
  2652.         end
  2653.        
  2654.         return result
  2655.     end
  2656.    
  2657.     -- calls onEvent listener for row
  2658.     local function dispatchRowTouch( row, phase, parentWidget )
  2659.         if row.onEvent then
  2660.             -- set up event table
  2661.             local e = {}
  2662.             e.name = "tableView_rowTouch"
  2663.             e.type = "touch"
  2664.             e.parent = parentWidget -- tableView that this row belongs to
  2665.             e.target = row
  2666.             e.row = row
  2667.             e.id = row.id
  2668.             e.view = row.view
  2669.             e.background = row.view.background
  2670.             e.line = row.view.line
  2671.             e.data = row.data
  2672.             e.phase = phase     -- phases: render, press, tap, release, swipeLeft, swipeRight
  2673.             e.index = row.index
  2674.            
  2675.             row.onEvent( e )
  2676.         end
  2677.     end
  2678.    
  2679.     --Function to invoke the users tableView listener if specified
  2680.     local function invokeUserListener( self, event ) --Self == tableView
  2681.         if self.userListener and type( self.userListener ) == "function" then
  2682.             if event.type == "beganScroll" then
  2683.                 if self.hasScrolled == false then
  2684.                     self.userListener( event )
  2685.                     self.hasScrolled = true
  2686.                 end
  2687.             elseif event.type == "endedScroll" then
  2688.                 if self.hasScrolled == true then
  2689.                     self.userListener( event )
  2690.                    
  2691.                     self.hasScrolled = false
  2692.                 end
  2693.             else
  2694.                 self.userListener( event )
  2695.             end
  2696.         end
  2697.     end
  2698.    
  2699.    
  2700.     -- listens to scrollView events for tableView
  2701.     local function scrollListener( event )
  2702.         local tableView = event.target
  2703.         local content = tableView.content
  2704.         local eType = event.type
  2705.        
  2706.         local moveRowsWithTween = function( self )  -- self == tableView
  2707.             local updateRows = function() renderVisibleRows( self ) end
  2708.             if self.rowTimer then timer.cancel( self.rowTimer ); end;
  2709.             self.rowTimer = timer.performWithDelay( 1, updateRows, 400 )
  2710.         end
  2711.        
  2712.         if eType == "contentTouch" then
  2713.             local tapThresh = 3     -- used to determine if touch was a "drag" or a quick tap
  2714.             local moveThresh = 10   -- do not allow swipes once user drags up/down past this amount
  2715.             local swipeThresh = 12  -- if finger moves left/right this amount, trigger swipe event
  2716.            
  2717.             if event.phase == "press" then
  2718.                
  2719.                 -- tableView content has been touched
  2720.                
  2721.                 if tableView.rowTimer then timer.cancel( tableView.rowTimer ); tableView.rowTimer = nil; end;
  2722.                 Runtime:removeEventListener( "enterFrame", tableView.rowListener )
  2723.                
  2724.                 -- find out which row the touch began on and store a reference to it
  2725.                 tableView.currentSelectedRow = getRowAtCoordinate( tableView, event.y )
  2726.                 tableView.renderFrameCount = 0
  2727.                 tableView.renderFramePace = 0
  2728.                 content.yDistance = 0
  2729.                 content.canSwipe = true
  2730.                
  2731.                 Runtime:addEventListener( "enterFrame", tableView.rowListener )
  2732.                 content.trackRowSelection = true
  2733.                
  2734.             elseif event.phase == "moved" then
  2735.            
  2736.                 --Dispatch contentTouch event
  2737.                 event.type = "contentTouch"
  2738.                 invokeUserListener( tableView, event )
  2739.                
  2740.                 -- tableView content is being dragged
  2741.                 local canDrag = content.canDrag
  2742.                 local canSwipe = content.canSwipe
  2743.                 local yDistance
  2744.                
  2745.                 -- calculate distance traveled in y direction (only when needed)
  2746.                 if not canDrag or canSwipe then
  2747.                     yDistance = mAbs( event.y - event.yStart )
  2748.                 end
  2749.                
  2750.                 -- determine if this touch event could possibly be a "row tap"
  2751.                 if not canDrag then
  2752.                     if yDistance > tapThresh then
  2753.                         content.yDistance = yDistance
  2754.                         content.canDrag, canDrag = true, true
  2755.                         content.trackRowSelection = nil
  2756.                     end
  2757.                 end
  2758.                
  2759.                 -- determine whether y distance traveled is low enough to allow left/right swipes
  2760.                 if canSwipe then
  2761.                     if yDistance > moveThresh then
  2762.                         canSwipe = nil
  2763.                         content.canSwipe = nil
  2764.                     end
  2765.                 else
  2766.                     local selectedRow = tableView.currentSelectedRow
  2767.                    
  2768.                     if selectedRow and selectedRow.isTouched then
  2769.                         selectedRow.isTouched = false
  2770.                         selectedRow.reRender = true
  2771.                     end
  2772.                    
  2773.                     -- ensure rows move with drag
  2774.                     renderVisibleRows( tableView )
  2775.                 end
  2776.                
  2777.                 -- left/right swipes
  2778.                 local row = tableView.currentSelectedRow
  2779.                 if row and canSwipe then
  2780.                     local xDistance = event.x - event.xStart
  2781.                    
  2782.                     -- check to see if a "swipe" event should be dispatched
  2783.                     if xDistance > swipeThresh then
  2784.                         dispatchRowTouch( row, "swipeRight", tableView )
  2785.                         row.isTouched = false
  2786.                         row = nil
  2787.                         tableView.currentSelectedRow = nil
  2788.                         renderVisibleRows( tableView )
  2789.                         content.velocity = 0
  2790.                         content.trackRowSelection = false
  2791.                    
  2792.                         -- remove focus from tableView's content
  2793.                         display.getCurrentStage():setFocus( nil )
  2794.                         content.isFocus = nil
  2795.                        
  2796.                     elseif xDistance < -swipeThresh then
  2797.                         dispatchRowTouch( row, "swipeLeft", tableView )
  2798.                         row.isTouched = false
  2799.                         row = nil
  2800.                         tableView.currentSelectedRow = nil
  2801.                         renderVisibleRows( tableView )
  2802.                         content.velocity = 0
  2803.                         content.trackRowSelection = false
  2804.                    
  2805.                         -- remove focus from tableView's content
  2806.                         display.getCurrentStage():setFocus( nil )
  2807.                         content.isFocus = nil
  2808.                     end
  2809.                 end
  2810.            
  2811.             elseif event.phase == "release" then
  2812.                
  2813.                 local row = tableView.currentSelectedRow
  2814.                 if row then
  2815.                     if content.yDistance < tapThresh and content.trackRowSelection then
  2816.                         -- user tapped tableView content (dispatch row release event)
  2817.                         dispatchRowTouch( row, "tap", tableView )
  2818.                         row.isTouched = nil
  2819.                         row = nil
  2820.                         tableView.currentSelectedRow = nil
  2821.                     else
  2822.                         if row and row.isTouched then
  2823.                             dispatchRowTouch( row, "release", tableView )
  2824.                             row.isTouched = nil
  2825.                             row = nil
  2826.                             tableView.currentSelectedRow = nil
  2827.                             renderVisibleRows( tableView )
  2828.                         end
  2829.                     end
  2830.                 end
  2831.                
  2832.                 -- variables used during "moved" phase
  2833.                 content.yDistance = nil
  2834.                 content.canDrag = nil
  2835.                 content.canSwipe = nil
  2836.                 content.trackRowSelection = nil
  2837.                
  2838.                 -- prevents categories from getting 'stuck' in the wrong position
  2839.                 if content.category and content.category.y ~= 0 then content.category.y = 0; end
  2840.             end
  2841.        
  2842.         elseif eType == "movingToTopLimit" or eType == "movingToBottomLimit" then
  2843.             --Dispatch moving to top/bottom limit event
  2844.             invokeUserListener( tableView, event )
  2845.                
  2846.             -- prevents categories from getting 'stuck' in the wrong position
  2847.             if content.category and content.category.y ~= 0 then content.category.y = 0; end
  2848.             moveRowsWithTween( tableView )
  2849.         end
  2850.     end
  2851.    
  2852.     -- times how long finger has been held down on a specific row
  2853.     local function checkSelectionStatus( self, time )   -- self == tableView
  2854.         local content = self.content
  2855.         if content.trackRowSelection then
  2856.             local timePassed = time - content.markTime
  2857.                
  2858.             if timePassed > 110 then        -- finger has been held down for more than 100 milliseconds
  2859.                 content.trackRowSelection = false
  2860.                
  2861.                 -- initiate "press" event if a row is being touched
  2862.                 local row = self.currentSelectedRow
  2863.                 if row then
  2864.                     row.isTouched = true
  2865.                     dispatchRowTouch( row, "press", tableView )
  2866.                 end
  2867.             end
  2868.         end
  2869.     end
  2870.    
  2871.     -- enterFrame listener for tableView widget
  2872.     local function rowListener( self, event )   -- self == tableView
  2873.         -- limit velocity to maximum amount (self.maxVelocity)
  2874.         local velocity = self.content.velocity
  2875.         if velocity < -self.maxVelocity then self.content.velocity = -self.maxVelocity; end
  2876.         if velocity > self.maxVelocity then self.content.velocity = self.maxVelocity; end
  2877.        
  2878.         --Dispatch began scroll event
  2879.         event.type = "beganScroll"
  2880.         invokeUserListener( self, event )
  2881.        
  2882.         local isTrackingVelocity = self.content.trackVelocity
  2883.         if not isTrackingVelocity then
  2884.             if self.content.velocity == 0 then
  2885.                 --Dispatch endedScroll event
  2886.                 event.type = "endedScroll"
  2887.                 invokeUserListener( self, event )
  2888.                
  2889.                 Runtime:removeEventListener( "enterFrame", self.rowListener )
  2890.             end
  2891.             -- prevents categories from getting 'stuck' in the wrong position
  2892.             local content = self.content
  2893.             if content.category and content.category.y ~= 0 then content.category.y = 0; end
  2894.            
  2895.             -- renderFramePace and renderFrameCount will skip rendering frames if velocity (list travel speed) is too high
  2896.             -- [[
  2897.             local velocity = mAbs( velocity )
  2898.             if velocity >= 4 then
  2899.                 self.renderFramePace = 1
  2900.                
  2901.             elseif velocity >= 6 then
  2902.                 self.renderFramePace = 2
  2903.            
  2904.             elseif velocity >= 8 then
  2905.                 self.renderFramePace = 3
  2906.            
  2907.             elseif velocity >= 10 then
  2908.                 self.renderFramePace = 4
  2909.                
  2910.             else
  2911.                 self.renderFramePace = 0
  2912.             end
  2913.            
  2914.             if self.renderFrameCount >= self.renderFramePace then
  2915.                 self.renderFrameCount = 0
  2916.                 renderVisibleRows( self )
  2917.             else
  2918.                 self.renderFrameCount = self.renderFrameCount + 1
  2919.             end
  2920.             --]]
  2921.            
  2922.             --renderVisibleRows( self )
  2923.         else
  2924.             -- check to see if current row should be selected
  2925.             checkSelectionStatus( self, event.time )
  2926.         end
  2927.     end
  2928.    
  2929.     -- force re-render of all rows
  2930.     local function forceReRender( self )    -- self == tableView
  2931.         local content = self.content
  2932.         local rows = content.rows
  2933.        
  2934.         --self.topMostRendered, self.bottomMostRendered = nil, nil
  2935.         for i=1,#rows do
  2936.             local r = rows[i]
  2937.             if r.view then r.view:removeSelf(); r.view = nil; end
  2938.             r.isRendered = nil
  2939.         end
  2940.     end
  2941.    
  2942.     -- find proper category in proportion to specific row being at very top of list
  2943.     local function renderProperCategory( self, rows, rowIndex )     -- self == tableView
  2944.         local categoryIndex
  2945.                
  2946.         if rows[rowIndex].isCategory then
  2947.             categoryIndex = rowIndex
  2948.         else
  2949.             -- loop backwards to find the current category
  2950.             for i=rowIndex,1,-1 do
  2951.                 if rows[i].isCategory then
  2952.                     categoryIndex = i
  2953.                     break
  2954.                 end
  2955.             end
  2956.         end
  2957.        
  2958.         -- category found; render it and return the index
  2959.         if categoryIndex then
  2960.             renderCategory( self, rows[categoryIndex] )
  2961.             return categoryIndex
  2962.         end
  2963.     end
  2964.    
  2965.     -- scroll content to specified y-position
  2966.     local function scrollToY( self, yPosition, timeInMs )       -- self == tableView
  2967.         yPosition = yPosition or 0
  2968.         timeInMs = timeInMs or 1500
  2969.                
  2970.         if yPosition > 0 and not self._isPicker then
  2971.             print( "WARNING: You must specify a y-value less than zero (negative) when using tableView:scrollToY()." )
  2972.             return
  2973.         end
  2974.  
  2975.         local content = self.content
  2976.        
  2977.         -- called once content is in desired location
  2978.         local function contentInPosition()
  2979.             local row = getRowAtCoordinate( self, self.y )
  2980.             if row then
  2981.                 renderProperCategory( self, content.rows, row.index )
  2982.             end
  2983.         end
  2984.  
  2985.         if timeInMs > 0 then
  2986.             local updateTimer
  2987.             local cancelUpdateTimer = function() timer.cancel( updateTimer ); updateTimer = nil; contentInPosition(); end
  2988.             if content.tween then transition.cancel( content.tween ); end
  2989.             content.tween = transition.to( content, { y=yPosition, time=timeInMs, transition=easing.outQuad, onComplete=cancelUpdateTimer } )
  2990.             local updateRows = function() renderVisibleRows( self ); end
  2991.             updateTimer = timer.performWithDelay( 1, updateRows, timeInMs )
  2992.         else
  2993.             content.y = yPosition
  2994.             forceReRender( self )
  2995.             renderVisibleRows( self )
  2996.             contentInPosition()
  2997.         end
  2998.     end
  2999.    
  3000.     -- scroll content to place specific row at top of tableView
  3001.     local function scrollToIndex( self, rowIndex, timeInMs )    -- self == tableView
  3002.         local content = self.content
  3003.         local rows = content.rows
  3004.         local padding = self.topPadding or 0
  3005.         local yPosition = -(rows[rowIndex].topOffset) + padding
  3006.        
  3007.         if yPosition then
  3008.             if timeInMs then
  3009.                 scrollToY( self, yPosition, timeInMs )
  3010.             else
  3011.                 content.y = yPosition
  3012.                 forceReRender( self )
  3013.                 renderVisibleRows( self )
  3014.                 renderProperCategory( self, rows, rowIndex ) -- render the appropriate category
  3015.             end
  3016.         end
  3017.     end
  3018.    
  3019.     -- returns y-position of content
  3020.     local function getContentPosition( self )   -- self == tableView
  3021.         return self.content.y
  3022.     end
  3023.  
  3024.     -- clear all rows from tableview (method)
  3025.     local function deleteAllRows( self ) -- self == tableView
  3026.         local content = self.content
  3027.         local rows = content.rows
  3028.  
  3029.         for i=#rows,1,-1 do
  3030.             local r = rows[i]
  3031.             display.remove( r.view ); r.view = nil
  3032.             table.remove( rows, r.index )
  3033.  
  3034.             if r.isCategory and content.category then
  3035.                 if content.category.index == r.index then
  3036.                     display.remove( content.category )
  3037.                     content.category = nil
  3038.                 end
  3039.             end
  3040.         end
  3041.         self.content.rows = {}
  3042.     end
  3043.    
  3044.     -- permanently remove specified row from tableView
  3045.     local function deleteRow( self, rowOrIndex )    -- self == tableView
  3046.         local content = self.content
  3047.         local rows = content.rows
  3048.         local row
  3049.        
  3050.         -- function accepts row table or index integer
  3051.         if type(rowOrIndex) == "table" then
  3052.             row = rowOrIndex
  3053.         else
  3054.             row = rows[rowOrIndex]
  3055.         end
  3056.        
  3057.         if row then
  3058.             if row.isRendered then
  3059.                 if row.view then display.remove( row.view ); row.view = nil; end
  3060.             end
  3061.             table.remove( content.rows, row.index )
  3062.  
  3063.             if row.isCategory and content.category then
  3064.                 if content.category.index == row.index then
  3065.                     display.remove( content.category )
  3066.                     content.category = nil
  3067.                 end
  3068.             end
  3069.         end
  3070.        
  3071.         updateRowData( self )
  3072.         renderVisibleRows( self )
  3073.     end
  3074.    
  3075.     -- cleanup function (automatically called prior to calling removeSelf)
  3076.     local function cleanTableView( self )   -- self == tableView
  3077.         -- remove tableView's enterframe listener
  3078.         Runtime:removeEventListener( "enterFrame", self.rowListener )
  3079.         self.rowListener = nil
  3080.        
  3081.         -- cancel any potentially active timers/transitions
  3082.         if self.rowTimer then timer.cancel( self.rowTimer ); self.rowTimer = nil; end
  3083.         if self.content and self.content.tween then
  3084.             transition.cancel( self.content.tween ); self.content.tween = nil
  3085.         end
  3086.        
  3087.         -- remove category object if it exists
  3088.         if self.content.category then
  3089.             self.content.category:removeSelf()
  3090.             self.content.category = nil
  3091.         end
  3092.     end
  3093.    
  3094.     local function createTableView( options )
  3095.         local options = options or {}
  3096.        
  3097.         -- tableView-specific parameters
  3098.         local   id = options.id or "widget_tableView"
  3099.         local   renderThresh = options.renderThresh or 100  -- px amount above and below to begin rendering rows
  3100.        
  3101.         -- shared scrollView parameters (if not specified will use scrollView defaults)
  3102.         local   left = options.left or 0
  3103.         local   top = options.top or 0
  3104.         local   width = options.width or (display.contentWidth-left)
  3105.         local   height = options.height or (display.contentHeight-top)
  3106.         local   scrollWidth = width
  3107.         local   scrollHeight = height
  3108.         local   friction = options.friction
  3109.         local   bgColor = options.bgColor
  3110.         local   maskFile = options.maskFile
  3111.         local   hideBackground = options.hideBackground
  3112.         local   keepRowsPastTopVisible = options.keepRowsPastTopVisible
  3113.         local   topPadding = options.topPadding
  3114.         local   bottomPadding = options.bottomPadding
  3115.         local   maxVelocity = options.maxVelocity or 10
  3116.         local   baseDir = options.baseDir or system.ResourceDirectory
  3117.         local   hideScrollBar = options.hideScrollBar or false
  3118.         local   scrollBarColor = options.scrollBarColor
  3119.         local   userListener = options.listener or nil
  3120.        
  3121.         --Picker - Variables used by the picker soft landing function
  3122.         local   selectionHeight = options.selectionHeight or nil
  3123.         local   isPicker = options.isPicker or false
  3124.         local   pickerTop = options.pickerTop or nil
  3125.                
  3126.         -- tableView foundation is a scrollView widget
  3127.         local tableView = widget.newScrollView{
  3128.             id = id,
  3129.             left = left,
  3130.             top = top,
  3131.             width = width,
  3132.             height = height,
  3133.             scrollWidth = scrollWidth,
  3134.             scrollHeight = scrollHeight,
  3135.             friction = friction,
  3136.             bgColor = bgColor,
  3137.             maskFile = maskFile,
  3138.             hideBackground = hideBackground,
  3139.             listener = scrollListener,
  3140.             isVirtualized = true,
  3141.             topPadding = topPadding,
  3142.             bottomPadding = bottomPadding,
  3143.             baseDir = baseDir,
  3144.             hideScrollBar = hideScrollBar,
  3145.             scrollBarColor = scrollBarColor,
  3146.             selectionHeight = selectionHeight,
  3147.         }
  3148.         tableView.userListener = userListener;
  3149.         tableView.hasScrolled = false
  3150.        
  3151.         -- properties and methods
  3152.         tableView._isWidget = true
  3153.         --Variables exposed for picker soft landing function
  3154.         tableView._isPicker = isPicker
  3155.         tableView.pickerTop = pickerTop
  3156.         -------------------------------------
  3157.         function tableView.rowListener( event ) rowListener( tableView, event ); end
  3158.         tableView.content.rows = {} -- holds row data
  3159.         tableView.insertRow = insertRow
  3160.         tableView.deleteRow = deleteRow
  3161.         tableView.deleteAllRows = deleteAllRows
  3162.         tableView.renderThresh = renderThresh
  3163.         tableView.scrollToY = scrollToY
  3164.         tableView.scrollToIndex = scrollToIndex
  3165.         tableView.getRowAtCoordinate = getRowAtCoordinate
  3166.         tableView.getContentPosition = getContentPosition
  3167.         tableView.keepRowsPastTopVisible = keepRowsPastTopVisible
  3168.         tableView.maxVelocity = maxVelocity
  3169.         tableView.renderFramePace = 0
  3170.         tableView.renderFrameCount = 0
  3171.        
  3172.         -- clean method will be called whenever 'removeSelf' is called
  3173.         tableView.clean = cleanTableView
  3174.        
  3175.         -- position the content based on 'topPadding' variable
  3176.         tableView.content.y = topPadding or 0
  3177.        
  3178.         return tableView
  3179.     end
  3180.    
  3181.     return createTableView( options )
  3182. end
  3183.  
  3184. -----------------------------------------------------------------------------------------
  3185. -----------------------------------------------------------------------------------------
  3186.  
  3187. return widget
Advertisement
Add Comment
Please, Sign In to add comment