iOS Style Pull To Refresh

This is an extension to my recent posts about iOS Style WidgetDemo improvements. To get the full demo working get the Improved WidgetDemo Sample from the code exchange:

http://developer.anscamobile.com/code/improved-widgetdemo-sample

This builds on the recent iOS Style Search by adding pull to refresh function as seen in apps like the Twitter client. It also adds some debugging over the previous posts.

You will need to download this image and save it in the WidgetDemo directory:

Refresh: http://content.screencast.com/users/HoraceBury/folders/Default/media/10d14535-be2a-4d97-93e3-eba179f3541f/refresh.png

The code below shows how to add pull to refresh functionality - simply add the ‘pulltorefresh’ value to the header of your content table and give it a function to call. The function will need to take the parent item value and a function to call with the content table once the data has been refreshed. The callback function can be called immediately, but is there in case you need to perform a network request which may not respond immediately.

To add the demo functionality, replace the WidgetDemo ‘menu.lua’ and ‘menustructure.lua’ code with the files below…

But first, a warning. There are three known bugs:

  • Images will not be correctly displayed because I have not used newImageRect.
  • Searches cannot have a pull to refresh because the list variable appears to be the parent tableView object in the menu.lua at line 556. The pull operation has no way to know that the scroll position has gone beyond the threshold.
  • The scrollbar size is incorrect in certain situations, such as search results. You are best served by disabling the scrollbar in those situations.

Please be assured that I am working on these and continuing to improve the functionality. Once this is all working satisfactorily I will begin work on making it iPad compatible and implementing pop-over menus, etc.

menustructure.lua:
[lua]-- menu structure

require(“net”)

rootmenu, byproduct, byjob, byregion, companieswith = nil, nil, nil, nil, nil, nil

byproduct = {
title=“By Product”,
{ id="", text=“Cutlery”, iscategory=false, dataitem=nil },
{ id="", text=“Homewares”, iscategory=false, dataitem=nil },
{ id="", text=“Frozen”, iscategory=false, dataitem=nil },
{ id="", text=“Refreshments”, iscategory=false, dataitem=nil },
}

byjob = {
title=“By Job”,
{ id="", text=“Director”, iscategory=false, dataitem=nil },
{ id="", text=“Sales”, iscategory=false, dataitem=nil },
{ id="", text=“Purchasing”, iscategory=false, dataitem=nil },
}

byafrica = {
title=“Africa”,
{ id="", text=“Mozambique”, iscategory=false, dataitem=nil },
{ id="", text=“Zaire”, iscategory=false, dataitem=nil },
{ id="", text=“Ethiopia”, iscategory=false, dataitem=nil },
}

byregion = {
title=“By Region”,
{ id="", text=“Africa”, iscategory=false, dataitem=byafrica },
{ id="", text=“Asia”, iscategory=false, dataitem=nil },
{ id="", text=“Oceana”, iscategory=false, dataitem=nil },
}

function GetTestSubMenu()
print(‘GetTestSubMenu()’)
local tbl = { title=“Test Items”, defaultscroll=1, hasindex=true, }
for i=65, 90 do
tbl[#tbl+1] = { id="", text=string.char(i), iscategory=false, callback=nil, dataitem=nil, index=string.char(i), }
end
return tbl
end

function GetBbcNewsRss( dataitem, func )
– builds the content for the submenu items
function GetData( name, xml )
– sub menu header info
local tbl = { title=“BBC News”, defaultscroll=1, hasscrollbar=true, hasindex=false, canscrolltotop=true, }

– build submenu items
for i=1, #xml.channel.item do
local item = xml.channel.item[i]
tbl[#tbl+1] = { id="", text=item.title, iscategory=false, callback=nil, dataitem=nil, index=nil, }
end

– return the submenu content
func( tbl )
end

– go read the rss and it will execute the GetData function callback when the network request responds
readBbcNews( “bbcnewsrss”, GetData )
end

function PerformSearch( parentitem, term, callback )
local list = {}
for i=1, math.random(1, 7) do
list[#list+1] = { id="", text=term…’ '…math.random(100,999), iscategory=false, dataitem=nil }
end
print(‘Searching…’)
callback( list )
end

function RefreshRootMenu( parentitem, callback )
print(‘Refreshing…’)
callback( rootmenu )
end

rootmenu = {
title=“Products”, defaultscroll=2, hasindex=true, hasscrollbar=true, canscrolltotop=true,
pulltorefresh=RefreshRootMenu,
{ id="", text=“Search Bar”, issearch=true, dataitem=PerformSearch, },
{ id="", text=“Find”, iscategory=true, dataitem=nil, index=“F” },
{ id="", text=“By Product”, iscategory=false, dataitem=byproduct, candelete=true },
{ id="", text=“By Job”, iscategory=false, dataitem=byjobfunction, candelete=true },
{ id="", text=“By Region”, iscategory=false, dataitem=byregion, candelete=true },
{ id="", text=“Search”, iscategory=true, dataitem=nil, index=“S” },
{ id="", text=“Companies With…”, iscategory=false, dataitem=function(d,f) f(GetTestSubMenu()) end },
{ id="", text=“Info”, iscategory=true, dataitem=nil, index=“I” },
{ id="", text=“About…”, iscategory=false, dataitem=nil },
{ id="", text=“Magazines”, iscategory=true, dataitem=nil, index=“M” },
{ id="", text=“The Times”, iscategory=false, dataitem=nil },
{ id="", text=“The Independent”, iscategory=false, dataitem=nil },
{ id="", text=“The Sun”, iscategory=false, dataitem=nil },
{ id="", text=“News Feeds”, iscategory=true, dataitem=nil, index=“N” },
{ id="", text=“BBC News”, iscategory=false, dataitem=GetBbcNewsRss },
}[/lua]

menu.lua:
[lua]-- menu

function newMenu( parent, rootdata, x, y, width, height )
– renders a title bar for a submenu
local function newTopBar( parent, title, scrollToBack, scrollToTop )
– local this bar
local group = display.newGroup()
group.widgets = {}
parent:insert( group )

– clean out the widgets
function group:clean()
for i=#group.widgets, 1, -1 do
group.widgets[i]:removeSelf()
group.widgets[i] = nil
end
group:removeSelf()
end

– status bar touch pad
if (scrollToTop) then
local touchbar = display.newRect( group, 0, 0, display.contentWidth, display.statusBarHeight )
function touchbar:touch( event )
if (event.phase == “began” and event.y <= display.statusBarHeight) then
scrollToTop()
end
return true
end
touchbar:addEventListener( “touch”, touchbar )
end

– create a gradient for the top-half of the toolbar
local toolbarGradient = graphics.newGradient( {168, 181, 198, 255 }, {139, 157, 180, 255}, “down” )

– create toolbar to go at the top of the screen
local titleBar = widget.newTabBar{
top = display.statusBarHeight,
topGradient = toolbarGradient,
bottomFill = { 117, 139, 168, 255 },
height = 44, width = 320
}
group:insert( titleBar.view )
group.widgets[#group.widgets+1] = titleBar

– create embossed text to go above toolbar
local titleText = display.newEmbossedText( title, 0, 0, native.systemFontBold, 20, { 255 } )
titleText:setReferencePoint( display.CenterReferencePoint )
titleText.x = display.contentWidth * 0.5 – (display.contentWidth * 0.5) – + x
titleText.y = 44 – + (titleBar.y + titleBar.height * 0.5)
group:insert( titleText )
group.widgets[#group.widgets+1] = titleText

– onRelease listener for back button
local function onBackRelease( event )
if (scrollToBack) then
scrollToBack( group.clean )
end
return true
end

– create ‘back’ button to be placed on toolbar
if (parent.parent.numChildren > 1) then
local backButton = widget.newButton{
label = “Back”,
left = 5, top = 28,
style = “backSmall”,
onRelease = onBackRelease
}
group:insert( backButton.view )
group.widgets[#group.widgets+1] = backButton
end
end – newTopBar

– renders a search input box for a search panel
local function newSearchBox( parent, cancel, scrollToTop, searchCallback )
– local this bar
local group = display.newGroup()
group.widgets = {}
parent:insert( group )

– declare the input text box
local inputbox = nil

– clean out the widgets
function group:clean()
for i=#group.widgets, 1, -1 do
group.widgets[i]:removeSelf()
group.widgets[i] = nil
end
group:removeSelf()
end

– status bar touch pad
if (scrollToTop) then
local touchbar = display.newRect( group, 0, 0, display.contentWidth, display.statusBarHeight )
function touchbar:touch( event )
if (event.phase == “began” and event.y <= display.statusBarHeight) then
scrollToTop()
end
return true
end
touchbar:addEventListener( “touch”, touchbar )
end

– search box image
local searchimg = display.newImage( group, “searchcancel.png” )
searchimg.xScale, searchimg.yScale = .5, .5
searchimg.x, searchimg.y = display.contentCenterX, searchimg.height/2-2

– onRelease listener for back button
local function onCancelRelease( event )
if (cancel) then
cancel( group.clean )
end
return true
end

– create ‘cancel’ button to be placed on toolbar
if (parent.parent.numChildren > 1) then
local cancelButton = widget.newButton{
label = “”,
left = 260, top = 27,
–style = “backSmall”,
default=“defaultcancel.png”,
over=“overcancel.png”,
width=110/2, height=58/2,
onRelease = onCancelRelease
}
group:insert( cancelButton.view )
group.widgets[#group.widgets+1] = cancelButton
end

– input box listener function
local function textListener( event )
if (event.phase == “editing”) then
searchCallback( event.text )
end
return true
end

– create native text input box
inputbox = native.newTextBox( 30, 30, 200, 25, textListener )
group.widgets[#group.widgets+1] = inputbox
inputbox.isEditable = true
inputbox.align = “left”
inputbox.size = 16

– return the text input box so pull to refresh can work
return inputbox
end – newSearchBox

– creates the submenu or search panel
local function newSubMenu( contentdata, issearch )
– validate
issearch = issearch or false

– container for all submenu components
local submenugroup = display.newGroup()
parent:insert( submenugroup )

– timers etc
local list = nil – table view containing the items in the menu (the important widget)
local enterFrame = nil – will be called with the regular enterFrame event to call other functions which check and refresh visual elements
local scrollCheck = nil – called by enterframe to check the scroll position of the tableview content
local adjustScrollBar = nil – called by enterframe to check the position of the tableview content and update the scrollbar size and position
local pullToRefresh = nil – function called to refresh the content

– position the submenugroup
if (parent.numChildren > 1 and issearch == true) then
submenugroup.x, submenugroup.y = parent[parent.numChildren-1].x, parent[parent.numChildren-1].y
else
submenugroup.x, submenugroup.y = (parent.numChildren-1) * display.contentWidth, 0
end

– create tableView widget
list = widget.newTableView{
top = display.statusBarHeight + 44,
width = display.contentWidth,
height = 366,
maskFile = “assets/mask-320x366.png”
}

– keeps track of the number of rows
list.totalRowCount = 0
– keeps track of the total height of the widget content
list.totalheight = 0

– remove this if using later than build 2012.826
local function deleteAllRows()
while (list.totalRowCount > 0) do
list:deleteRow( 1 )
list.totalRowCount = list.totalRowCount - 1
end
end

– list of scroll index items
local function populateTableView( menudata, isnewdata )
local scroll = {}

– onEvent listener for the tableView
local function onRowTouch( event )
local row = event.target
local rowGroup = event.view

if (not row.isCategory) then
if (event.phase == “swipeLeft” and menudata[event.index].candelete == true) then
transition.to( rowGroup.del, {time=rowGroup.del.transtime,maskX=-(rowGroup.del.width/2+1),onComplete=rowGroup.del.setActive} )
elseif (event.phase == “swipeRight” and menudata[event.index].candelete == true) then
transition.to( rowGroup.del, {time=rowGroup.del.transtime,maskX=rowGroup.del.width/2+1,onComplete=rowGroup.del.setInactive} )
elseif (event.phase == “release” and not row.isCategory) then
row.reRender = true
print( “You touched row #” … event.index…" - ‘"…menudata[event.index].text…"’" )

– do population of submenu
local menuitem = menudata[event.index]

if (menuitem.dataitem ~= nil) then
– get the submenu’s data and add the new sub menu
– this will shift the parent group left to show a deeper submenu
local dataitem = menuitem.dataitem

– populate the submenu (dataitem passed in is the submenu table of rows)
function callback( dataitem )
– create the submenu or search item tableView
newSubMenu( dataitem, menuitem.issearch )

– use animation to show the next tableView
if (menuitem.issearch == true) then
– lay the search panel over the top of the current tableView menu (should be improved later to look like the smooth contacts list animation)
– ie: right now, do nothing (the search will just snap into place over the current tableView widget)
else
– slide the current tableView widget left to show the submenu animating into position
transition.to( parent, {time=300, x=parent.x-display.contentWidth} )
transition.to( topGroup, {time=300, x=parent.x-display.contentWidth} )
end
end

– if dataitem is a function, call it to get the data to put in the submenu, otherwise just populate the submenu
– if the dataitem is a function it must callback the passed in function to populate the submenu, this allows for network traffic delays
– the callback function does not need to be called, if the dataitem function wants to switch to another tab, for example
if (type(dataitem) == “function”) then
– if the item is a search, populate it with the initial search term
if (menuitem.issearch == true) then
– call the client function as an search
– callback( dataitem( {phase=“began”, term=""} ) )
callback( menuitem )
else
– call the client function with the parent item to get the submenu items…
– pullToRefresh = function()
dataitem( menuitem, callback )
– end
– pullToRefresh()
end
else
– populate directly because the submenu items are known here
callback( dataitem )
end
end
end
end

return true
end – onRowTouch

– onRender listener for the tableView
local function onRowRender( event )
local row = event.target
local rowGroup = event.view
local textFunction = display.newRetinaText
if row.isCategory then textFunction = display.newEmbossedText; end

– add pulltorefresh
if (event.index == 1 and type(pullToRefresh) == “function”) then
print(‘Add Pull To Refresh’)
local refreshgroup = display.newGroup()
list.refreshgroup = refreshgroup
rowGroup:insert( refreshgroup )
refreshgroup.x, refreshgroup.y = 0, -64
local rect = display.newRect( refreshgroup, 0, 0, display.contentWidth, 64 )
–rect:setFillColor(255,0,0)
local refreshimg = display.newImage( refreshgroup, “refresh.png” )
refreshimg.x, refreshimg.y = 50, 25
refreshimg.xScale, refreshimg.yScale = .5, .5
local refreshtext = display.newText( refreshgroup, “Pull to refresh”, 100, 15, native.systemFont, 16 )
refreshtext:setTextColor( 180, 180, 180 )
local releasetext = display.newText( refreshgroup, “Release to refresh”, 100, 15, native.systemFont, 16 )
releasetext:setTextColor( 180, 180, 180 )
releasetext.isVisible = false

rect.willRefresh = false

list.refreshgroup.indicateRefresh = function()
if (not rect.willRefresh) then
rect.willRefresh = true
–rect:setFillColor(0,255,0)
transition.to( refreshimg, { time=200, rotation=-180 } )
refreshtext.isVisible = false
releasetext.isVisible = true
end
end
list.refreshgroup.isRefreshRequested = function()
return rect.willRefresh
end
list.refreshgroup.resetRefresh = function()
rect:setFillColor(255,0,0)
rect.willRefresh = false
refreshimg.rotation = 0
refreshtext.isVisible = true
releasetext.isVisible = false
end
end

if (event.index == 1 and menudata[event.index].issearch == true) then
– is a search bar
row.searchimg = display.newImage( rowGroup, “searchwith.png” )
row.searchimg.xScale, row.searchimg.yScale = .5, .5
row.height = row.searchimg.height/2
row.searchimg.x, row.searchimg.y = display.contentCenterX, row.searchimg.height/4
else
– is regular bar
row.title = textFunction( menudata[event.index].text, 12, 0, native.systemFontBold, 16 )
row.title:setReferencePoint( display.CenterLeftReferencePoint )
row.title.y = row.height * 0.5

if (not row.isCategory) then
row.title.x = 15
row.title:setTextColor( 0 )

local del = nil – the image sitting over the top of the button widget

local onButtonEvent = function (event )
print(‘del.isactive’,del.isactive)
if (not del.isactive) then
return false
end

if (event.phase == “release”) then
print( “You pressed and released a button!” )
end
return true
end

local myButton = widget.newButton{
id = “btn001”,
left = 225,
top = 9,
label = “”,
width = 90, height = 46,
cornerRadius = 8,
onEvent = onButtonEvent,
default=“deleteback.png”,
over=“deleteback.png”,
}
rowGroup:insert(myButton.view)

del = display.newImage(rowGroup,“deletebtn.png”)
del.x, del.y = 270, 32
del.xScale, del.yScale = .7, .7
del.isactive = false
rowGroup.del = del

local mask = graphics.newMask(“slidemask.png”)
del:setMask( mask )

del.maskScaleX, del.maskScaleY = -1, 1
del.maskX = del.width/2+1
del.maskY = 100

del.transtime = 300

function del:setActive()
print(‘active’,deletetap)
del.isactive = true
end
function del:setInactive()
print(‘inactive’)
del.isactive = false
end
end

– must insert everything into event.view:
rowGroup:insert( row.title )
end
end – onRowRender

– assign data to the rows
function createContent()
– defaults
local defaultRowColor = { 174, 183, 190, 255 }
local defaultLineColor = {0,0,0,255}

– Add rows
for i=1, #menudata do
local rowHeight, rowColor, lineColor, isCategory = 64
local menuitem = menudata[i]

if (menuitem.iscategory) then
isCategory = true; rowHeight = 24; rowColor=defaultRowColor; lineColor=defaultLineColor
end

– accumulate the total height of the content rows
list.totalheight = list.totalheight + rowHeight
list.totalRowCount = list.totalRowCount + 1

– insert the row into the tableView widget
list:insertRow{
onEvent=onRowTouch,
onRender=onRowRender,
height=rowHeight,
isCategory=isCategory,
rowColor=rowColor,
lineColor=lineColor,
issearch=true
}

– index can be a string or a function to return a string - string can be one char
if (menuitem.index ~= nil) then
local scrollitem = menuitem.index
if (type(scrollitem) == “function”) then
scrollitem = scrollitem( menuitem )
end
scroll[#scroll+1] = { index=i, char=scrollitem }
end
end

function addAlphaStrip(parent, callback)
local strip = display.newGroup()
parent:insert( strip )

strip.x, strip.y = display.contentWidth - 15, display.statusBarHeight+50
local alpha = display.newRoundedRect( strip, -10, 0, 20, display.contentHeight - 128, 10 )
alpha:setFillColor( 224, 224, 224 )
alpha.isVisible = false
alpha.isHitTestable = true

local size = 13.3
for i=65, 90 do
local index = i-64

local function touch(event)
alpha.isVisible = (event.phase == “began” or event.phase == “moved”)

for s=1, #scroll do
if (scroll[s].char == string.char(i)) then
list:scrollToIndex( scroll[s].index, 300 )
break
end
end
return true
end

local rect = display.newRect( strip, 0, 0, 40, size )
rect.x, rect.y = 0, 10+(i-65)*size
rect.alpha = 0
rect.isHitTestable = true

local letter = display.newText(strip, string.char(i), 0, 0, native.systemFont, 10)
letter:setTextColor(106, 115, 125)
letter.x, letter.y = 0.5, 10+(i-65)*size

rect:addEventListener( “touch”, touch )
end
end
if (isnewdata and menudata.hasindex) then addAlphaStrip( submenugroup ) end

– auto scroll to initial position
if (menudata.defaultscroll) then list:scrollToIndex( menudata.defaultscroll, 0 ) end

local currentpos = list:getScrollPosition()
list.maxscrolltop = 366-list.totalheight+24

–[[scroll bar handling]]–
local function addScrollBar()
local scrollbar = display.newRoundedRect( submenugroup, display.contentWidth - 5, display.statusBarHeight+50, 2.5, display.contentHeight - 128, 2.5 )
scrollbar:setFillColor( 255,0,0 )
scrollbar.lastchangetime = system.getTimer()
adjustScrollBar = function( event )
if (list.totalheight < 366) then
scrollbar.alpha = 0
else
local starty = scrollbar.y
– check scroll position and adjust
local scrollpos = list:getScrollPosition()
if (scrollpos <= 0 and scrollpos >= list.maxscrolltop) then
local heightperc = (346 / list.totalheight) * 100
local scrollperc = (math.abs(scrollpos / math.abs(list.maxscrolltop))) * 100

scrollbar.height = 346/100*heightperc
scrollbar.y = 10 + display.statusBarHeight + 44 + (scrollbar.height/2)+((346-scrollbar.height)/100*scrollperc)
elseif (scrollpos > 0) then
local scrollperc = (scrollpos / 366) * 100
local heightperc = (346 / list.totalheight) * 100
heightperc = heightperc - scrollperc

scrollbar.height = 346/100*heightperc
scrollbar.y = 10 + display.statusBarHeight + 44 + (scrollbar.height/2)
elseif (scrollpos < list.maxscrolltop) then
local scrollperc = ((list.maxscrolltop - scrollpos) / 366) * 100
local heightperc = (346 / list.totalheight) * 100
heightperc = heightperc - scrollperc

scrollbar.height = 346/100*heightperc
scrollbar.y = 10 + display.statusBarHeight + 44 + (346 - (scrollbar.height/2))
end
– fade out the scrollbar
if (starty ~= scrollbar.y) then
scrollbar.lastchangetime = system.getTimer()
end
if (system.getTimer() - scrollbar.lastchangetime > 500) then
if (scrollbar.fadeintrans ~= nil) then
transition.cancel( scrollbar.fadeintrans )
scrollbar.fadeintrans = nil
end
if (scrollbar.fadeouttrans == nil) then
scrollbar.fadeouttrans = transition.to( scrollbar, { time=250, alpha=0 } )
end
else
if (scrollbar.fadeouttrans ~= nil) then
transition.cancel( scrollbar.fadeouttrans )
scrollbar.fadeouttrans = nil
end
if (scrollbar.fadeintrans == nil) then
scrollbar.fadeintrans = transition.to( scrollbar, { time=150, alpha=1 } )
end
end
end
end – adjustScrollBar
–Runtime:addEventListener( “enterFrame”, adjustScrollBar ) – called by the more global enterFrame(event) function
end – addScrollBar
if (isnewdata and menudata.hasscrollbar) then addScrollBar() end
end – createContent

– clear current tableView content
deleteAllRows()
list.totalRowCount = 0
list.totalheight = 0

– create the content
createContent()
end – populateTableView

– scroll to top function
if (contentdata.canscrolltotop) then
submenugroup.scrollToTop = function()
if (contentdata.defaultscroll) then
list:scrollToIndex( contentdata.defaultscroll, 400 )
else
list:scrollToIndex( 1, 400 )
end
end
end

– pull to refresh check
if (contentdata.pulltorefresh) then
scrollCheck = function()
local currentpos = list:getScrollPosition()
if (pullToRefresh) then
–print(‘currentpos’,currentpos)
if (currentpos >= 64) then
list.refreshgroup:indicateRefresh()
elseif (currentpos >= -1 and currentpos <= 1 and list.refreshgroup:isRefreshRequested()) then
list.refreshgroup:resetRefresh()
pullToRefresh()
end
end
end
end

– if this is a search panel provide the search bar at the top, if not provide a title bar
if (issearch == true) then
– is a search panel
– stops the search and removes the search panel
local function cancelSearch( onComplete )
Runtime:removeEventListener( “enterFrame”, enterFrame )
onComplete()
list:removeSelf()
list = nil
submenugroup:removeSelf()
end

– gets the search results from the search callback and populates the tableview
local function searchCallback( searchterm )
– called by the menu’s search function to populate the tableView
function doPopulate( list )
populateTableView( list, false )
end

– call the menu’s search function to get the search results table
contentdata.dataitem( contentdata, tostring(searchterm), doPopulate )
end

– create search bar
local textbox = newSearchBox( submenugroup, cancelSearch, submenugroup.scrollToTop, searchCallback )

– called by the pull to refresh function
pullToRefresh = function()
– get the current search text and call the data retrieval function
searchCallback( textbox.text )
end
else
– is a regular sub/menu
– scroll to origin - this is actually the back button effect
local function scrollToBack( onComplete )
function clean()
Runtime:removeEventListener( “enterFrame”, enterFrame )
onComplete()
list:removeSelf()
list = nil
submenugroup:removeSelf()
end
transition.to( parent, {time=300, x=parent.x+display.contentWidth, onComplete=clean} )
end

– create title bar
newTopBar( submenugroup, contentdata.title, scrollToBack, submenugroup.scrollToTop )

– if there is a refresh function provided the callback
if (type(contentdata.pulltorefresh) == “function”) then
pullToRefresh = function()
– called by the menu’s search function to populate the tableView
function doPopulate( list )
populateTableView( list, false )
end

– call the menu’s refresh function to get the content table
contentdata.pulltorefresh( contentdata, doPopulate )
end
end
end – (issearch == true)

– insert widget into demoGroup
submenugroup:insert( list.view )

– populate the tableView with initial data
populateTableView( contentdata, true )

– call the refresh functions
enterFrame = function( event )
if (scrollCheck) then scrollCheck() end
if (adjustScrollBar) then adjustScrollBar() end
end

– start the enterframe listener to call refresh functions
Runtime:addEventListener( “enterFrame”, enterFrame )
end – newSubMenu

newSubMenu( rootdata, rootdata.issearch )
end[/lua] [import]uid: 8271 topic_id: 27589 reply_id: 327589[/import]

The bug involving pull to refresh for searches has been fixed.

Please be aware that the header properties (such as title, dataitem and pulltorefresh) of a search results tableView will actually come from the parent menuitem and not the table of results data. This means that the pulltorefresh function needs to be declared in the dataitem table representing the row with the search box, not the search results table. You can see an example of this in the menustructure.lua fix below, where the first row of tableView data (text=“Search Bar”) has the pulltorefresh function, whereas it would normally be defined in the header, as seen at the top of the rootmenu table.

The rootmenu table is also a special case as it defines the issearch and dataitem properties. This is because, while it can be a regular tableView it can also be a search. If you uncomment those two properties you will see that the initial content of the top level menu is from the rows defined in the rootmenu table, but that pulling to refresh causes the content to be replaced by the search results rows returned by the PerformSearch function as defined in the header of rootmenu.

I will continue to work on the other bugs and feature improvements.

menustructure.lua:
[lua]-- menu structure

require(“net”)

rootmenu, byproduct, byjob, byregion, companieswith = nil, nil, nil, nil, nil, nil

byproduct = {
title=“By Product”,
{ id="", text=“Cutlery”, iscategory=false, dataitem=nil },
{ id="", text=“Homewares”, iscategory=false, dataitem=nil },
{ id="", text=“Frozen”, iscategory=false, dataitem=nil },
{ id="", text=“Refreshments”, iscategory=false, dataitem=nil },
}

byjob = {
title=“By Job”,
{ id="", text=“Director”, iscategory=false, dataitem=nil },
{ id="", text=“Sales”, iscategory=false, dataitem=nil },
{ id="", text=“Purchasing”, iscategory=false, dataitem=nil },
}

byafrica = {
title=“Africa”,
{ id="", text=“Mozambique”, iscategory=false, dataitem=nil },
{ id="", text=“Zaire”, iscategory=false, dataitem=nil },
{ id="", text=“Ethiopia”, iscategory=false, dataitem=nil },
}

byregion = {
title=“By Region”, canscrolltotop=false,
{ id="", text=“Africa”, iscategory=false, dataitem=byafrica },
{ id="", text=“Asia”, iscategory=false, dataitem=nil },
{ id="", text=“Oceana”, iscategory=false, dataitem=nil },
}

function GetTestSubMenu()
print(‘GetTestSubMenu()’)
local tbl = { title=“Test Items”, defaultscroll=1, hasindex=true, }
for i=65, 90 do
tbl[#tbl+1] = { id="", text=string.char(i), iscategory=false, callback=nil, dataitem=nil, index=string.char(i), }
end
return tbl
end

function GetBbcNewsRss( dataitem, func )
– builds the content for the submenu items
function GetData( name, xml )
– sub menu header info
local tbl = { title=“BBC News”, defaultscroll=1, hasscrollbar=true, hasindex=false, canscrolltotop=true, }

– build submenu items
for i=1, #xml.channel.item do
local item = xml.channel.item[i]
tbl[#tbl+1] = { id="", text=item.title, iscategory=false, callback=nil, dataitem=nil, index=nil, }
end

– return the submenu content
func( tbl )
end

– go read the rss and it will execute the GetData function callback when the network request responds
readBbcNews( “bbcnewsrss”, GetData )
end

function PerformSearch( parentitem, term, callback )
local list = {}
for i=1, math.random(1, 7) do
list[#list+1] = { id="", text=term…’ '…math.random(100,999), iscategory=false, dataitem=nil }
end
print(‘Searching…’)
callback( list )
end

function RefreshRootMenu( parentitem, callback )
print(‘Refreshing…’)
callback( rootmenu )
end

rootmenu = {
title=“Products”, defaultscroll=2, hasindex=true, hasscrollbar=true, canscrolltotop=true,
pulltorefresh=RefreshRootMenu, – issearch=true, dataitem=PerformSearch,
{ id="", text=“Search Bar”, issearch=true, dataitem=PerformSearch, pulltorefresh=PerformSearch },
{ id="", text=“Find”, iscategory=true, dataitem=nil, index=“F” },
{ id="", text=“By Product”, iscategory=false, dataitem=byproduct, candelete=true },
{ id="", text=“By Job”, iscategory=false, dataitem=byjobfunction, candelete=true },
{ id="", text=“By Region”, iscategory=false, dataitem=byregion, candelete=true },
{ id="", text=“Search”, iscategory=true, dataitem=nil, index=“S” },
{ id="", text=“Companies With…”, iscategory=false, dataitem=function(d,f) f(GetTestSubMenu()) end },
{ id="", text=“Info”, iscategory=true, dataitem=nil, index=“I” },
{ id="", text=“About…”, iscategory=false, dataitem=nil },
{ id="", text=“Magazines”, iscategory=true, dataitem=nil, index=“M” },
{ id="", text=“The Times”, iscategory=false, dataitem=nil },
{ id="", text=“The Independent”, iscategory=false, dataitem=nil },
{ id="", text=“The Sun”, iscategory=false, dataitem=nil },
{ id="", text=“News Feeds”, iscategory=true, dataitem=nil, index=“N” },
{ id="", text=“BBC News”, iscategory=false, dataitem=GetBbcNewsRss },
}[/lua]
menu.lua:
[lua]-- menu

function newMenu( parent, rootdata, x, y, width, height )
– renders a title bar for a submenu
local function newTopBar( parent, title, scrollToBack, scrollToTop )
– local this bar
local group = display.newGroup()
group.widgets = {}
parent:insert( group )

– clean out the widgets
function group:clean()
for i=#group.widgets, 1, -1 do
group.widgets[i]:removeSelf()
group.widgets[i] = nil
end
group:removeSelf()
end

– status bar touch pad
if (scrollToTop) then
local touchbar = display.newRect( group, 0, 0, display.contentWidth, display.statusBarHeight )
function touchbar:touch( event )
if (event.phase == “began” and event.y <= display.statusBarHeight) then
scrollToTop()
end
return true
end
touchbar:addEventListener( “touch”, touchbar )
end

– create a gradient for the top-half of the toolbar
local toolbarGradient = graphics.newGradient( {168, 181, 198, 255 }, {139, 157, 180, 255}, “down” )

– create toolbar to go at the top of the screen
local titleBar = widget.newTabBar{
top = display.statusBarHeight,
topGradient = toolbarGradient,
bottomFill = { 117, 139, 168, 255 },
height = 44, width = 320
}
group:insert( titleBar.view )
group.widgets[#group.widgets+1] = titleBar

– create embossed text to go above toolbar
local titleText = display.newEmbossedText( title, 0, 0, native.systemFontBold, 20, { 255 } )
titleText:setReferencePoint( display.CenterReferencePoint )
titleText.x = display.contentWidth * 0.5 – (display.contentWidth * 0.5) – + x
titleText.y = 44 – + (titleBar.y + titleBar.height * 0.5)
group:insert( titleText )
group.widgets[#group.widgets+1] = titleText

– onRelease listener for back button
local function onBackRelease( event )
if (scrollToBack) then
scrollToBack( group.clean )
end
return true
end

– create ‘back’ button to be placed on toolbar
if (parent.parent.numChildren > 1) then
local backButton = widget.newButton{
label = “Back”,
left = 5, top = 28,
style = “backSmall”,
onRelease = onBackRelease
}
group:insert( backButton.view )
group.widgets[#group.widgets+1] = backButton
end
end – newTopBar

– renders a search input box for a search panel
local function newSearchBox( parent, cancel, scrollToTop, searchCallback )
– local this bar
local group = display.newGroup()
group.widgets = {}
parent:insert( group )

– declare the input text box
local inputbox = nil

– clean out the widgets
function group:clean()
for i=#group.widgets, 1, -1 do
group.widgets[i]:removeSelf()
group.widgets[i] = nil
end
group:removeSelf()
end

– status bar touch pad
if (scrollToTop) then
local touchbar = display.newRect( group, 0, 0, display.contentWidth, display.statusBarHeight )
function touchbar:touch( event )
if (event.phase == “began” and event.y <= display.statusBarHeight) then
scrollToTop()
end
return true
end
touchbar:addEventListener( “touch”, touchbar )
end

– search box image
local searchimg = display.newImage( group, “searchcancel.png” )
searchimg.xScale, searchimg.yScale = .5, .5
searchimg.x, searchimg.y = display.contentCenterX, searchimg.height/2-2

– onRelease listener for back button
local function onCancelRelease( event )
if (cancel) then
cancel( group.clean )
end
return true
end

– create ‘cancel’ button to be placed on toolbar
if (parent.parent.numChildren > 1) then
local cancelButton = widget.newButton{
label = “”,
left = 260, top = 27,
–style = “backSmall”,
default=“defaultcancel.png”,
over=“overcancel.png”,
width=110/2, height=58/2,
onRelease = onCancelRelease
}
group:insert( cancelButton.view )
group.widgets[#group.widgets+1] = cancelButton
end

– input box listener function
local function textListener( event )
if (event.phase == “editing”) then
searchCallback( event.text )
end
return true
end

– create native text input box
inputbox = native.newTextBox( 30, 30, 200, 25, textListener )
group.widgets[#group.widgets+1] = inputbox
inputbox.isEditable = true
inputbox.align = “left”
inputbox.size = 16

– return the text input box so pull to refresh can work
return inputbox
end – newSearchBox

– creates the submenu or search panel
local function newSubMenu( contentdata, issearch )
– validate
issearch = issearch or false

– container for all submenu components
local submenugroup = display.newGroup()
parent:insert( submenugroup )

– timers etc
local list = nil – table view containing the items in the menu (the important widget)
local enterFrame = nil – will be called with the regular enterFrame event to call other functions which check and refresh visual elements
local scrollCheck = nil – called by enterframe to check the scroll position of the tableview content
local adjustScrollBar = nil – called by enterframe to check the position of the tableview content and update the scrollbar size and position
local pullToRefresh = nil – function called to refresh the content

– position the submenugroup
if (parent.numChildren > 1 and issearch == true) then
submenugroup.x, submenugroup.y = parent[parent.numChildren-1].x, parent[parent.numChildren-1].y
else
submenugroup.x, submenugroup.y = (parent.numChildren-1) * display.contentWidth, 0
end

– create tableView widget
list = widget.newTableView{
top = display.statusBarHeight + 44,
width = display.contentWidth,
height = 366,
maskFile = “assets/mask-320x366.png”
}
print(‘list1’,list)

– keeps track of the number of rows
list.totalRowCount = 0
– keeps track of the total height of the widget content
list.totalheight = 0

– remove this if using later than build 2012.826
function list:deleteAllRows()
while (self.totalRowCount > 0) do
self:deleteRow( 1 )
self.totalRowCount = self.totalRowCount - 1
end
end

– list of scroll index items
local function populateTableView( menudata, isnewdata )
local scroll = {}

– onEvent listener for the tableView
local function onRowTouch( event )
local list = event.tableView
local row = event.target
local rowGroup = event.view

print(‘list6’,list)

if (not row.isCategory) then
if (event.phase == “swipeLeft” and menudata[event.index].candelete == true) then
transition.to( rowGroup.del, {time=rowGroup.del.transtime,maskX=-(rowGroup.del.width/2+1),onComplete=rowGroup.del.setActive} )
elseif (event.phase == “swipeRight” and menudata[event.index].candelete == true) then
transition.to( rowGroup.del, {time=rowGroup.del.transtime,maskX=rowGroup.del.width/2+1,onComplete=rowGroup.del.setInactive} )
elseif (event.phase == “release” and not row.isCategory) then
row.reRender = true
print( “You touched row #” … event.index…" - ‘"…menudata[event.index].text…"’" )

– do population of submenu
local menuitem = menudata[event.index]

if (menuitem.dataitem ~= nil) then
– get the submenu’s data and add the new sub menu
– this will shift the parent group left to show a deeper submenu
local dataitem = menuitem.dataitem

– populate the submenu (dataitem passed in is the submenu table of rows)
local function callback( dataitem )
– at this point if menuitem.issearch==true then dataitem==menuitem
– which means if the menu item is a search then the contentdata
– for the next newsubmenu call is the parent menu item and not the submenu table
– create the submenu or search item tableView
newSubMenu( dataitem, menuitem.issearch )

– use animation to show the next tableView
if (menuitem.issearch == true) then
– lay the search panel over the top of the current tableView menu (should be improved later to look like the smooth contacts list animation)
– ie: right now, do nothing (the search will just snap into place over the current tableView widget)
else
– slide the current tableView widget left to show the submenu animating into position
transition.to( parent, {time=300, x=parent.x-display.contentWidth} )
transition.to( topGroup, {time=300, x=parent.x-display.contentWidth} )
end
end

– if dataitem is a function, call it to get the data to put in the submenu, otherwise just populate the submenu
– if the dataitem is a function it must callback the passed in function to populate the submenu, this allows for network traffic delays
– the callback function does not need to be called, if the dataitem function wants to switch to another tab, for example
if (type(dataitem) == “function”) then
– if the item is a search, populate it with the initial search term
if (menuitem.issearch == true) then
– call the client function as an search
– callback( dataitem( {phase=“began”, term=""} ) )
callback( menuitem )
else
– call the client function with the parent item to get the submenu items…
– pullToRefresh = function()
dataitem( menuitem, callback )
– end
– pullToRefresh()
end
elseif (type(dataitem) == “table”) then
– populate directly because the submenu items are known here
callback( dataitem )
end
end
end
end

return true
end – onRowTouch

– onRender listener for the tableView
local function onRowRender( event )
local list = event.tableView
local row = event.target
local rowGroup = event.view
local textFunction = display.newRetinaText
if row.isCategory then textFunction = display.newEmbossedText; end

– add pulltorefresh
if (event.index == 1 and type(pullToRefresh) == “function”) then
print(‘Add Pull To Refresh’,list)
local refreshgroup = display.newGroup()
list.refreshgroup = refreshgroup
rowGroup:insert( refreshgroup )
refreshgroup.x, refreshgroup.y = 0, -64
local rect = display.newRect( refreshgroup, 0, 0, display.contentWidth, 64 )
–rect:setFillColor(255,0,0)
local refreshimg = display.newImage( refreshgroup, “refresh.png” )
refreshimg.x, refreshimg.y = 50, 25
refreshimg.xScale, refreshimg.yScale = .5, .5
local refreshtext = display.newText( refreshgroup, “Pull to refresh”, 100, 15, native.systemFont, 16 )
refreshtext:setTextColor( 180, 180, 180 )
local releasetext = display.newText( refreshgroup, “Release to refresh”, 100, 15, native.systemFont, 16 )
releasetext:setTextColor( 180, 180, 180 )
releasetext.isVisible = false

rect.willRefresh = false

list.refreshgroup.indicateRefresh = function()
if (not rect.willRefresh) then
rect.willRefresh = true
–rect:setFillColor(0,255,0)
transition.to( refreshimg, { time=200, rotation=-180 } )
refreshtext.isVisible = false
releasetext.isVisible = true
end
end
list.refreshgroup.isRefreshRequested = function()
return rect.willRefresh
end
list.refreshgroup.resetRefresh = function()
rect:setFillColor(255,0,0)
rect.willRefresh = false
refreshimg.rotation = 0
refreshtext.isVisible = true
releasetext.isVisible = false
end
end

if (event.index == 1 and menudata[event.index].issearch == true) then
– is a search bar
row.searchimg = display.newImage( rowGroup, “searchwith.png” )
row.searchimg.xScale, row.searchimg.yScale = .5, .5
row.height = row.searchimg.height/2
row.searchimg.x, row.searchimg.y = display.contentCenterX, row.searchimg.height/4
else
– is regular bar
row.title = textFunction( menudata[event.index].text, 12, 0, native.systemFontBold, 16 )
row.title:setReferencePoint( display.CenterLeftReferencePoint )
row.title.y = row.height * 0.5

if (not row.isCategory) then
row.title.x = 15
row.title:setTextColor( 0 )

local del = nil – the image sitting over the top of the button widget

local onButtonEvent = function (event )
print(‘del.isactive’,del.isactive)
if (not del.isactive) then
return false
end

if (event.phase == “release”) then
print( “You pressed and released a button!” )
end
return true
end

local myButton = widget.newButton{
id = “btn001”,
left = 225,
top = 9,
label = “”,
width = 90, height = 46,
cornerRadius = 8,
onEvent = onButtonEvent,
default=“deleteback.png”,
over=“deleteback.png”,
}
rowGroup:insert(myButton.view)

del = display.newImage(rowGroup,“deletebtn.png”)
del.x, del.y = 270, 32
del.xScale, del.yScale = .7, .7
del.isactive = false
rowGroup.del = del

local mask = graphics.newMask(“slidemask.png”)
del:setMask( mask )

del.maskScaleX, del.maskScaleY = -1, 1
del.maskX = del.width/2+1
del.maskY = 100

del.transtime = 300

function del:setActive()
print(‘active’,deletetap)
del.isactive = true
end
function del:setInactive()
print(‘inactive’)
del.isactive = false
end
end

– must insert everything into event.view:
rowGroup:insert( row.title )
end
end – onRowRender

– assign data to the rows
local function createContent()
– defaults
local defaultRowColor = { 174, 183, 190, 255 }
local defaultLineColor = {0,0,0,255}

– Add rows
print(‘list3.1’,list)
for i=1, #menudata do
local rowHeight, rowColor, lineColor, isCategory = 64
local menuitem = menudata[i]

if (menuitem.iscategory) then
isCategory = true; rowHeight = 24; rowColor=defaultRowColor; lineColor=defaultLineColor
end

– accumulate the total height of the content rows
list.totalheight = list.totalheight + rowHeight
list.totalRowCount = list.totalRowCount + 1

– insert the row into the tableView widget
print(‘list3.2’,list)
list:insertRow{
onEvent=onRowTouch,
onRender=onRowRender,
height=rowHeight,
isCategory=isCategory,
rowColor=rowColor,
lineColor=lineColor,
issearch=true
}

– index can be a string or a function to return a string - string can be one char
if (menuitem.index ~= nil) then
local scrollitem = menuitem.index
if (type(scrollitem) == “function”) then
scrollitem = scrollitem( menuitem )
end
scroll[#scroll+1] = { index=i, char=scrollitem }
end
end

local function addAlphaStrip(parent, callback)
local strip = display.newGroup()
parent:insert( strip )

strip.x, strip.y = display.contentWidth - 15, display.statusBarHeight+50
local alpha = display.newRoundedRect( strip, -10, 0, 20, display.contentHeight - 128, 10 )
alpha:setFillColor( 224, 224, 224 )
alpha.isVisible = false
alpha.isHitTestable = true

local size = 13.3
for i=65, 90 do
local index = i-64

local function touch(event)
alpha.isVisible = (event.phase == “began” or event.phase == “moved”)

for s=1, #scroll do
if (scroll[s].char == string.char(i)) then
list:scrollToIndex( scroll[s].index, 300 )
break
end
end
return true
end

local rect = display.newRect( strip, 0, 0, 40, size )
rect.x, rect.y = 0, 10+(i-65)*size
rect.alpha = 0
rect.isHitTestable = true

local letter = display.newText(strip, string.char(i), 0, 0, native.systemFont, 10)
letter:setTextColor(106, 115, 125)
letter.x, letter.y = 0.5, 10+(i-65)*size

rect:addEventListener( “touch”, touch )
end
end
if (isnewdata and menudata.hasindex) then addAlphaStrip( submenugroup ) end

– auto scroll to initial position
if (menudata.defaultscroll) then list:scrollToIndex( menudata.defaultscroll, 0 ) end

local currentpos = list:getScrollPosition()
list.maxscrolltop = 366-list.totalheight+24

–[[scroll bar handling]]–
local function addScrollBar()
local scrollbar = display.newRoundedRect( submenugroup, display.contentWidth - 5, display.statusBarHeight+50, 2.5, display.contentHeight - 128, 2.5 )
scrollbar:setFillColor( 255,0,0 )
scrollbar.lastchangetime = system.getTimer()
adjustScrollBar = function( event )
if (list.totalheight < 366) then
scrollbar.alpha = 0
else
local starty = scrollbar.y
– check scroll position and adjust
local scrollpos = list:getScrollPosition()
if (scrollpos <= 0 and scrollpos >= list.maxscrolltop) then
local heightperc = (346 / list.totalheight) * 100
local scrollperc = (math.abs(scrollpos / math.abs(list.maxscrolltop))) * 100

scrollbar.height = 346/100*heightperc
scrollbar.y = 10 + display.statusBarHeight + 44 + (scrollbar.height/2)+((346-scrollbar.height)/100*scrollperc)
elseif (scrollpos > 0) then
local scrollperc = (scrollpos / 366) * 100
local heightperc = (346 / list.totalheight) * 100
heightperc = heightperc - scrollperc

scrollbar.height = 346/100*heightperc
scrollbar.y = 10 + display.statusBarHeight + 44 + (scrollbar.height/2)
elseif (scrollpos < list.maxscrolltop) then
local scrollperc = ((list.maxscrolltop - scrollpos) / 366) * 100
local heightperc = (346 / list.totalheight) * 100
heightperc = heightperc - scrollperc

scrollbar.height = 346/100*heightperc
scrollbar.y = 10 + display.statusBarHeight + 44 + (346 - (scrollbar.height/2))
end
– fade out the scrollbar
if (starty ~= scrollbar.y) then
scrollbar.lastchangetime = system.getTimer()
end
if (system.getTimer() - scrollbar.lastchangetime > 500) then
if (scrollbar.fadeintrans ~= nil) then
transition.cancel( scrollbar.fadeintrans )
scrollbar.fadeintrans = nil
end
if (scrollbar.fadeouttrans == nil) then
scrollbar.fadeouttrans = transition.to( scrollbar, { time=250, alpha=0 } )
end
else
if (scrollbar.fadeouttrans ~= nil) then
transition.cancel( scrollbar.fadeouttrans )
scrollbar.fadeouttrans = nil
end
if (scrollbar.fadeintrans == nil) then
scrollbar.fadeintrans = transition.to( scrollbar, { time=150, alpha=1 } )
end
end
end
end – adjustScrollBar
–Runtime:addEventListener( “enterFrame”, adjustScrollBar ) – called by the more global enterFrame(event) function
end – addScrollBar
if (isnewdata and menudata.hasscrollbar) then addScrollBar() end
end – createContent

– clear current tableView content
list:deleteAllRows()
list.totalRowCount = 0
list.totalheight = 0

– create the content
print(‘list5’,list)
createContent()
end – populateTableView

– scroll to top function
if (contentdata.canscrolltotop) then
submenugroup.scrollToTop = function()
if (contentdata.defaultscroll) then
list:scrollToIndex( contentdata.defaultscroll, 400 )
else
list:scrollToIndex( 1, 400 )
end
end
end

– pull to refresh check
scrollCheck = function()
local currentpos = list:getScrollPosition()
– print(list,contentdata.title,contentdata.text,contentdata.canscrolltotop,type(contentdata),contentdata.pulltorefresh,enterFrame)
if (contentdata.pulltorefresh) then
if (pullToRefresh and list.totalRowCount > 0) then
– print(‘currentpos’,currentpos,pullToRefresh)
if (currentpos >= 64) then
list.refreshgroup:indicateRefresh()
elseif (currentpos >= -1 and currentpos <= 1 and list.refreshgroup:isRefreshRequested()) then
list.refreshgroup:resetRefresh()
pullToRefresh()
end
end
end
end

– if this is a search panel provide the search bar at the top, if not provide a title bar
if (issearch == true) then
– is a search panel
– stops the search and removes the search panel
local function cancelSearch( onComplete )
Runtime:removeEventListener( “enterFrame”, enterFrame )
onComplete()
list:removeSelf()
list = nil
submenugroup:removeSelf()
print(‘cancel’)
end

– gets the search results from the search callback and populates the tableview
local function searchCallback( searchterm )
– called by the menu’s search function to populate the tableView
function doPopulate( list )
populateTableView( list, false )
end

– call the menu’s search function to get the search results table
contentdata.dataitem( contentdata, tostring(searchterm), doPopulate )
end

– create search bar
local textbox = newSearchBox( submenugroup, cancelSearch, submenugroup.scrollToTop, searchCallback )

– called by the pull to refresh function
pullToRefresh = function()
– get the current search text and call the data retrieval function
searchCallback( textbox.text )
end
else
– is a regular sub/menu
– scroll to origin - this is actually the back button effect
local function scrollToBack( onComplete )
function clean()
Runtime:removeEventListener( “enterFrame”, enterFrame )
onComplete()
list:removeSelf()
list = nil
submenugroup:removeSelf()
end
transition.to( parent, {time=300, x=parent.x+display.contentWidth, onComplete=clean} )
end

– create title bar
newTopBar( submenugroup, contentdata.title, scrollToBack, submenugroup.scrollToTop )

– if there is a refresh function provided the callback
if (type(contentdata.pulltorefresh) == “function”) then
pullToRefresh = function()
– called by the menu’s search function to populate the tableView
function doPopulate( list )
populateTableView( list, false )
end

– call the menu’s refresh function to get the content table
contentdata.pulltorefresh( contentdata, doPopulate )
end
end
end – (issearch == true)

– insert widget into demoGroup
submenugroup:insert( list.view )
print(‘list4’,list)

– populate the tableView with initial data
populateTableView( contentdata, true )

– call the refresh functions
enterFrame = function( event )
if (scrollCheck) then scrollCheck() end
if (adjustScrollBar) then adjustScrollBar() end
end

– start the enterframe listener to call refresh functions
Runtime:addEventListener( “enterFrame”, enterFrame )
print(‘list2’,list)
end – newSubMenu

newSubMenu( rootdata, rootdata.issearch )
end[/lua]

Matt. [import]uid: 8271 topic_id: 27589 reply_id: 112070[/import]

Great stuff matt. If you keep this up you could end up with your own widget library.

Time for source control maybe? Github or maybe bitbucket?
[import]uid: 84637 topic_id: 27589 reply_id: 112167[/import]

I’m aiming for a widget library…

Unfortunately, github is blocked here and doesn’t play well with my home solution. I know, whinge whinge whinge :wink:

I’ll look into it though. Anything which works over http will have a chance of success.

I also have some minor fixes to post, so it’s a good time to try. [import]uid: 8271 topic_id: 27589 reply_id: 112174[/import]

If you can’t use github, bitbucket is great. I use it personally and highly recommend it. It’s accompanying program “SourceTree” (mac) makes the whole thing a breeze [import]uid: 84637 topic_id: 27589 reply_id: 112177[/import]

Well, here’s a simple solution (after all of the above failed on my current network)…

http://dl.dropbox.com/u/10254959/WidgetDemo.2012-06-18.001.zip [import]uid: 8271 topic_id: 27589 reply_id: 112185[/import]