diff --git a/widgetLibrary/editfield_calibrationdb.lua b/widgetLibrary/editfield_calibrationdb.lua new file mode 100644 index 0000000..0886a98 --- /dev/null +++ b/widgetLibrary/editfield_calibrationdb.lua @@ -0,0 +1,15 @@ +local M = + +{ +["device-Nexus 7"] = { +fontScale = 1.77, +textFieldHeightAdjust = 18, +textFieldXOffset = 3, +textFieldYOffset = -7, +labelXoffset = 0, +labelYoffset = 0, +}, +} + +return M + diff --git a/widgetLibrary/editfield_defaults.lua b/widgetLibrary/editfield_defaults.lua new file mode 100755 index 0000000..59030c4 --- /dev/null +++ b/widgetLibrary/editfield_defaults.lua @@ -0,0 +1,154 @@ + +local clbData = require("widgets.editfield_calibrationdb") + +local M = {} +M.androidKeyboardHeight = 350 + +if "Android" == system.getInfo("platformName") then + M.textFieldHeightAdjust = 18 + M.textLineAdjust = 0 + M.textFieldYOffset = - (M.textFieldHeightAdjust / 3) + M.textFieldXOffset = -1 + M.fakeLabelYOffset = 0 + M.fakeLabelXOffset = 0 + +elseif "simulator" == system.getInfo("environment") then + M.textLineAdjust = 0 + M.textFieldYOffset = -1 + M.textFieldXOffset = 0 + M.fakeLabelYOffset = 0 + M.fakeLabelXOffset = 1 + M.textFieldHeightAdjust = 0; +else + M.textLineAdjust = 0 + M.textFieldYOffset = 0 + M.textFieldXOffset = 0 + M.fakeLabelYOffset = M.textLineAdjust / 2 + M.fakeLabelXOffset = 0 + M.textFieldHeightAdjust = 0; + +end +function M:calcFontSize() + local environment = system.getInfo("environment") + --default scaing for simulator + local fontScale = (( display.contentWidth - (display.screenOriginX * 2) ) / display.contentScaleX) / display.contentWidth + if system.getInfo("platformName") == "Android" then + --default font scaling for all versions + fontScale = (display.pixelWidth / display.contentWidth) * 0.5; + --check if its an Android version 2 device + local osVersion = system.getInfo("platformVersion") + if osVersion then + local pos = string.find(osVersion,"2.") + if pos == 1 then + fontScale = 1 + end + end + elseif environment == "simulator" then + --in Corona simulator, check if its a tall device, which is probably Zoomed out + if display.pixelHeight > 800 then + fontScale = fontScale * .5 + end + else + if string.match(system.getInfo("model"),"iPa") then + --iPads + if display.pixelHeight == 2048 then + --iPad retina + fontScale = (display.pixelHeight / display.contentHeight) * 0.5; + else + fontScale = 1 / display.contentScaleY + end + else + fontScale = (display.pixelHeight / display.contentHeight) * 0.5; + end + + end + + return fontScale + +end + +function M:robMiracleCalcFontSize() + return ( display.pixelWidth / display.contentWidth ) * 0.5 +end + +function M:mpappasCalcFontSize() + local sysType = system.getInfo("architectureInfo") + + local fontScale = 1 / display.contentScaleY -- old method is fallback (ends up being sim setting) + + if system.getInfo("platformName") == "Android" or not string.match(system.getInfo("model"),"iP") then -- try and get the real width / height and deal with it + --print(" -- -- Android font size calc...") + + local missingInches = display.screenOriginY * -2 -- account for both top and bottom area (x2), and change the sign sign since originY is always negative, if it exists... + + missingInches = missingInches / 320 -- 320 is 1 inch, in my 640x960 virual content space (960 == 3 inches for my app, designed for a standard iphone 4 display) + -- different contentScale sizes will use a different virtual pixel / inch value (eg, 320x480 virtual content = missingInches / 160) + --print(" -- missing inches == ", missingInches) + + local heightIn = system.getInfo( "androidDisplayHeightInInches" ) + local widthIn = system.getInfo( "androidDisplayWidthInInches" ) + --print(" -- original height == ", heightIn) + if heightIn ~= nil then + heightIn = heightIn - missingInches + + --Make sure its not nil... e.g. the simulator will return nil, perhaps some bad devices will too... + + -- heightIn is height of actual droid app is running on + fontScale = heightIn / 3.0 -- 3.0 is actual iPhone 3gs /4 /4s height + --print(" -- fontsize set on actual droid screen inch height") + --print(" -- widthIn = ", widthIn) + --print(" -- heightIn = ", heightIn) + else + fontScale = 4/3 --Default to a 4 inch diagonal (iphone is 3.0) + --print(" -- fontsize set to 4 inch phone size") + end + + + else + local fontScale = 1 + local heightIn = 3 -- default to stnadard iPhone size + local missingInches = display.screenOriginY * -2 -- account for both top and bottom area (x2), and change the sign sign originY is negative, if it exists... + + missingInches = missingInches / 320 -- 320 is 1 inch, in my 640x960 virual content space (960 == 3 inches for my app, designed for a standard iphone 4 display) + -- different contentScale sizes will use a different virtual pixel / inch value (eg, 320x480 virtual content = missingInches / 160) + -- + -- Since there is no corona facility to get the screen height of the device on iOS, we'll determine it ourselves. + -- + if( sysType == "iPhone2,1" ) then -- 3GS + heightIn = 3.0 - missingInches -- original iPhone size + elseif( string.match(sysType, "iPhone3") or string.match(sysType, "iPhone4") ) then -- iPhone 4, 4S + heightIn = 3.0 - missingInches -- Same size as the old 3gs- better resolution, but same size + elseif( string.match(sysType, "iPhone5") ) then -- iPhone 5 + heightIn = 4.0 - missingInches -- Should go back to 3, after missing inches chopped off by corona are deducted. + elseif sysType == "iPad2,5" or sysType == "iPad2,6" or sysType == "iPad2,7" or sysType == "iPad2,8" then -- 2,8 is unknown, just a guess at next mini # + heightIn = 6.25 - missingInches -- mini + elseif( string.match(sysType, "iPad") ) then -- standard iPad + heightIn = 7.75 - missingInches -- iPad + else + if( missingInches == 0 ) then + -- iPod falls through to here (and it is 3 inches tall, and any missing inches accounted for in a tall version, so that works for now) + heightIn = 3.0 - missingInches -- I would hope this is typical, zero extra on an unknown ios device + else + heightIn = 4.0 - missingInches -- otherwise, we'll take a flying guess that the unknown device is bigger than a tiny breadbox, and smaller than a car. (4 inches tall, iphone 5 format) + end + end + fontScale = heightIn / 3.0 -- 3.0 inches is actual iPhone 4 height (that my content is based on) + end + return fontScale + +end + +M.fontScale = M:calcFontSize() + +local calibration = clbData[system.getInfo( "environment").."-".. system.getInfo("model")] +if calibration then + M.fontScale = calibration.fontScale + M.textFieldHeightAdjust = calibration.textFieldHeightAdjust + M.textFieldYOffset = calibration.textFieldYOffset + M.textFieldXOffset = calibration.textFieldXOffset + M.fakeLabelYOffset = calibration.labelYoffset + M.fakeLabelXOffset = calibration.labelXoffset +end + +return M + diff --git a/widgetLibrary/storyboardext.lua b/widgetLibrary/storyboardext.lua new file mode 100644 index 0000000..e6962d6 --- /dev/null +++ b/widgetLibrary/storyboardext.lua @@ -0,0 +1,82 @@ +local storyboard = require("storyboard") + +function storyboard.getCurrentScene() + local sceneName = storyboard.getCurrentSceneName() + if sceneName and string.len(sceneName) > 0 then + return storyboard.getScene(sceneName) + else + return nil + end +end + + +local oldNewScene = nil; +local function newScene() + local scene = oldNewScene() + scene._editFields = {} + + function onExitScene( event ) + native.setKeyboardFocus(nil) + end + + function onEnterScene( event ) + native.setKeyboardFocus(nil) + end + + function onCreateScene( event ) + native.setKeyboardFocus(nil) + end + + function scene.addEditField(field) + scene._editFields [field] = field; + end + + function scene.removeEditField(field) + scene._editFields [field] = nil; + end + + function scene.validateEditFields() + local retVal = {} + for i,v in pairs(scene._editFields) do + if i:validate() then + table.insert(retVal, i) + end + end + if #retVal > 0 then + return retVal + else + return nil + end + end + + function scene.isModified() + local retVal = {} + for i,v in pairs(scene._editFields) do + if i:isModified() then + table.insert(retVal, i) + end + end + if #retVal > 0 then + return retVal + else + return nil + end + end + function scene.setIsModified(value) + for i,v in pairs(scene._editFields) do + i:setIsModified(value) + end + end + + + scene:addEventListener( "exitScene", onExitScene ) + scene:addEventListener( "enterScene", onEnterScene ) + scene:addEventListener( "createScene", onCreateScene ) + return scene; +end +if oldNewScene ~= newScene then + oldNewScene = storyboard.newScene +end + +storyboard.newScene = newScene; + diff --git a/widgetLibrary/widget_button.lua b/widgetLibrary/widget_button.lua index 873e2e3..85983db 100755 --- a/widgetLibrary/widget_button.lua +++ b/widgetLibrary/widget_button.lua @@ -47,6 +47,20 @@ else buttonDefault = { default = { 0, 0, 0 }, over = { 1, 1, 1 } } end +local shapeStrokeDefault; +if isGraphicsV1 then + shapeStrokeDefault = { default = { 0, 0, 0 }, over = { 0, 0, 0 } } +else + shapeStrokeDefault = { default = { 0, 0, 0 }, over = { 0, 0, 0 } } +end + +local shapeFillDefault; +if isGraphicsV1 then + shapeFillDefault = { default = { 204, 204, 204 }, over = { 153, 153, 153} } +else + shapeFillDefault = { default = { 0.8, 0.8, 0.8 }, over = { 0.6, 0.6, 0.6 } } +end + -- Function to handle touches on a widget button, function is common to all widget button creation types (ie image files, imagesheet, and 9 slice button creation) local function manageButtonTouch( view, event ) local phase = event.phase @@ -140,30 +154,68 @@ end -- Text only button ------------------------------------------------------------------------ local function createUsingText( button, options ) + -- Create a local reference to our options table local opt = options -- Forward references local view - + + local rect = nil; + local shape = opt.shape; + if shape then + + rect = display.newRoundedRect( button, button.x, button.y, opt.width , opt.height , shape.cornerRadius); + rect.strokeWidth = shape.strokeWidth or 1; + rect:setStrokeColor(unpack(shape.strokeColor.default)); + rect:setFillColor(unpack(shape.fillColor.default)); + end + local viewLabel; + -- Create the label (either embossed or standard) if opt.embossedLabel then - view = display.newEmbossedText( button, opt.label, 0, 0, opt.font, opt.fontSize ) + viewLabel = display.newEmbossedText( button, opt.label, 0, 0, opt.font, opt.fontSize ) else - view = display.newText( button, opt.label, 0, 0, opt.font, opt.fontSize ) + viewLabel = display.newText( button, opt.label, 0, 0, opt.font, opt.fontSize ) end + if rect then + view = rect; + view._shapeColor = shape.fillColor; + view._strokeColor = shape.strokeColor; + view._label = viewLabel; + + else + view = viewLabel; + end -- Set the view's color - view:setFillColor( unpack( opt.labelColor.default ) ) + viewLabel:setFillColor( unpack( opt.labelColor.default ) ) view._labelColor = opt.labelColor ---------------------------------- -- Positioning ---------------------------------- - -- The view - view.x = button.x + ( view.contentWidth * 0.5 ) - view.y = button.y + ( view.contentHeight * 0.5 ) + -- Labels position + if "center" == opt.labelAlign then + viewLabel.x = view.x + opt.labelXOffset + elseif "left" == opt.labelAlign then + viewLabel.x = view.x - ( view.contentWidth * 0.5 ) + ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + elseif "right" == opt.labelAlign then + viewLabel.x = view.x + ( view.contentWidth * 0.5 ) - ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + end + + + if "center" == opt.labelAlignY then + viewLabel.y = view.y + opt.labelYOffset + elseif "top" == opt.labelAlignY then + viewLabel.y = view.y - ( view.contentHeight * 0.5 ) + ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + elseif "bottom" == opt.labelAlignY then + + viewLabel.y = view.y + ( view.contentHeight * 0.5 ) - ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + end + + ------------------------------------------------------- -- Assign properties/objects to the view @@ -171,8 +223,8 @@ local function createUsingText( button, options ) view._isEnabled = opt.isEnabled view._pressedState = "default" - view._fontSize = opt.fontSize - view._labelColor = view._labelColor + viewLabel._fontSize = opt.fontSize + viewLabel._labelColor = view._labelColor -- Methods view._onPress = opt.onPress @@ -192,7 +244,11 @@ local function createUsingText( button, options ) -- Function to set the buttons text color function button:setFillColor( ... ) - self._view:setFillColor( ... ) + if self._label then + self._view._label:setFillColor( ... ) + else + self._view:setFillColor( ... ) + end; end -- Function to set the button's label @@ -220,9 +276,11 @@ local function createUsingText( button, options ) return true end + + view:addEventListener( "touch" ) - + ---------------------------------------------------------- -- PRIVATE METHODS ---------------------------------------------------------- @@ -230,8 +288,12 @@ local function createUsingText( button, options ) -- Function to set the button's label function view:_setLabel( newLabel ) -- Update the label's text - if "function" == type( self.setText ) then - self:setText( newLabel ) + if "function" == type( self.setText ) or self._label then + if self._label then + self._label:setText( newLabel ) + else + self:setText( newLabel ) + end else self.text = newLabel end @@ -239,17 +301,22 @@ local function createUsingText( button, options ) -- Function to get the button's label function view:_getLabel() - return self._label.text + return self._label.text end -- Function to set the buttons current state function view:_setState( state ) local newState = state - - if "over" == newState then + if "over" == newState then -- Set the label to it's over color if "table" == type( self ) then - self:setFillColor( unpack( self._labelColor.over ) ) + if self._label then + self:setFillColor( unpack( self._shapeColor.over ) ); + self:setStrokeColor(unpack( self._strokeColor.over ) ); + self._label:setFillColor( unpack( self._labelColor.over ) ) + else + self:setFillColor( unpack( self._labelColor.over ) ) + end end -- The pressedState is now "over" @@ -257,8 +324,15 @@ local function createUsingText( button, options ) elseif "default" == newState then -- Set the label back to it's default color + if "table" == type( self ) then - self:setFillColor( unpack( self._labelColor.default ) ) + if self._label then + self:setFillColor( unpack( self._shapeColor.default ) ) + self:setStrokeColor(unpack( self._strokeColor.default ) ); + self._label:setFillColor( unpack( self._labelColor.default ) ) + else + self:setFillColor( unpack( self._labelColor.default ) ) + end end -- The pressedState is now "default" @@ -1249,12 +1323,23 @@ function M.new( options, theme ) end opt.labelAlign = customOptions.labelAlign or "center" + opt.labelAlignY = customOptions.labelAlignY or "center" + opt.labelXOffset = customOptions.labelXOffset or 0 opt.labelYOffset = customOptions.labelYOffset or 0 opt.embossedLabel = customOptions.emboss or themeOptions.emboss or false opt.isEnabled = customOptions.isEnabled + opt.textOnlyButton = customOptions.textOnly or false - + opt.shape = customOptions.shape or nil; + if opt.shape then + + opt.shape.cornerRadius = opt.shape.cornerRadius or 0; + opt.shape.strokeWidth = opt.shape.strokeWidth or 1; + opt.shape.strokeColor = opt.shape.strokeColor or shapeStrokeDefault; + opt.shape.fillColor = opt.shape.fillColor or shapeFillDefault; + end; + -- If the user didn't pass in a isEnabled flag, set it to true if nil == opt.isEnabled then opt.isEnabled = true @@ -1307,7 +1392,7 @@ function M.new( options, theme ) opt.bottomMiddleOverFrame = customOptions.bottomMiddleOverFrame or _widget._getFrameIndex( themeOptions, themeOptions.bottomMiddleOverFrame ) -- Are we using a nine piece button? - local using9PieceButton = not opt.defaultFrame and not opt.overFrame and not opt.defaultFile and not opt.overFile and not opt.textOnlyButton and opt.topLeftFrame and opt.topLeftOverFrame and opt.middleLeftFrame and opt.middleLeftOverFrame and opt.bottomLeftFrame and opt.bottomLeftOverFrame and + local using9PieceButton = not opt.defaultFrame and not opt.overFrame and not opt.defaultFile and not opt.overFile and not opt.textOnlyButton and not opt.shape and opt.topLeftFrame and opt.topLeftOverFrame and opt.middleLeftFrame and opt.middleLeftOverFrame and opt.bottomLeftFrame and opt.bottomLeftOverFrame and opt.topRightFrame and opt.topRightOverFrame and opt.middleRightFrame and opt.middleRightOverFrame and opt.bottomRightFrame and opt.bottomRightOverFrame and opt.topMiddleFrame and opt.topMiddleOverFrame and opt.middleFrame and opt.middleOverFrame and opt.bottomMiddleFrame and opt.bottomMiddleOverFrame @@ -1384,7 +1469,6 @@ function M.new( options, theme ) baseDir = opt.baseDir, widgetType = "button", } - -- Create the button if using9PieceButton then -- If we are using a 9 piece button @@ -1401,7 +1485,7 @@ function M.new( options, theme ) end -- Text only button - if opt.textOnlyButton then + if opt.textOnlyButton or opt.shape then createUsingText( button, opt ) end end diff --git a/widgetLibrary/widget_editfield.lua b/widgetLibrary/widget_editfield.lua new file mode 100755 index 0000000..78923b4 --- /dev/null +++ b/widgetLibrary/widget_editfield.lua @@ -0,0 +1,1068 @@ +-- Copyright © 2014 Top Rank Software LLC. All Rights Reserved. +-- This library includes code developed by Corona Labs Inc. (http://www.coronalabs.com). +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- * Neither the name of the company nor the names of its contributors +-- may be used to endorse or promote products derived from this software +-- without specific prior written permission. +-- * Redistributions in any form whatsoever must retain the following +-- acknowledgment visually in the program (e.g. the credits of the program): +-- 'This product includes software developed by Top Rank Software LLC' +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +-- DISCLAIMED. IN NO EVENT SHALL CORONA LABS INC. BE LIABLE FOR ANY +-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-- Require needed widget files +local _widget = require( "widget" ) +local _storyboard = require("storyboard") +local efDefaults = require("widgets.editfield_defaults") +local version = "1.03" + +local M = +{ + _options = {}, + _widgetName = "widget.newEditField", +} + + +local isGraphicsV1 = ( 1 == display.getDefault( "graphicsCompatibility" ) ) +local isByteColorRange = display.getDefault( "isByteColorRange" ) + +local labelDefault = { 0, 0, 0 } + +local frameStrokeDefault; +if isByteColorRange then + frameStrokeDefault = { 0, 0, 0 } +else + frameStrokeDefault = { 0, 0, 0 } +end + + +local frameFillDefault; +if isByteColorRange then + frameFillDefault = { 255, 255, 255} +else + frameFillDefault = { 1, 1, 1 } +end + +local frameErrorStrokeDefault; +if isByteColorRange then + frameErrorStrokeDefault = { .9, 0, 0 } +else + frameErrorStrokeDefault = { 240, 0, 0 } +end + +local frameErrorFillDefault; +if isByteColorRange then + frameErrorFillDefault = { 0, 0, 0, 0 } +else + frameErrorFillDefault = { 0, 0, 0, 0 } +end + + + +local _focusedField = nil; + +local function getKeyboardHeight() + local function getActualHeight() + local function isLandscape() + return system.orientation == "landscapeLeft" or + system.orientation == "landscapeRight" + end + local model = system.getInfo("model") + if model == "iPad" then + if isLandscape() then + return 352 + else + return 264 + end + elseif model == "iPhone" or + model == "iPod" then + if isLandscape() then + return 162 + else + return 216 + end + + else + --dear android + return math.min(efDefaults.androidKeyboardHeight, display.contentHeight*efDefaults.fontScale / 2); + end + end + + return getActualHeight()/efDefaults.fontScale; +end +-- Creates a new edit field from an image +local function initEditField( editField, options ) + + local opt = options + + + + local themeData = require( opt.themeData ) + + local yCenter = opt.height / 2 + local imageSheet + + --default values for start, end + local xStart = 0; + local xEnd = opt.width + local height, width = opt.height, opt.width + local function create9SliceFrame() + local viewTopLeft, viewMiddleLeft, viewBottomLeft + local viewTopMiddle, viewMiddle, viewBottomMiddle + local viewTopRight, viewMiddleRight, viewBottomRight + local sheet + + + local themeData = require( opt.themeData ) + sheet = themeData:getSheet() + imageSheet = graphics.newImageSheet( opt.themeSheetFile, sheet ) + + --top left + local index = themeData:getFrameIndex(opt.themeOptions.topLeftFrame) + local frame = sheet.frames[index] + viewTopLeft = display.newImageRect( editField, imageSheet, index, frame.width, frame.height ) + viewTopLeft.x = frame.width / 2 + viewTopLeft.y = frame.height / 2 + + --middle left + local index = themeData:getFrameIndex(opt.themeOptions.middleLeftFrame) + local frame = sheet.frames[index] + viewMiddleLeft = display.newImageRect( editField, imageSheet, index, frame.width, frame.height ) + viewMiddleLeft.x = frame.width / 2 + viewMiddleLeft.y = opt.height / 2 + + --bottom left + local index = themeData:getFrameIndex(opt.themeOptions.bottomLeftFrame) + local frame = sheet.frames[index] + viewBottomLeft = display.newImageRect( editField, imageSheet, index, frame.width, frame.height ) + viewBottomLeft.x = frame.width / 2 + viewBottomLeft.y = opt.height - frame.height / 2 + viewMiddleLeft.height = viewBottomLeft.y - viewBottomLeft.height / 2 - (viewTopLeft.y + viewTopLeft.height / 2) + + --top middle + local index = themeData:getFrameIndex(opt.themeOptions.topMiddleFrame) + local frame = sheet.frames[index] + viewTopMiddle = display.newImageRect( editField, imageSheet, index, frame.width, frame.height ) + viewTopMiddle.x = opt.width / 2 + viewTopMiddle.y = frame.height / 2 + + --top right + local index = themeData:getFrameIndex(opt.themeOptions.topRightFrame) + local frame = sheet.frames[index] + viewTopRight = display.newImageRect( editField, imageSheet, index, frame.width, frame.height ) + viewTopRight.x = opt.width - frame.width / 2 + viewTopRight.y = frame.height / 2 + viewTopMiddle.width = viewTopRight.x - viewTopRight.width / 2 - (viewTopLeft.x + viewTopLeft.width / 2) + + --middle right + local index = themeData:getFrameIndex(opt.themeOptions.middleRightFrame) + local frame = sheet.frames[index] + viewMiddleRight = display.newImageRect( editField, imageSheet, index, frame.width, frame.height ) + viewMiddleRight.x = opt.width - frame.width / 2 + viewMiddleRight.y = opt.height / 2 + + --bottom right + local index = themeData:getFrameIndex(opt.themeOptions.bottomRightFrame) + local frame = sheet.frames[index] + viewBottomRight = display.newImageRect( editField, imageSheet, index, frame.width, frame.height ) + viewBottomRight.x = opt.width - frame.width / 2 + viewBottomRight.y = opt.height - frame.height / 2 + viewMiddleRight.height = viewBottomRight.y - viewBottomRight.height / 2 - (viewTopRight.y + viewTopRight.height / 2) + + --bottom middle + local index = themeData:getFrameIndex(opt.themeOptions.bottomMiddleFrame) + local frame = sheet.frames[index] + viewBottomMiddle = display.newImageRect( editField, imageSheet, index, frame.width, frame.height ) + viewBottomMiddle.x = opt.width / 2 + viewBottomMiddle.y = opt.height - frame.height / 2 + viewBottomMiddle.width = viewBottomRight.x - viewBottomRight.width / 2 - (viewBottomLeft.x + viewBottomLeft.width / 2) + + --middle + local index = themeData:getFrameIndex(opt.themeOptions.middleFrame) + local frame = sheet.frames[index] + viewMiddle = display.newImageRect( editField, imageSheet, index, frame.width, frame.height ) + viewMiddle.x = opt.width / 2 + viewMiddle.y = opt.height / 2 + viewMiddle.width = viewMiddleRight.x - viewMiddleRight.width / 2 - (viewBottomLeft.x + viewBottomLeft.width / 2) + viewMiddle.height = viewBottomMiddle.y - viewBottomMiddle.height / 2 - (viewTopMiddle.y + viewTopMiddle.height / 2) + + xStart = viewMiddleLeft.contentWidth ; + xEnd = opt.width - viewMiddleRight.contentWidth ; + height = opt.height - (viewBottomMiddle.height + viewTopMiddle.height) + 2; + end + + local function create3SliceFrame() + -- Create the imageSheet + if opt.sheet then + imageSheet = opt.sheet + else + imageSheet = graphics.newImageSheet( opt.themeSheetFile, themeData:getSheet() ) + + end + local viewLeft, viewRight, viewMiddle + -- The left edge + viewLeft = display.newImageRect( editField, imageSheet, opt.leftFrame, opt.edgeWidth, opt.edgeHeight ) + viewLeft.x = opt.edgeWidth / 2 + viewLeft.y = yCenter + + -- The right edge + viewRight = display.newImageRect( editField, imageSheet, opt.rightFrame, opt.edgeWidth, opt.edgeHeight ) + + + -- The middle fill + viewMiddle = display.newImageRect( editField, imageSheet, opt.middleFrame, opt.edgeWidth, opt.edgeHeight ) + viewMiddle.width = width - viewLeft.contentWidth - viewRight.contentWidth; + + viewMiddle.x = viewLeft.x + viewLeft.contentWidth * 0.5 + ( viewMiddle.width * 0.5 ) + viewMiddle.y = yCenter + + viewRight.x = viewMiddle.x + ( viewMiddle.width * 0.5 ) + viewRight.width * 0.5 + viewRight.y = yCenter + + xStart = viewLeft.x + viewLeft.contentWidth / 2 ; + xEnd = viewRight.x - viewRight.contentWidth / 2 ; + height = opt.height - 2*4 ; + end + + local function createErrorFrame() + local frame = opt.errorFrame; + local rect = display.newRoundedRect(editField, width / 2 , height / 2 , width+2 * frame.offset, height + 2 * frame.offset, frame.cornerRadius) + editField._errorFrame = rect + rect.strokeWidth = frame.strokeWidth ; + rect:setStrokeColor(unpack(frame.strokeColor)); + rect:setFillColor(unpack(frameErrorFillDefault)); + xStart = 0; + xEnd = opt.width; + + rect.isVisible = false; + end + + local function createRectFrame() + local frame = opt.frame; + local rect = display.newRoundedRect(editField, opt.width / 2, opt.height / 2, opt.width, opt.height, frame.cornerRadius) + rect.strokeWidth = frame.strokeWidth ; + rect:setStrokeColor(unpack(frame.strokeColor)); + rect:setFillColor(unpack(frame.fillColor)); + xStart = math.max(frame.strokeWidth, frame.cornerRadius); + xEnd = opt.width - math.max(frame.strokeWidth, frame.cornerRadius); + height = opt.height - 2*frame.strokeWidth - 2; + end + + --if there is an "validation error" frame, create it and subtract from height/width + if opt.errorFrame then + createErrorFrame() + end + --create with a rectangle frame or slice frame + if opt.frame then + createRectFrame() + else + if opt.topLeftFrame and opt.middleLeftFrame and opt.bottomLeftFrame and + opt.topRightFrame and opt.middleRightFrame and opt.bottomRightFrame and + opt.topMiddleFrame and opt.middleFrame and opt.bottomMiddleFrame then + + create9SliceFrame() + else + create3SliceFrame() + end + end + local fieldDescription, viewTextField + + --add the buttons + local buttonsWidthLeft = 0; + local buttonsWidthRight = 0; + local buttons = opt.buttons; + for i = 1,#buttons do + local button = buttons[i] + if button.imageFrame or button.style or button.defaultFile or button.label then + local btn = nil; + if (button.kind == "icon") or (button.kind == "clear") then + if button.defaultFile then + btn = display.newImage(button.defaultFile) + else + btn = display.newImage(button.imageSheet or imageSheet, + button.defaultFrame or themeData:getFrameIndex( opt.themeOptions[ button.style] )) + end + if button.kind == "clear" then + editField._clearButton = btn; + btn.isVisible = false; + + end + else + local onPress + if button.onPress then + onPress = function (event) + event.target = editField + button.onPress(event) + end + end + local onRelease + if button.onRelease then + onRelease = function (event) + event.target = editField + button.onRelease(event) + end + end + + btn = _widget.newButton( + {sheet = button.imageSheet or imageSheet, + defaultFrame = button.defaultFrame or themeData:getFrameIndex( opt.themeOptions[ button.style] ), + overFrame = button.overFrame or themeData:getFrameIndex( opt.themeOptions[tostring( button.style).."_over"] ), + defaultFile = button.defaultFile, + overFile = button.overFile, + onPress = onPress, + label = button.label, + labelColor = button.labelColor, + fontSize = button.fontSize, + width = button.width, + height = button.height, + onRelease = onRelease + } + ) + end + if btn then + editField:insert(btn) + btn.y = yCenter + opt.buttonYOffset + if button.align == "left" then + btn.x = xStart + buttonsWidthLeft + ( btn.contentWidth * 0.5 )+ + opt.spacing + opt.buttonXOffset + buttonsWidthLeft = opt.spacing + btn.contentWidth + buttonsWidthLeft; + + else + --by default buttons are right aligned + btn.x = xEnd - buttonsWidthRight - opt.spacing - + ( btn.contentWidth * 0.5 ) + opt.buttonXOffset + buttonsWidthRight = opt.spacing + btn.contentWidth + buttonsWidthRight; + + end + end; + end + + end + -- create the label if needed + local textLabelWidth = 0; + local textLabelX = xStart; + editField.label = opt.label; + editField.hint = opt.hint; + -- The label for the field + if opt.label and opt.hideLabel == false then + fieldDescription = display.newText( editField, opt.label, 0,0, opt.labelFont, opt.labelFontSize ); + fieldDescription:setFillColor(unpack(opt.labelColor)); + fieldDescription.anchorX = 0; + fieldDescription.x = xStart + buttonsWidthLeft + opt.spacing ; + fieldDescription.y = yCenter + opt.fakeLabelYOffset + textLabelX = fieldDescription.x; + textLabelWidth = fieldDescription.contentWidth + end; + + local labelWidth = opt.labelWidth or textLabelWidth + buttonsWidthLeft + -- Create the textbox (that is contained within the editField) + local textFieldWidth = opt.textFieldWidth; + + if textFieldWidth == 0 then + textFieldWidth = (xEnd - xStart) - labelWidth - buttonsWidthRight -opt.textFieldXOffset + - 2* opt.spacing; + end + --calculate textheight for selected font + local tmpField = display.newText("qÖ",0,0,opt.editFont,opt.editFontSize) + local lineHeight = tmpField.contentHeight + efDefaults.textLineAdjust; + tmpField:removeSelf(); + + editField._xOriginal = textLabelX - opt.textFieldHeightAdjust/2 + labelWidth + opt.textFieldXOffset + opt.spacing + editField._yOriginal = yCenter + opt.textFieldHeightAdjust/2 + opt.textFieldYOffset + if opt.native then + viewTextField = native.newTextField(editField._xOriginal, editField._yOriginal, textFieldWidth+ opt.textFieldHeightAdjust,lineHeight + opt.textFieldHeightAdjust ) + viewTextField.placeholder = opt.hint; + else + viewTextField = native.newTextField( -1000, -1000, textFieldWidth+ opt.textFieldHeightAdjust,lineHeight + opt.textFieldHeightAdjust ) + end + + viewTextField.anchorX = 0; + editField:insert(viewTextField); + + viewTextField.isEditable = true + viewTextField.hasBackground = false + viewTextField.align = "left" + viewTextField.inputType = opt.inputType; + + editField.inputType = opt.inputType; + + viewTextField.isSecure = opt.isSecure; + editField.isSecure = opt.isSecure; + + viewTextField:setReturnKey(opt.returnKey); + editField.returnKey = opt.returnKey; + + editField.required = opt.required; + + editField.slideGroup = opt.slideGroup + editField.keyboardSlideTime = opt.keyboardSlideTime + editField.calibrating = opt.calibrating + editField.native = opt.native; + editField.fontScale = opt.fontScale; + editField.editFontColor = opt.editFontColor; + editField.editHintColor = opt.editHintColor; + + viewTextField.font = native.newFont( opt.editFont ) + viewTextField.size = opt.editFontSize * opt.fontScale --deviceScale + viewTextField.align = opt.align + if system.getInfo("platformName") == "Android" then + viewTextField:setTextColor(opt.editFontColor[1]*255, opt.editFontColor[2]*255, + opt.editFontColor[3]*255, opt.editFontColor[4]*255); + else + viewTextField:setTextColor(opt.editFontColor[1], opt.editFontColor[2], + opt.editFontColor[3], opt.editFontColor[4]); + end + local fakeTextField + if not opt.native then + fakeTextField = display.newText( + {parent=editField, + text="", + x=0, + y=0, + width=textFieldWidth -2*opt.fakeLabelXOffset+2*opt.textFieldXOffset, + height=lineHeight, + font=opt.editFont, + fontSize=opt.editFontSize, + align = opt.align}) + fakeTextField.anchorX = 0; + fakeTextField.x = textLabelX + labelWidth + opt.fakeLabelXOffset + opt.spacing; + fakeTextField.y = yCenter + opt.fakeLabelYOffset ; + fakeTextField:setFillColor(unpack(opt.editFontColor)); + fakeTextField._viewTextField = viewTextField; + fakeTextField._hint = opt.hint; + + editField._fakeTextField = fakeTextField; + end; + if ( opt.listener and type(opt.listener) == "function" ) then + viewTextField._listener = opt.listener + end + if ( opt.onSubmit and type(opt.onSubmit) == "function" ) then + editField._onSubmit = opt.onSubmit + end + + editField._textField = viewTextField + editField._textLabelX = textLabelX + editField.maxChars = opt.maxChars; + editField.allowedChars = opt.allowedChars; + editField._isModified = false + + + viewTextField._editField = editField; + + if "function" == type(opt.onClick) then + editField._onClick = opt.onClick; + end; + + --add to storyboard list of editFields + if _storyboard.getCurrentScene then + local scene = _storyboard.getCurrentScene() + if scene.addEditField then + scene.addEditField(editField) + editField._scene = scene; + end + + end + + local function onClearTap(event) + editField._isModified = editField._originalText and string.len(editField._originalText) > 0; + if string.len(editField._textField.text) > 0 then + editField._textField.text = ""; + + editField._clearButton.isVisible = false; + editField:updateFakeContent("") + editField._textField._originalText = "" + event.target = editField; + if editField._onSubmit then + event.phase = "cleared" + editField._onSubmit(event); + end + end; + return true; + end + + if editField._clearButton then + editField._clearButton:addEventListener( "tap" , onClearTap ) + end + ---------------------------------------------------------- + -- PUBLIC METHODS + ---------------------------------------------------------- + function editField:updateFakeContent(value) + if self._fakingIt then + local fakeTextField = self._fakeTextField; + local text = value; + if text and text:len() > 0 then + if not self.calibrating then + fakeTextField:setFillColor(unpack(self.editFontColor)); + else + fakeTextField:setFillColor(1,0,0,1); + fakeTextField:toFront(); + end + if self.isSecure then + fakeTextField.text = string.rep("•", string.len(text)); + else + fakeTextField.text = text; + end + else + if not self.calibrating then + fakeTextField:setFillColor(unpack(self.editHintColor)); + fakeTextField.text = fakeTextField._hint; + else + fakeTextField:setFillColor(1,0,0,1); + fakeTextField:toFront(); + end + + end + end; + end + + function editField:slideBackKeyboard() + local slideGroup = self.slideGroup + local function onSlideComplete(event) + slideGroup._restorePosition = nil + + end + + if slideGroup then + --check if we are in a scrollview + if self == slideGroup._slidingField then + slideGroup._restorePosition = slideGroup._originalSlideY + self:setSlideGroupPosition(slideGroup._originalSlideY, onSlideComplete) + slideGroup._slidingField = nil + slideGroup._originalSlideY = nil + + else + end + end + end + + function editField:getSlideGroupPosition() + local slideGroup = self.slideGroup; + local xGroup, yGroup + if slideGroup._restorePosition then + yGroup = slideGroup._restorePosition + elseif slideGroup.getContentPosition and slideGroup.scrollToPosition then + xGroup, yGroup = slideGroup:getContentPosition() + else + yGroup = slideGroup.y; + end + return yGroup + end + + function editField:setSlideGroupPosition(yGroup, onSlideComplete) + local slideGroup = self.slideGroup; + if slideGroup.getContentPosition and slideGroup.scrollToPosition then + slideGroup:scrollToPosition({y=yGroup,time = self.keyboardSlideTime, onComplete = onSlideComplete}) + else + transition.to(slideGroup,{y = yGroup,time=self.keyboardSlideTime, onComplete = onSlideComplete} ) + end + end + + function editField:slideForKeyboard(neededHeight) + local slideGroup = self.slideGroup; + if slideGroup then + local yGroup = self:getSlideGroupPosition() + local groupOffset = 0 + --if the group is already slid up, check difference + if slideGroup._originalSlideY then + groupOffset = (slideGroup._originalSlideY - yGroup) + end + local lx, ly = self:contentToLocal(0,0) + local top = -ly + groupOffset; + + + local kbHeight = neededHeight or getKeyboardHeight(); + local desiredTop = display.contentHeight - kbHeight - self.contentHeight; + if top > desiredTop then + local y = yGroup - (top - desiredTop) + groupOffset + if not slideGroup._slidingField then + slideGroup._originalSlideY = yGroup + end + self:setSlideGroupPosition(y) + slideGroup._slidingField = self + end + end + end + + function editField:_swapFakeField( fakeIt ) + + + local fakeTextField = self._fakeTextField; + local viewTextField = self._textField; + + local function showEditField(event) + + self:insert(viewTextField) + viewTextField.x = self._xOriginal; + viewTextField.y = self._yOriginal; + native.setKeyboardFocus( viewTextField ) + fakeTextField.isVisible = self.calibrating; + self._fakingIt = false; + if self._clearButton then + self._clearButton.isVisible = string.len(viewTextField.text) ~= 0 + end; + end + + if fakeIt then + if not self._fakingIt then + self._fakingIt = true; + self:updateFakeContent(viewTextField.text) + fakeTextField.isVisible = true; + + if not self.calibrating then + display.getCurrentStage():insert(viewTextField) + viewTextField.x = -1000; + viewTextField.y = -1000; + else + viewTextField.x = self._xOriginal; + viewTextField.y = self._yOriginal; + end; + end + else + showEditField() + end + + end + + local function touchFakeLabel(event) + local phase = event.phase; + if "began" == phase then + --keep focus, since the field can be scrolling up + display.getCurrentStage():setFocus(fakeTextField); + if editField._onClick == nil then + editField:_swapFakeField( false ) + else + event.target = editField + editField._onClick(event) + end + elseif ( "ended" == phase or "off" == phase or "cancelled" == phase ) then + --release focus + display.getCurrentStage():setFocus(nil); + + end; + return true + end + + local function touchLabelText(event) + if editField._onClick then + editField._onClick(event) + end + end + local function onBackgroundTouch(event) + local phase = event.phase; + if "began" == phase then + --keep focus, since the field can be scrolling up + display.getCurrentStage():setFocus(editField); + elseif ( "ended" == phase or "off" == phase or "cancelled" == phase ) then + --release focus + display.getCurrentStage():setFocus(nil); + end + return true + end + if fakeTextField then + fakeTextField:addEventListener( "touch", touchFakeLabel ) + end; + + if fieldDescription then + fieldDescription:addEventListener( "touch", touchLabelText ) + end + + editField._fakingIt = false; + if not editField.native then + editField:_swapFakeField( true ) + end + + editField:addEventListener( "touch", onBackgroundTouch ) + + -- Function to listen for textbox events + function viewTextField:_inputListener( event ) + local editField = self._editField; + local phase = event.phase + + --async called on Submit in case other edit field is taking focus + local function onHideField(e) + if _focusedField == nil or _focusedField == self then + native.setKeyboardFocus( nil ) + _focusedField = nil; + end; + editField:slideBackKeyboard() + --phase submitted + if editField._onSubmit and (phase == "submitted" or phase == "ended" and self.text ~= self._originalText) then + self._originalText = self.text + event.target = editField + editField._onSubmit(event) + end + --phase ended - send onSubmit only if the text is different + end + + if "began" == phase then + _focusedField = self; + self._originalText = self.text + self._editing = true; + editField:slideForKeyboard() + elseif "editing" == phase then + -- If there is one or more characters in the textField show the cancel button, if not hide it + local sText = event.text; + local textLen = string.len(sText); + if editField.allowedChars and event.newCharacters then + if not string.find(editField.allowedChars, event.newCharacters,1,true) then + --remove escape characters + local plainChars = string.gsub(event.newCharacters,"(%W)","%%%1") + event.target.text = string.gsub(sText, plainChars, "") + end + end + if editField.maxChars > 0 then + if textLen > editField.maxChars then + event.target.text = string.sub(sText, 1, editField.maxChars) + end + end + editField._isModified = editField._originalText ~= self.text + + if editField.calibrating then + editField._fakeTextField.text = self.text; + end + if editField._clearButton then + editField._clearButton.isVisible = textLen > 0 + end + if editField._errorFrame and editField.validating then + editField._errorFrame.isVisible = editField.required and textLen == 0; + end + + elseif "submitted" == phase or + "ended" == phase then + -- Hide keyboard + if self._editing then + timer.performWithDelay(100,onHideField ,1) + self._editing = false; + end + + if not editField.native then + editField:_swapFakeField( true ) + end + + end + + -- If there is a listener defined, execute it + if self._listener then + event.target = editField; + self._listener( event ) + end + return true; + end + + viewTextField.userInput = viewTextField._inputListener + viewTextField:addEventListener( "userInput" ) + + ---------------------------------------------------------- + -- PRIVATE METHODS + ---------------------------------------------------------- + + function editField:setText(value) + self._textField.text = value + self:updateFakeContent(value) + self:setIsModified(false) + --android sets text asynchroneously + + end + + function editField:getText() + return self._textField.text + end + + function editField:setValue(value) + self:setText(value) + end + + function editField:getValue() + return self:getText() + end + + function editField:setReturnKey(value) + self.returnKey = value; + self._textField:setReturnKey(self.returnKey); + + end; + + function editField:setInputType(value) + self.inputType = value; + self._textField.inputType = value; + end; + + function editField:setIsSecure(value) + self.isSecure = value; + self._textField.isSecure = value; + end; + + function editField:validate() + local notValid = self.required and + string.len(self._textField.text) == 0 + if self._errorFrame then + self.validating = true; + self._errorFrame.isVisible = notValid + end + return notValid + end + + function editField:setEditMode(value) + self:_swapFakeField( not value ) + end + + function editField:getEditMode() + return not self._fakingIt; + end + function editField:setIsModified(value) + local isModified = value == nil and false or value; + if not isModified then + self._originalText = self:getText() + end + self._isModified = isModified + end + + function editField:isModified() + return self._isModified; + end + + function editField:getVersion() + return version + end + + -- Finalize function + function editField:_finalize() + --remove from storyboard + local scene = self._scene; + if scene then + if scene.removeEditField then + scene.removeEditField(self) + end + end + + -- Remove the textField + display.remove( self._textField ) + + self._textField = nil + self._fakeTextField = nil; + self._cancelButton = nil + + end + + --create a hidden background to "eat" the touch events" + local bg = display.newRect(editField, opt.width / 2, opt.height / 2, opt.width, opt.height) + bg:setFillColor(0,0,0,0); + bg.isHitTestable = true; + bg:toBack() + bg:addEventListener("touch", function (event) return true; end) + return editField +end + + + + + +-- Function to create a new editfield object ( widget.newEditField) +function M.new( options, theme ) + local customOptions = options or {} + local themeOptions = theme or {} + + -- Create a local reference to our options table + local opt = M._options + + -- Check if the requirements for creating a widget has been met (throws an error if not) + _widget._checkRequirements( customOptions, themeOptions, M._widgetName ) + + ------------------------------------------------------- + -- Properties + ------------------------------------------------------- + -- Positioning & properties + opt.left = customOptions.left or 0 + opt.top = customOptions.top or 0 + opt.x = customOptions.x or nil + opt.y = customOptions.y or nil + if customOptions.x and customOptions.y then + opt.left = 0 + opt.top = 0 + end + opt.width = customOptions.width or 150 + + opt.frame = customOptions.frame + if opt.frame then + + opt.frame.cornerRadius = opt.frame.cornerRadius or 0; + opt.frame.strokeWidth = opt.frame.strokeWidth or 1; + opt.frame.strokeColor = opt.frame.strokeColor or frameStrokeDefault; + opt.frame.fillColor = opt.frame.fillColor or frameFillDefault; + end; + + opt.errorFrame = customOptions.errorFrame + if opt.errorFrame then + opt.errorFrame.cornerRadius = opt.errorFrame.cornerRadius or (opt.frame and opt.frame.cornerRadius or 0); + opt.errorFrame.strokeWidth = opt.errorFrame.strokeWidth or (opt.frame and opt.frame.strokeWidth or 1); + opt.errorFrame.strokeColor = opt.errorFrame.strokeColor or frameErrorStrokeDefault; + opt.errorFrame.offset = opt.errorFrame.offset or 3; + + end; + + opt.id = customOptions.id + opt.hint = customOptions.hint or ""; + opt.inputType = customOptions.inputType or "default" + opt.isSecure = customOptions.isSecure or false; + opt.returnKey = customOptions.returnKey or "done"; + opt.onClick = customOptions.onClick; + opt.required = customOptions.required or false; + opt.textFieldYOffset = customOptions.textFieldYOffset or efDefaults.textFieldYOffset + opt.textFieldXOffset = customOptions.textFieldXOffset or efDefaults.textFieldXOffset + opt.fakeLabelYOffset = customOptions.fakeLabelYOffset or efDefaults.fakeLabelYOffset + opt.fakeLabelXOffset = customOptions.fakeLabelXOffset or efDefaults.fakeLabelXOffset + opt.textFieldHeightAdjust = customOptions.textFieldHeightAdjust or efDefaults.textFieldHeightAdjust + opt.fontScale = customOptions.fontScale or efDefaults.fontScale; + opt.calibrating = customOptions.calibrating + + opt.textFieldWidth = customOptions.textFieldWidth or 0; + opt.labelWidth = customOptions.labelWidth + opt.listener = customOptions.listener + opt.onSubmit = customOptions.onSubmit + opt.editFontColor = customOptions.editFontColor or themeOptions.editTextColor or {0,0,0,1} + opt.editHintColor = customOptions.editHintColor or {0.5,0.5,0.5,1} + + + -- Frames & Images + opt.sheet = customOptions.sheet + opt.themeSheetFile = themeOptions.sheet + opt.themeData = themeOptions.data + opt.themeOptions = themeOptions + opt.label = customOptions.label + opt.hideLabel = customOptions.hideLabel or false; + opt.labelColor = customOptions.labelColor or labelDefault + opt.labelFont = customOptions.labelFont or themeOptions.font or native.systemFont + opt.labelFontSize = customOptions.labelFontSize or themeOptions.fontSize or 14 + opt.spacing = customOptions.spacing or 2; + opt.buttons = customOptions.buttons or {}; + opt.maxChars = customOptions.maxChars or 0; + opt.allowedChars = customOptions.allowedChars or nil; + if opt.allowedChars and string.len(opt.allowedChars)== 0 then + opt.allowedChars = nil; + end + opt.buttonXOffset = customOptions.buttonXOffset or 0 + opt.buttonYOffset = customOptions.buttonYOffset or 0 + + opt.editFont = customOptions.editFont or opt.labelFont + opt.editFontSize = customOptions.editFontSize or opt.labelFontSize + opt.height = customOptions.height or (opt.editFontSize and opt.editFontSize + 12 or 29) + opt.native = customOptions.native or false + opt.slideGroup = customOptions.slideGroup + opt.keyboardSlideTime = customOptions.keyboardSlideTime or 200 + opt.align = customOptions.align or "left" + -- Left ( 9 piece set ) + opt.topLeftFrame = customOptions.topLeftFrame or _widget._getFrameIndex( themeOptions, themeOptions.topLeftFrame ) + opt.middleLeftFrame = customOptions.middleLeftFrame or _widget._getFrameIndex( themeOptions, themeOptions.middleLeftFrame ) + opt.bottomLeftFrame = customOptions.bottomLeftFrame or _widget._getFrameIndex( themeOptions, themeOptions.bottomLeftFrame ) + + -- Right ( 9 piece set ) + opt.topRightFrame = customOptions.topRightFrame or _widget._getFrameIndex( themeOptions, themeOptions.topRightFrame ) + opt.middleRightFrame = customOptions.middleRightFrame or _widget._getFrameIndex( themeOptions, themeOptions.middleRightFrame ) + opt.bottomRightFrame = customOptions.bottomRightFrame or _widget._getFrameIndex( themeOptions, themeOptions.bottomRightFrame ) + + -- Middle ( 9 piece set ) + opt.topMiddleFrame = customOptions.topMiddleFrame or _widget._getFrameIndex( themeOptions, themeOptions.topMiddleFrame ) + opt.middleFrame = customOptions.middleFrame or _widget._getFrameIndex( themeOptions, themeOptions.middleFrame ) + opt.bottomMiddleFrame = customOptions.bottomMiddleFrame or _widget._getFrameIndex( themeOptions, themeOptions.bottomMiddleFrame ) + + --3 slice + opt.leftFrame = customOptions.leftFrame or _widget._getFrameIndex( themeOptions, themeOptions.leftFrame ) + opt.rightFrame = customOptions.rightFrame or _widget._getFrameIndex( themeOptions, themeOptions.rightFrame ) + opt.middleFrame = opt.middleFrame or customOptions.middleFrame or _widget._getFrameIndex( themeOptions, themeOptions.middleFrame ) + if opt.leftFrame and opt.rightFrame and opt.middleFrame then + opt.edgeWidth = customOptions.edgeWidth or themeOptions.edgeWidth or error( "ERROR: " .. M._widgetName .. ": edgeFrameWidth expected, got nil", 3 ) + opt.edgeHeight = customOptions.edgeHeight or themeOptions.edgeHeight or error( "ERROR: " .. M._widgetName .. ": edgeFrameHeight expected, got nil", 3 ) + end + + ------------------------------------------------------- + -- Create the editField + ------------------------------------------------------- + + -- Create the editField object + local editField = _widget._new + { + left = opt.left, + top = opt.top, + id = opt.id or "widget_editField", + } + + initEditField( editField, opt ) + + -- Set the editField's position ( set the reference point to center, just to be sure ) + if ( isGraphicsV1 ) then + editField:setReferencePoint( display.CenterReferencePoint ) + end + local x, y = _widget._calculatePosition( editField, opt ) + editField.x, editField.y = x, y + + -- create metatable + local _editProxy = editField + + -- create proxy for property getters/setters + local editField = {} + + editField._class = _editProxy._class + editField._proxy = _editProxy._proxy + + local mt = { + __index = function (m,k) + if k == "text" then + return _editProxy:getText() + elseif k == "returnKey" then + return _editProxy.returnKey + elseif k == "inputType" then + return _editProxy.inputType + elseif k == "isSecure" then + return _editProxy.isSecure + elseif k == "editMode" then + return not _editProxy._fakingIt + else + + return _editProxy[k] -- access the original table + end + end, + + __newindex = function (m,k,v) + if k == "text" then + _editProxy:setText(v) + elseif k == "returnKey" then + _editProxy:setReturnKey(v) + elseif k == "inputType " then + _editProxy:setInputType(v) + elseif k == "isSecure" then + _editProxy:setIsSecure(v) + elseif k == "editMode" then + _editProxy:_swapFakeField( not k ) + else + _editProxy[k] = v -- update original table + end + end + + } + + setmetatable(editField, mt) + + + return editField +end + + +return M diff --git a/widgetLibrary/widget_pageslider.lua b/widgetLibrary/widget_pageslider.lua new file mode 100644 index 0000000..72b5d46 --- /dev/null +++ b/widgetLibrary/widget_pageslider.lua @@ -0,0 +1,308 @@ +-- Copyright © 2014 Top Rank Software LLC. All Rights Reserved. +-- This library includes code developed by Corona Labs Inc. (http://www.coronalabs.com). +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- * Neither the name of the company nor the names of its contributors +-- may be used to endorse or promote products derived from this software +-- without specific prior written permission. +-- * Redistributions in any form whatsoever must retain the following +-- acknowledgment visually in the program (e.g. the credits of the program): +-- 'This product includes software developed by Top Rank Software LLC' +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +-- DISCLAIMED. IN NO EVENT SHALL CORONA LABS INC. BE LIABLE FOR ANY +-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-- Require needed widget files +local _widget = require( "widget" ) + + +local M = +{ + _options = {}, + _widgetName = "widget.newPageSlider", +} + +local isGraphicsV1 = ( 1 == display.getDefault( "graphicsCompatibility" ) ) +local isByteColorRange = display.getDefault( "isByteColorRange" ) + +local fillColorDefault +if isByteColorRange then + fillColorDefault = { 255, 255, 255 } +else + fillColorDefault = { 1, 1, 1 } +end + +local dotFillColorDefault + +if isByteColorRange then + dotFillColorDefault = { 127, 127, 127 } +else + dotFillColorDefault = { 0.5, 0.5, 0.5 } +end + +local function createPageSlider( pageSlider, options ) + local opt = options + + + local view = display.newGroup(); + pageSlider:insert(view) + + local rect = display.newRect( 0, 0,opt.width * opt.numPages, opt.height); + rect:setFillColor(unpack(opt.fillColor)); + rect.x = rect.contentWidth / 2; + rect.y = rect.contentHeight / 2; + rect.isHitTestable = true; + view:insert(rect) + + + pageSlider._view = view + pageSlider._transition = 0; + view._numPages = opt.numPages; + + local dots = {}; + local dGroup = nil + if opt.dotSegmentHeight > 0 then + dGroup = display.newGroup() + dGroup.x = (opt.width - (opt.numPages+1)*opt.dotsMargin)/ 2; + dGroup.y = opt.height - opt.dotSegmentHeight + pageSlider:insert(dGroup); + end; + + local pages = {}; + for i = 1, opt.numPages do + if opt.dotSegmentHeight > 0 then + local dot = display.newCircle(dGroup, i*opt.dotsMargin, + opt.dotSegmentHeight / 2, + opt.dotRadius) ; + dot:setFillColor(unpack(opt.dotFillColor)) + dot:setStrokeColor(unpack(opt.dotStrokeColor)); + dot.strokeWidth = 1; + dots[#dots+1] = dot + end; + + local page = display.newGroup(); + page.x = opt.width * (i - 1) --+ opt.width / 2; + page.y = 0 --opt.height / 2 + page.contentWidth = opt.width + page.contentHeight = opt.height + view:insert(page); + pages[i] = page; + end + + view._dots = dots; + + view._width = opt.width; + view._isEnabled = opt.isEnabled + view._fastSwipeTime = opt.fastSwipeTime; + view._fastSwipeDist = opt.fastSwipeDist; + pageSlider.pages = pages; + pageSlider._onChangePage = opt.onChangePage; + + function pageSlider:selectPage(iPage, isAnimated) + + local function onTransitionComplete( event ) + transition.cancel( pageSlider._transition ) ; + pageSlider._transition = 0 + + local targetX = -(self._view.selectedPage-1)* self._view._width + if ( self._view.x ~= targetX ) then + self._view.x = targetX + end + self:updateDots( self._view.selectedPage) + end + + local canChangePage = true; + if self._onChangePage then + canChangePage = self._onChangePage(iPage) + + end + + if canChangePage then + self._view.selectedPage = iPage; + end; + if ( self._transition ~= 0 ) then + transition.cancel( self._transition ) + self._transition = 0 + end + local targetX = -(self._view.selectedPage-1) * self._view._width + local _view = self._view; + if targetX ~= _view.X then + + if isAnimated then + self._transition = transition.to( + _view , + { x = targetX, + time = 1000*math.abs(targetX - _view.x)/self._view._width, + transition = easing.outQuad, + onComplete = onTransitionComplete } ) + + else + _view.x = targetX; + end; + self:updateDots( self._view.selectedPage ); + end + + end + + function pageSlider:updateDots( iPage ) + local dots = self._view._dots; + + for i = 1, #dots do + if i == iPage then + dots[i].alpha = 1.0 + else + dots[i].alpha = 0.3 + end + end + end + + + + function pageSlider:getPage( iPage ) + return self.pages[iPage]; + end + + function pageSlider:getSelected() + return self._view.selectedPage; + end + -- Function to set a pageSlider as active + function pageSlider:setEnabled( isEnabled ) + self._view._isEnabled = isEnabled + end + + function pageSlider:takeFocus(event) + self._view:touch(event) + end + + function view:touch( event ) + + local phase = event.phase + local currentPage = self.selectedPage; + + -- If the pageSlider isn't active, just return + if not self._isEnabled then + return + end + if "began" == phase then + display.getCurrentStage():setFocus(self); + self.isFocus = true; + native.setKeyboardFocus(nil) + if (self._transition ~= 0 ) then + transition.cancel( self._transition ) ; + self._transition = 0 + end + self._startX = event.x ; + self._lastX = event.x ; + self._startTime = event.time; + elseif ( "moved" == phase and self.isFocus == true ) then + local movedDistance = event.x - self._lastX; + local totalDistance = event.x - self._startX ; + local resist = 1; + if (( currentPage == 1 and totalDistance > 0 ) or + ( currentPage == self._numPages and totalDistance < 0 ) ) then + resist = 0.3; + end + self.x = self.x + (movedDistance * resist) ; + self._lastX = event.x ; + + elseif ( "ended" == phase or "off" == phase or "cancelled" == phase ) and self._startTime then + display.getCurrentStage():setFocus(nil); + self.isFocus = false; + local totalSwipeTime = system.getTimer() - self._startTime + self._startTime = nil + local totalDistance = event.x - self._startX ; + local swipeDist = math.abs( totalDistance ) + local fastSwipeTime = self._fastSwipeTime; + local pageWidth = self._width; + if (totalSwipeTime <= fastSwipeTime and swipeDist >= self._fastSwipeDist) or + ( totalSwipeTime > fastSwipeTime and swipeDist >= pageWidth*0.5 ) then + if totalDistance < 0 then + currentPage = math.min(self._numPages, currentPage + 1); + else + currentPage = math.max(1, currentPage - 1) + end + end; + self.parent:selectPage(currentPage, true) + + end + return true + end + + view:addEventListener( "touch") + pageSlider:selectPage(opt.selectedPage, false) + + + +end + +-- Function to create a new button object ( widget.newButton ) +function M.new( options, theme ) + local customOptions = options or {} + local themeOptions = theme or {} + + -- Create a local reference to our options table + local opt = M._options + + -- Positioning & properties + opt.left = customOptions.left or 0 + opt.top = customOptions.top or 0 + opt.x = customOptions.x or nil + opt.y = customOptions.y or nil + + if customOptions.x and customOptions.y then + opt.left = 0 + opt.top = 0 + end + opt.width = customOptions.width or themeOptions.width + opt.height = customOptions.height or themeOptions.height + opt.isEnabled = customOptions.isEnabled or true; + opt.numPages = customOptions.numPages or 1; + opt.fillColor = customOptions.fillColor or fillColorDefault; + opt.selectedPage = customOptions.selectedPage or 1; + opt.dotFillColor = customOptions.dotFillColor or dotFillColorDefault; + opt.dotStrokeColor = customOptions.dotStrokeColor or opt.dotFillColor + opt.dotsMargin = customOptions.dotsMargin or 35; + opt.fastSwipeTime = customOptions.fastSwipeTime or 300; + opt.fastSwipeDist = customOptions.fastSwipeDist or display.contentWidth/10 + opt.dotSegmentHeight = customOptions.dotSegmentHeight or 20; + opt.dotRadius = customOptions.dotRadius or 6; + + + opt.onChangePage = ( type(customOptions.onChangePage) == "function" ) and customOptions.onChangePage or nil; + -- Create the pageslider object + local pageSlider = _widget._new + { + left = opt.left, + top = opt.top, + id = opt.id or "widget_pageslider", + baseDir = opt.baseDir, + widgetType = "pageslider", + } + createPageSlider(pageSlider, opt) + + if ( isGraphicsV1 ) then + pageSlider:setReferencePoint( display.CenterReferencePoint ) + end + + local x, y = _widget._calculatePosition( pageSlider, opt ) + pageSlider.x, pageSlider.y = x, y + + return pageSlider +end; + +return M \ No newline at end of file diff --git a/widgetLibrary/widget_panel.lua b/widgetLibrary/widget_panel.lua new file mode 100644 index 0000000..16da715 --- /dev/null +++ b/widgetLibrary/widget_panel.lua @@ -0,0 +1,1212 @@ + +local M = +{ + _options = {}, + _widgetName = "widget.newPanel", +} + + +-- Require needed widget files +local _widget = require( "widget" ) + +local isGraphicsV1 = ( 1 == display.getDefault( "graphicsCompatibility" ) ) +local isByteColorRange = display.getDefault( "isByteColorRange" ) + + +-- define a default color set for both graphics modes +local panelDefault = { 0, 0, 0 } + +local shapeStrokeDefault = { 0, 0, 0 }; + +local shapeFillDefault +if isGraphicsV1 then + shapeFillDefault = { 204, 204, 204 } +else + shapeFillDefault = { 0.8, 0.8, 0.8 } +end + + + +-- Function to handle touches on a widget panel, function is common to all widget panel creation types (ie image files, imagesheet, and 9 slice panel creation) +local function managePanelTouch( view, event ) + local phase = event.phase + + -- If the panel isn't active, just return + if not view._isEnabled then + return + end + + if "began" == phase then + -- Create the alpha fade if ios7 + + -- If there is a onPress method ( and not a onEvent method ) + if view._onPress and not view._onEvent then + view._onPress( event ) + end + + -- If the parent group still exists + if "table" == type( view.parent ) then + -- Set focus on the panel + view._isFocus = true + display.getCurrentStage():setFocus( view, event.id ) + + end + + elseif view._isFocus then + if "moved" == phase then + if not _widget._isWithinBounds( view, event ) then + + + else + + end + + elseif "ended" == phase or "cancelled" == phase then + if _widget._isWithinBounds( view, event ) then + -- If there is a onRelease method ( and not a onEvent method ) + if view._onRelease and not view._onEvent then + view._onRelease( event ) + end + end + + -- Remove focus from the panel + view._isFocus = false + display.getCurrentStage():setFocus( nil ) + end + end + + -- If there is a onEvent method ( and not a onPress or onRelease method ) + if view._onEvent and not view._onPress and not view._onRelease then + if not _widget._isWithinBounds( view, event ) and "ended" == phase then + event.phase = "cancelled" + + end + + view._onEvent( event ) + end +end + + +------------------------------------------------------------------------ +-- Text only panel +------------------------------------------------------------------------ +local function createUsingText( panel, options ) + -- Create a local reference to our options table + local opt = options + + -- Forward references + local view + local rect = nil; + local shape = opt.shape; + if shape then + + rect = display.newRoundedRect( panel, panel.x, panel.y, opt.width , opt.height , shape.cornerRadius); + rect.strokeWidth = shape.strokeWidth or 1; + rect:setStrokeColor(unpack(shape.strokeColor)); + rect:setFillColor(unpack(shape.fillColor)); + end + local viewLabel; + -- Create the label (either embossed or standard) + if opt.embossedLabel then + viewLabel = display.newEmbossedText( panel, opt.label, 0, 0, opt.font, opt.fontSize ) + else + viewLabel = display.newText( panel, opt.label, 0, 0, opt.font, opt.fontSize ) + end + + if rect then + view = rect; + view._shapeColor = shape.fillColor; + view._strokeColor = shape.strokeColor; + view._label = viewLabel; + + else + view = viewLabel; + end + viewLabel:setFillColor( unpack( opt.labelColor) ) + view._labelColor = opt.labelColor + + ---------------------------------- + -- Positioning + ---------------------------------- + + -- Labels position + if "center" == opt.labelAlign then + viewLabel.x = view.x + opt.labelXOffset + elseif "left" == opt.labelAlign then + viewLabel.x = view.x - ( view.contentWidth * 0.5 ) + ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + elseif "right" == opt.labelAlign then + viewLabel.x = view.x + ( view.contentWidth * 0.5 ) - ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + end + + + if "center" == opt.labelAlignY then + viewLabel.y = view.y + opt.labelYOffset + elseif "top" == opt.labelAlignY then + viewLabel.y = view.y - ( view.contentHeight * 0.5 ) + ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + elseif "bottom" == opt.labelAlignY then + + viewLabel.y = view.y + ( view.contentHeight * 0.5 ) - ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + end + + + ------------------------------------------------------- + -- Assign properties/objects to the view + ------------------------------------------------------- + + view._isEnabled = opt.isEnabled + viewLabel._fontSize = opt.fontSize + viewLabel._labelColor = view._labelColor + + -- Methods + view._onPress = opt.onPress + view._onRelease = opt.onRelease + view._onEvent = opt.onEvent + + ------------------------------------------------------- + -- Assign properties/objects to the panel + ------------------------------------------------------- + + -- Assign objects to the panel + panel._view = view + + ---------------------------------------------------------- + -- PUBLIC METHODS + ---------------------------------------------------------- + + -- Function to set the panels text color + function panel:setFillColor( ... ) + if self._label then + self._view._label:setFillColor( ... ) + else + self._view:setFillColor( ... ) + end; + end + + -- Function to set the panel's label + function panel:setLabel( newLabel ) + return self._view:_setLabel( newLabel ) + end + + -- Function to get the panel's label + function panel:getLabel() + return self._view:_getLabel() + end + + -- Function to set a panel as active + function panel:setEnabled( isEnabled ) + self._view._isEnabled = isEnabled + end + + -- Touch listener for our panel + function view:touch( event ) + -- Set the target to the view's parent group (the panel object) + event.target = self.parent + + -- Manage touch events on the panel + managePanelTouch( self, event ) + + return true + end + + view:addEventListener( "touch" ) + + ---------------------------------------------------------- + -- PRIVATE METHODS + ---------------------------------------------------------- + + -- Function to set the panel's label + function view:_setLabel( newLabel ) + -- Update the label's text + if "function" == type( self.setText ) or self._label then + if self._label then + self._label:setText( newLabel ) + else + self:setText( newLabel ) + end + else + self.text = newLabel + end + end + + -- Function to get the panel's label + function view:_getLabel() + return self._label.text + end + + + -- Finalize function + function panel:_finalize() + end + + return panel +end + + +------------------------------------------------------------------------ +-- Image Files Panel +------------------------------------------------------------------------ + +-- Creates a new panel from single png images +local function createUsingImageFiles( panel, options ) + -- Create a local reference to our options table + local opt = options + + -- Forward references + local view, viewLabel + + -- Create the view + if opt.width and opt.height then + view = display.newImageRect( panel, opt.defaultFile, opt.baseDir, opt.width, opt.height ) + else + view = display.newImage( panel, opt.defaultFile, opt.baseDir ) + end + + + -- Create the label (either embossed or standard) + if opt.embossedLabel then + viewLabel = display.newEmbossedText( panel, opt.label, 0, 0, opt.font, opt.fontSize ) + else + viewLabel = display.newText( panel, opt.label, 0, 0, opt.font, opt.fontSize ) + end + + ---------------------------------- + -- Positioning + ---------------------------------- + + -- The view + view.x = panel.x + ( view.contentWidth * 0.5 ) + view.y = panel.y + ( view.contentHeight * 0.5 ) + + + -- Setup the label + viewLabel:setFillColor( unpack( opt.labelColor) ) + viewLabel._isLabel = true + viewLabel._labelColor = opt.labelColor + + + -- Labels position + if "center" == opt.labelAlign then + viewLabel.x = view.x + opt.labelXOffset + elseif "left" == opt.labelAlign then + viewLabel.x = view.x - ( view.contentWidth * 0.5 ) + ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + elseif "right" == opt.labelAlign then + viewLabel.x = view.x + ( view.contentWidth * 0.5 ) - ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + end + + + if "center" == opt.labelAlignY then + viewLabel.y = view.y + opt.labelYOffset + elseif "top" == opt.labelAlignY then + viewLabel.y = view.y - ( view.contentHeight * 0.5 ) + ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + elseif "bottom" == opt.labelAlignY then + + viewLabel.y = view.y + ( view.contentHeight * 0.5 ) - ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + end + + ------------------------------------------------------- + -- Assign properties/objects to the view + ------------------------------------------------------- + + view._isEnabled = opt.isEnabled + view._fontSize = opt.fontSize + view._label = viewLabel + view._labelColor = viewLabel._labelColor + view._labelAlign = opt.labelAlign + view._labelXOffset = opt.labelXOffset + view._labelYOffset = opt.labelYOffset + + -- Methods + view._onPress = opt.onPress + view._onRelease = opt.onRelease + view._onEvent = opt.onEvent + + ------------------------------------------------------- + -- Assign properties/objects to the panel + ------------------------------------------------------- + + -- Assign objects to the panel + panel._view = view + + ---------------------------------------------------------- + -- PUBLIC METHODS + ---------------------------------------------------------- + + -- Function to set the panels fill color + function panel:setFillColor( ... ) + self._view:setFillColor( ... ) + + end + + -- Function to set the panel's label + function panel:setLabel( newLabel ) + return self._view:_setLabel( newLabel ) + end + + -- Function to get the panel's label + function panel:getLabel() + return self._view:_getLabel() + end + + -- Function to set a panel as active + function panel:setEnabled( isEnabled ) + self._view._isEnabled = isEnabled + end + + -- Touch listener for our panel + function view:touch( event ) + -- Set the target to the view's parent group (the panel object) + event.target = self.parent + + -- Manage touch events on the panel + managePanelTouch( self, event ) + + return true + end + + view:addEventListener( "touch" ) + + ---------------------------------------------------------- + -- PRIVATE METHODS + ---------------------------------------------------------- + + -- Function to set the panel's label + function view:_setLabel( newLabel ) + -- Update the label's text + if "function" == type( self._label.setText ) then + self._label:setText( newLabel ) + else + self._label.text = newLabel + end + + -- Labels position + if "center" == opt.labelAlign then + viewLabel.x = view.x + opt.labelXOffset + elseif "left" == opt.labelAlign then + viewLabel.x = view.x - ( view.contentWidth * 0.5 ) + ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + elseif "right" == opt.labelAlign then + viewLabel.x = view.x + ( view.contentWidth * 0.5 ) - ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + end + + + if "center" == opt.labelAlignY then + viewLabel.y = view.y + opt.labelYOffset + elseif "top" == opt.labelAlignY then + viewLabel.y = view.y - ( view.contentHeight * 0.5 ) + ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + elseif "bottom" == opt.labelAlignY then + + viewLabel.y = view.y + ( view.contentHeight * 0.5 ) - ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + end + + end + + -- Function to get the panel's label + function view:_getLabel() + return self._label.text + end + + + -- Finalize function + function panel:_finalize() + end + + return panel +end + + +------------------------------------------------------------------------ +-- Image Sheet (2 Frame) Panel +------------------------------------------------------------------------ + +-- Creates a new panel from a sprite (imageSheet) +local function createUsingImageSheet( panel, options ) + -- Create a local reference to our options table + local opt = options + + -- Animation options + local sheetOptions = + { + { + name = "default", + start = opt.defaultFrame, + count = 1, + }, + } + + -- Forward references + local view, viewLabel, imageSheet + + -- Create a reference to the imageSheet + imageSheet = opt.sheet + + -- Create the view + view = display.newSprite( panel, imageSheet, sheetOptions ) + + -- Create the label (either embossed or standard) + if opt.embossedLabel then + viewLabel = display.newEmbossedText( panel, opt.label, 0, 0, opt.font, opt.fontSize ) + else + viewLabel = display.newText( panel, opt.label, 0, 0, opt.font, opt.fontSize ) + end + + ---------------------------------- + -- Positioning + ---------------------------------- + + -- The view + view.x = panel.x + ( view.contentWidth * 0.5 ) + view.y = panel.y + ( view.contentHeight * 0.5 ) + + -- Setup the label + viewLabel:setFillColor( unpack( opt.labelColor) ) + viewLabel._isLabel = true + viewLabel._labelColor = opt.labelColor + + + -- Labels position + if "center" == opt.labelAlign then + viewLabel.x = view.x + opt.labelXOffset + elseif "left" == opt.labelAlign then + viewLabel.x = view.x - ( view.contentWidth * 0.5 ) + ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + elseif "right" == opt.labelAlign then + viewLabel.x = view.x + ( view.contentWidth * 0.5 ) - ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + end + + + if "center" == opt.labelAlignY then + viewLabel.y = view.y + opt.labelYOffset + elseif "top" == opt.labelAlignY then + viewLabel.y = view.y - ( view.contentHeight * 0.5 ) + ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + elseif "bottom" == opt.labelAlignY then + + viewLabel.y = view.y + ( view.contentHeight * 0.5 ) - ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + end + + + ------------------------------------------------------- + -- Assign properties/objects to the view + ------------------------------------------------------- + + view._isEnabled = opt.isEnabled + view._fontSize = opt.fontSize + view._label = viewLabel + view._labelColor = viewLabel._labelColor + view._labelAlign = opt.labelAlign + + view._labelXOffset = opt.labelXOffset + view._labelYOffset = opt.labelYOffset + + -- Methods + view._onPress = opt.onPress + view._onRelease = opt.onRelease + view._onEvent = opt.onEvent + + ------------------------------------------------------- + -- Assign properties/objects to the panel + ------------------------------------------------------- + + -- Assign objects to the panel + panel._imageSheet = imageSheet + panel._view = view + + ---------------------------------------------------------- + -- PUBLIC METHODS + ---------------------------------------------------------- + + -- Function to set the panels fill color + function panel:setFillColor( ... ) + self._view:setFillColor( ... ) + end + + -- Function to set the panel's label + function panel:setLabel( newLabel ) + return self._view:_setLabel( newLabel ) + end + + -- Function to get the panel's label + function panel:getLabel() + return self._view:_getLabel() + end + + -- Function to set a panel as active + function panel:setEnabled( isEnabled ) + self._view._isEnabled = isEnabled + end + + -- Touch listener for our panel + function view:touch( event ) + -- Set the target to the view's parent group (the panel object) + event.target = self.parent + + -- Manage touch events on the panel + managePanelTouch( self, event ) + + return true + end + + view:addEventListener( "touch" ) + + ---------------------------------------------------------- + -- PRIVATE METHODS + ---------------------------------------------------------- + + -- Function to set the panel's label + function view:_setLabel( newLabel ) + -- Update the label's text + if "function" == type( self._label.setText ) then + self._label:setText( newLabel ) + else + self._label.text = newLabel + end + + -- Labels position + if "center" == opt.labelAlign then + viewLabel.x = view.x + opt.labelXOffset + elseif "left" == opt.labelAlign then + self._label.x = view.x - ( view.contentWidth * 0.5 ) + ( self._label.contentWidth * 0.5 ) + opt.labelXOffset + elseif "right" == opt.labelAlign then + self._label.x = view.x + ( view.contentWidth * 0.5 ) - ( self._label.contentWidth * 0.5 ) + opt.labelXOffset + end + + + if "center" == opt.labelAlignY then + self._label.y = view.y + opt.labelYOffset + elseif "top" == opt.labelAlignY then + self._label.y = view.y - ( view.contentHeight * 0.5 ) + ( self._label.contentHeight * 0.5 ) + opt.labelYOffset + elseif "bottom" == opt.labelAlignY then + + self._label.y = view.y + ( view.contentHeight * 0.5 ) - ( self._label.contentHeight * 0.5 ) + opt.labelYOffset + end + + -- Update the label's y position + self._label.y = self._label.y + end + + -- Function to get the panel's label + function view:_getLabel() + return self._label.text + end + + -- Finalize function + function panel:_finalize() + -- Set the ImageSheet to nil + self._imageSheet = nil + end + + return panel +end + + +------------------------------------------------------------------------ +-- Image Sheet (9 piece/slice) Panel +------------------------------------------------------------------------ + +-- Creates a new panel from a 9 piece sprite +local function createUsing9Slice( panel, options ) + -- Create a local reference to our options table + local opt = options + + -- Forward references + local imageSheet, view, viewLabel + + local viewTopLeft, viewMiddleLeft, viewBottomLeft + local viewTopMiddle, viewMiddle, viewBottomMiddle + local viewTopRight, viewMiddleRight, viewBottomRight + local viewTopPointer, viewLabelLeft, viewLabelMiddle, viewLabelRight + + -- Create the imageSheet + if opt.sheet then + imageSheet = opt.sheet + else + local themeData = require( opt.themeData ) + imageSheet = graphics.newImageSheet( opt.themeSheetFile, themeData:getSheet() ) + end + + -- The view is the panel (group) + view = panel + + -- Imagesheet options + local sheetOptions = + { + -- Top Left + viewTopLeft = + { + { + --name = "default", + start = opt.topLeftFrame, + count = 1, + }, + + + }, + + -- Middle Left + viewMiddleLeft = + { + { + --name = "default", + start = opt.middleLeftFrame, + count = 1, + }, + + }, + + -- Bottom Left + viewBottomLeft = + { + { + --name = "default", + start = opt.bottomLeftFrame, + count = 1, + }, + + }, + + -- Top Middle + viewTopMiddle = + { + { + --name = "default", + start = opt.topMiddleFrame, + count = 1, + }, + + }, + viewTopPointer= + { + { + --name = "default", + start = opt.topPointerFrame, + count = 1, + }, + + }, + + -- Middle + viewMiddle = + { + { + --name = "default", + start = opt.middleFrame, + count = 1, + }, + + }, + + -- Bottom Middle + viewBottomMiddle = + { + { + --name = "default", + start = opt.bottomMiddleFrame, + count = 1, + }, + + }, + + + -- Top Right + viewTopRight = + { + { + --name = "default", + start = opt.topRightFrame, + count = 1, + }, + + }, + + -- Middle Right + viewMiddleRight = + { + { + --name = "default", + start = opt.middleRightFrame, + count = 1, + }, + + }, + + -- Bottom Right + viewBottomRight = + { + { + --name = "default", + start = opt.bottomRightFrame, + count = 1, + }, + + }, + } + + + -- Create the left portion of the panel + viewTopLeft = display.newSprite( panel, imageSheet, sheetOptions.viewTopLeft ) + viewMiddleLeft = display.newSprite( panel, imageSheet, sheetOptions.viewMiddleLeft ) + viewBottomLeft = display.newSprite( panel, imageSheet, sheetOptions.viewBottomLeft ) + + -- Create the right portion of the panel + viewTopRight = display.newSprite( panel, imageSheet, sheetOptions.viewTopRight ) + viewMiddleRight = display.newSprite( panel, imageSheet, sheetOptions.viewMiddleRight ) + viewBottomRight = display.newSprite( panel, imageSheet, sheetOptions.viewBottomRight ) + + -- Create the middle portion of the panel + viewTopMiddle = display.newSprite( panel, imageSheet, sheetOptions.viewTopMiddle ) + if opt.pointerX then + viewTopMiddle2 = display.newSprite( panel, imageSheet, sheetOptions.viewTopMiddle ) + viewPointer = display.newSprite( panel, imageSheet, sheetOptions.viewTopPointer ) + end + viewMiddle = display.newSprite( panel, imageSheet, sheetOptions.viewMiddle ) + viewBottomMiddle = display.newSprite( panel, imageSheet, sheetOptions.viewBottomMiddle ) + + -- Create the label (either embossed or standard) + if opt.embossedLabel then + viewLabel = display.newEmbossedText( panel, opt.label, 0, 0, opt.font, opt.fontSize ) + else + viewLabel = display.newText( panel, opt.label, 0, 0, opt.font, opt.fontSize ) + end + + ---------------------------------- + -- Positioning + ---------------------------------- + + -- Top + viewTopLeft.x = panel.x + ( viewTopLeft.contentWidth * 0.5 ) + viewTopLeft.y = panel.y + ( viewTopLeft.contentHeight * 0.5 ) + local middleWidth = opt.width - ( viewTopLeft.contentWidth + viewTopRight.contentWidth ); + + if opt.pointerX then + viewPointer.x = panel.x + opt.pointerX; + viewPointer.y = viewPointer.contentHeight / 2 + viewTopMiddle.width = math.max(opt.pointerX - viewTopLeft.contentWidth - viewPointer.contentWidth / 2, + viewTopMiddle.contentWidth); + + + viewTopMiddle2.width = opt.width - viewTopRight.contentWidth - viewTopLeft.contentWidth - + viewPointer.contentHeight - viewTopMiddle.width; + + viewTopMiddle2.x = viewPointer.x + viewPointer.contentWidth / 2 + viewTopMiddle2.contentWidth / 2; + viewTopMiddle2.y = viewTopMiddle2.contentHeight / 2; + else + + viewTopMiddle.width = middleWidth + end + viewTopMiddle.y = viewTopMiddle.contentHeight / 2; + viewTopMiddle.x = viewTopLeft.x + ( viewTopLeft.contentWidth * 0.5 ) + ( viewTopMiddle.contentWidth * 0.5 ) + viewTopRight.x = panel.x + opt.width - ( viewTopRight.contentWidth * 0.5 ) + viewTopRight.y = viewTopLeft.y + + -- Middle + viewMiddleLeft.height = opt.height - ( viewTopLeft.contentHeight + viewTopRight.contentHeight ) + viewMiddleLeft.x = viewTopLeft.x + viewMiddleLeft.y = viewTopLeft.y + ( viewMiddleLeft.contentHeight * 0.5 ) + ( viewBottomLeft.contentHeight * 0.5 ) + + + viewMiddle.width = middleWidth + viewMiddle.height = opt.height - ( viewTopLeft.contentHeight + ( viewTopRight.contentHeight ) ) + viewMiddle.x = viewTopLeft.x + ( viewTopLeft.contentWidth * 0.5 ) + (viewMiddle.contentWidth * 0.5 ) + viewMiddle.y = viewTopMiddle.contentHeight + ( viewMiddle.contentHeight * 0.5 ) + + viewMiddleRight.height = opt.height - ( viewTopLeft.contentHeight + viewTopRight.contentHeight ) + viewMiddleRight.x = viewTopRight.x + viewMiddleRight.y = viewTopRight.y + ( viewMiddleRight.contentHeight * 0.5 ) + ( viewBottomRight.contentHeight * 0.5 ) + + -- Bottom + viewBottomLeft.x = viewTopLeft.x + viewBottomLeft.y = viewMiddle.y + ( viewMiddle.contentHeight * 0.5 ) + ( viewBottomLeft.contentHeight * 0.5 ) + + viewBottomMiddle.width = middleWidth + viewBottomMiddle.x = viewBottomLeft.x + ( viewBottomLeft.contentWidth * 0.5 ) + ( viewBottomMiddle.contentWidth * 0.5 ) + viewBottomMiddle.y = viewMiddle.y + ( viewMiddle.contentHeight * 0.5 ) + ( viewBottomMiddle.contentHeight * 0.5 ) + + viewBottomRight.x = viewTopRight.x + viewBottomRight.y = viewMiddle.y + ( viewMiddle.contentHeight * 0.5 ) + ( viewBottomRight.contentHeight * 0.5 ) + + -- If the passed width is less than the topLeft & top right width then don't use the middle pieces + if opt.width <= ( viewTopLeft.contentWidth + viewTopRight.contentWidth ) then + -- Hide the middle slices + viewTopMiddle.isVisible = false + viewMiddle.isVisible = false + viewBottomMiddle.isVisible = false + viewTopMiddle2.isVisible = false + viewPointer.isVisible = false + -- Re-position slices + viewTopRight.x = viewTopLeft.x + ( viewTopLeft.contentWidth * 0.5 ) + ( viewBottomRight.contentWidth * 0.5 ) + viewMiddleRight.x = viewTopLeft.x + ( viewTopLeft.contentWidth * 0.5 ) + ( viewBottomRight.contentWidth * 0.5 ) + viewBottomRight.x = viewTopLeft.x + ( viewTopLeft.contentWidth * 0.5 ) + ( viewBottomRight.contentWidth * 0.5 ) + end + + -- If the passed height is less than the topLeft & top right height then don't use the middle pieces + if opt.height <= ( viewTopLeft.contentHeight + viewTopRight.contentHeight ) then + if opt.width <= ( viewTopLeft.contentWidth + viewTopRight.contentWidth ) then + -- Hide the middle slices + viewMiddleRight.isVisible = false + viewMiddleLeft.isVisible = false + viewMiddle.isVisible = false + viewTopMiddle.isVisible = false + viewTopMiddle2.isVisible = false + viewPointer.isVisible = false + viewBottomMiddle.isVisible = false + + -- Re-position slices + viewTopRight.x = viewTopLeft.x + ( viewTopLeft.contentWidth * 0.5 ) + ( viewTopRight.contentWidth * 0.5 ) + viewBottomLeft.y = viewTopLeft.y + ( viewTopLeft.contentHeight * 0.5 ) + ( viewBottomLeft.contentHeight * 0.5 ) + viewBottomRight.x = viewTopLeft.x + ( viewTopLeft.contentWidth * 0.5 ) + ( viewTopRight.contentWidth * 0.5 ) + viewBottomRight.y = viewTopLeft.y + ( viewTopLeft.contentHeight * 0.5 ) + ( viewBottomRight.contentHeight * 0.5 ) + + else + -- Hide the middle slices + viewMiddle.isVisible = false + viewMiddleRight.isVisible = false + viewMiddleLeft.isVisible = false + + -- Re-position slices + viewBottomLeft.y = viewTopLeft.y + ( viewTopLeft.contentHeight * 0.5 ) + ( viewBottomLeft.contentHeight * 0.5 ) + viewBottomMiddle.y = viewTopLeft.y + ( viewTopLeft.contentHeight * 0.5 ) + ( viewBottomLeft.contentHeight * 0.5 ) + viewBottomRight.y = viewTopLeft.y + ( viewTopLeft.contentHeight * 0.5 ) + ( viewBottomRight.contentHeight * 0.5 ) + + end + end + + -- Setup the Label + viewLabel:setFillColor( unpack( opt.labelColor ) ) + viewLabel._isLabel = true + viewLabel._labelColor = opt.labelColor + + + -- Labels position + if "center" == opt.labelAlign then + viewLabel.x = view.x + ( opt.width * 0.5 ) + opt.labelXOffset + elseif "left" == opt.labelAlign then + viewLabel.x = view.x - ( opt.width * 0.5 ) + ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + elseif "right" == opt.labelAlign then + viewLabel.x = view.x + ( opt.width * 0.5 ) - ( viewLabel.contentWidth * 0.5 ) + opt.labelXOffset + end + + + if "center" == opt.labelAlignY then + viewLabel.y = view.y + opt.height / 2 + opt.labelYOffset + elseif "top" == opt.labelAlignY then + viewLabel.y = view.y + ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + elseif "bottom" == view.y + viewLabel.contentHeight -viewLabel.contentWidth * 0.5 -opt.labelAlignY then + + viewLabel.y = view.y + ( view.contentHeight * 0.5 ) - ( viewLabel.contentHeight * 0.5 ) + opt.labelYOffset + end + + -- The label's y position + local minHeight = opt.height + if opt.height < 30 then + minHeight = 30 + end + + + ------------------------------------------------------- + -- Assign properties/objects to the view + ------------------------------------------------------- + + view._isEnabled = opt.isEnabled + view._width = opt.width + view._fontSize = opt.fontSize + view._labelAlign = opt.labelAlign + view._labelXOffset = opt.labelXOffset + view._labelYOffset = opt.labelYOffset + view._label = viewLabel + + -- Methods + view._onPress = opt.onPress + view._onRelease = opt.onRelease + view._onEvent = opt.onEvent + + ------------------------------------------------------- + -- Assign properties/objects to the panel + ------------------------------------------------------- + + -- Assign objects to the panel + panel._imageSheet = imageSheet + panel._view = view + + ---------------------------------------------------------- + -- PUBLIC METHODS + ---------------------------------------------------------- + + -- Function to set the panels fill color + function panel:setFillColor( ... ) + for i = self.numChildren, 1, -1 do + if "function" == type( self[i].setFillColor ) then + self[i]:setFillColor( ... ) + end + end + end + + -- Function to set the panel's label + function panel:setLabel( newLabel ) + return self._view:_setLabel( newLabel ) + end + + -- Function to get the panel's label + function panel:getLabel() + return self._view:_getLabel() + end + + -- Function to set a panel as active + function panel:setEnabled( isEnabled ) + self._view._isEnabled = isEnabled + end + + -- Touch listener for our panel + function view:touch( event ) + -- Manage touch events on the panel + managePanelTouch( self, event ) + + return true + end + + view:addEventListener( "touch" ) + + ---------------------------------------------------------- + -- PRIVATE METHODS + ---------------------------------------------------------- + + -- Function to set the panel's label + function view:_setLabel( newLabel ) + if "function" == type( self._label.setText ) then + self._label:setText( newLabel ) + else + self._label.text = newLabel + end + + -- Labels position + if "center" == opt.labelAlign then + self._label.x = view.x + opt.labelXOffset + elseif "left" == opt.labelAlign then + self._label.x = view.x - ( view.contentWidth * 0.5 ) + ( self._label.contentWidth * 0.5 ) + opt.labelXOffset + elseif "right" == opt.labelAlign then + self._label.x = view.x + ( view.contentWidth * 0.5 ) - ( self._label.contentWidth * 0.5 ) + opt.labelXOffset + end + + + if "center" == opt.labelAlignY then + self._label.y = view.y + opt.labelYOffset + elseif "top" == opt.labelAlignY then + self._label.y = view.y - ( view.contentHeight * 0.5 ) + ( self._label.contentHeight * 0.5 ) + opt.labelYOffset + elseif "bottom" == opt.labelAlignY then + + self._label.y = view.y + ( view.contentHeight * 0.5 ) - ( self._label.contentHeight * 0.5 ) + opt.labelYOffset + end + end + + -- Function to get the panel's label + function view:_getLabel() + return self._label.text + end + + -- Finalize function + function panel:_finalize() + -- Set the ImageSheet to nil + self._imageSheet = nil + end + + return panel +end + + +-- Function to create a new panel object ( widget.newPanel ) +function M.new( options, theme ) + local customOptions = options or {} + local themeOptions = theme or {} + + -- Create a local reference to our options table + local opt = M._options + + -- Check if the requirements for creating a widget has been met (throws an error if not) + _widget._checkRequirements( customOptions, themeOptions, M._widgetName ) + + ------------------------------------------------------- + -- Properties + ------------------------------------------------------- + + -- Positioning & properties + opt.left = customOptions.left or 0 + opt.top = customOptions.top or 0 + opt.x = customOptions.x or nil + opt.y = customOptions.y or nil + if customOptions.x and customOptions.y then + opt.left = 0 + opt.top = 0 + end + opt.width = customOptions.width or themeOptions.width + opt.height = customOptions.height or themeOptions.height + opt.id = customOptions.id + opt.baseDir = customOptions.baseDir or system.ResourceDirectory + opt.label = customOptions.label or "" + opt.labelColor = customOptions.labelColor or panelDefault + opt.font = customOptions.font or themeOptions.font or native.systemFont + opt.fontSize = customOptions.fontSize or themeOptions.fontSize or 14 + + if _widget.isSeven() then + opt.font = customOptions.font or themeOptions.font or "HelveticaNeue-Light" + opt.fontSize = customOptions.fontSize or themeOptions.fontSize or 17 + opt.labelColor = customOptions.labelColor or themeOptions.labelColor or panelDefault + end + + opt.labelAlign = customOptions.labelAlign or "center" + opt.labelAlignY = customOptions.labelAlignY or "center" + opt.labelXOffset = customOptions.labelXOffset or 0 + opt.labelYOffset = customOptions.labelYOffset or 0 + opt.embossedLabel = customOptions.emboss or themeOptions.emboss or false + opt.isEnabled = customOptions.isEnabled + opt.textOnlyPanel = customOptions.textOnly or false + opt.pointerX = customOptions.pointerX or nil; + + opt.shape = customOptions.shape or nil; + if opt.shape then + + opt.shape.cornerRadius = opt.shape.cornerRadius or 0; + opt.shape.strokeWidth = opt.shape.strokeWidth or 1; + opt.shape.strokeColor = opt.shape.strokeColor or shapeStrokeDefault; + opt.shape.fillColor = opt.shape.fillColor or shapeFillDefault; + end; + + -- If the user didn't pass in a isEnabled flag, set it to true + if nil == opt.isEnabled then + opt.isEnabled = true + end + + opt.onPress = customOptions.onPress + opt.onRelease = customOptions.onRelease + opt.onEvent = customOptions.onEvent + + -- Frames & Images + opt.sheet = customOptions.sheet + opt.themeSheetFile = themeOptions.sheet + opt.themeData = themeOptions.data + + -- Single image files + opt.defaultFile = customOptions.defaultFile + + if opt.defaultFile then + opt.width = customOptions.width + opt.height = customOptions.height + end + + -- ImageSheet ( 2 frame panel ) + opt.defaultFrame = customOptions.defaultFrame or _widget._getFrameIndex( themeOptions, themeOptions.defaultFrame ) + + -- Left ( 9 piece set ) + opt.topLeftFrame = customOptions.topLeftFrame or _widget._getFrameIndex( themeOptions, themeOptions.topLeftFrame ) + opt.middleLeftFrame = customOptions.middleLeftFrame or _widget._getFrameIndex( themeOptions, themeOptions.middleLeftFrame ) + + opt.bottomLeftFrame = customOptions.bottomLeftFrame or _widget._getFrameIndex( themeOptions, themeOptions.bottomLeftFrame ) + + -- Right ( 9 piece set ) + opt.topRightFrame = customOptions.topRightFrame or _widget._getFrameIndex( themeOptions, themeOptions.topRightFrame ) + + opt.middleRightFrame = customOptions.middleRightFrame or _widget._getFrameIndex( themeOptions, themeOptions.middleRightFrame ) + + opt.bottomRightFrame = customOptions.bottomRightFrame or _widget._getFrameIndex( themeOptions, themeOptions.bottomRightFrame ) + + -- Middle ( 9 piece set ) + opt.topMiddleFrame = customOptions.topMiddleFrame or _widget._getFrameIndex( themeOptions, themeOptions.topMiddleFrame ) + opt.topPointerFrame= customOptions.topPointerFrame or _widget._getFrameIndex( themeOptions, themeOptions.topPointerFrame ) + + opt.middleFrame = customOptions.middleFrame or _widget._getFrameIndex( themeOptions, themeOptions.middleFrame ) + + opt.bottomMiddleFrame = customOptions.bottomMiddleFrame or _widget._getFrameIndex( themeOptions, themeOptions.bottomMiddleFrame ) + + -- Are we using a nine piece panel? + local using9PiecePanel = not opt.defaultFrame and not opt.defaultFile and not opt.textOnlyPanel and not opt.shape and opt.topLeftFrame and opt.middleLeftFrame and opt.bottomLeftFrame and + opt.topRightFrame and opt.middleRightFrame and opt.bottomRightFrame and + opt.topMiddleFrame and opt.middleFrame and opt.bottomMiddleFrame + + -- If we are using a 9-piece panel and have not passed in an imageSheet, throw an error + local isUsingSheet = opt.sheet or opt.themeSheetFile + + -- If were using a 9 piece/slice panel and have not passed a width/height + if using9PiecePanel and not opt.width then + error( "ERROR: " .. M._widgetName .. ": width expected, got nil", 3 ) + elseif using9PiecePanel and not opt.height then + error( "ERROR: " .. M._widgetName .. ": height expected, got nil", 3 ) + end + + if using9PiecePanel and not isUsingSheet then + error( "ERROR: " .. M._widgetName .. ": 9 piece frame or default frame definition expected, got nil", 3 ) + end + + -- Are we using a 2 frame panel? + local using2FramePanel = not using9PiecePanel and opt.defaultFrame + + -- If we are using a 2 frame panel and have not passed in an imageSheet, throw an error + if using2FramePanel and not opt.sheet then + error( "ERROR: " .. M._widgetName .. ": sheet definition expected, got nil", 3 ) + end + + + -- Turn off theme setting for text emboss if the user isn't using a theme + if "boolean" == type( customOptions.emboss ) then + opt.embossedLabel = customOptions.emboss + end + + --[[ + Notes: + *) A 9-piece/slice panel is favored over a 2 frame panel. + *) A 2 frame panel is favored over a 2 file panel. + --]] + + -- Favor nine piece panel over single image panel + if using9PiecePanel then + opt.defaultFrame = nil + + end + + -- Favor 2 frame panel over 2 file panel + if using2FramePanel then + opt.defaultFile = nil + + end + + ------------------------------------------------------- + -- Create the panel + ------------------------------------------------------- + + -- Create the panel object + local panel = _widget._new + { + left = opt.left, + top = opt.top, + id = opt.id or "widget_panel", + baseDir = opt.baseDir, + widgetType = "panel", + } + + -- Create the panel + if using9PiecePanel then + -- If we are using a 9 piece panel + createUsing9Slice( panel, opt ) + else + -- If using a 2 frame panel + if using2FramePanel then + createUsingImageSheet( panel, opt ) + end + + -- If using 2 images + if opt.defaultFile then + createUsingImageFiles( panel, opt ) + end + + -- Text only panel + if opt.textOnlyPanel or opt.shape then + createUsingText( panel, opt ) + end + end + + -- Set the panel's position ( set the reference point to center, just to be sure ) + if ( isGraphicsV1 ) then + panel:setReferencePoint( display.CenterReferencePoint ) + end + local x, y = _widget._calculatePosition( panel, opt ) + panel.x, panel.y = x, y + + + return panel +end + +return M diff --git a/widgetLibrary/widget_pickerWheel.lua b/widgetLibrary/widget_pickerWheel.lua index f8e9cec..48720b3 100644 --- a/widgetLibrary/widget_pickerWheel.lua +++ b/widgetLibrary/widget_pickerWheel.lua @@ -28,8 +28,8 @@ local M = { - _options = {}, - _widgetName = "widget.newPickerWheel", + _options = {}, + _widgetName = "widget.newPickerWheel", } -- Require needed widget files @@ -43,434 +43,446 @@ local defaultRowColor = { 1 } local blackColor = { 0 } if isByteColorRange then - _widget._convertColorToV1( labelColor ) - _widget._convertColorToV1( defaultRowColor ) - _widget._convertColorToV1( blackColor ) + _widget._convertColorToV1( labelColor ) + _widget._convertColorToV1( defaultRowColor ) + _widget._convertColorToV1( blackColor ) end -- Creates a new pickerWheel local function createPickerWheel( pickerWheel, options ) - -- Create a local reference to our options table - local opt = options - - -- Forward references - local imageSheet, view, viewBackground, viewOverlay, viewColumns - - -- Create the imageSheet - if opt.sheet then - imageSheet = opt.sheet - else - local themeData = require( opt.themeData ) - imageSheet = graphics.newImageSheet( opt.themeSheetFile, themeData:getSheet() ) - end - - -- Create the view - view = display.newGroup() - - -- The view's background - viewOverlay = display.newImageRect( pickerWheel, imageSheet, opt.overlayFrame, opt.overlayFrameWidth, opt.overlayFrameHeight ) - - ---------------------------------- - -- Properties - ---------------------------------- - - -- The table which holds our pickerWheel columns - viewColumns = {} - - ------------------------------------------------------- - -- Assign properties to the view - ------------------------------------------------------- - - -- Assign properties to our view - view._width = opt.overlayFrameWidth - view._height = opt.overlayFrameHeight - view._top = opt.top - view._yPosition = pickerWheel.y + ( view._height * 0.5 ) - - -- Assign objects to our view - view._overlay = viewOverlay - view._background = viewBackground - view._columns = viewColumns - view._didTap = false - - ------------------------------------------------------- - -- Assign properties/objects to the pickerWheel - ------------------------------------------------------- - - -- Assign objects to the pickerWheel - pickerWheel._imageSheet = imageSheet - pickerWheel._view = view - pickerWheel:insert( view ) - - -- Function to render the pickerWheels columns - local function _renderColumns( event ) - local phase = event.phase - local row = event.row - local fontSize = event.target._fontSize - local alignment = event.target._align - - -- Create the column's title text - local rowTitle = display.newText( row, row._label, 0, 0, opt.font, fontSize ) - rowTitle.y = row.contentHeight * 0.5 - - if _widget.isSeven() and row.index == pickerWheel._view._columns[row.id]._values.index then - rowTitle:setFillColor( 0 ) - else - rowTitle:setFillColor( unpack( opt.fontColor ) ) - end - - row.value = rowTitle.text - - -- check if the text is greater than the actual column size - local availableWidth = viewOverlay.width - 28 - local columnWidth = view._columns[ row.id ].width or availableWidth / #view._columns - local textWidth = rowTitle.contentWidth - if textWidth > columnWidth - 1 then - --cap the text - local pixelsPerChar = 23 -- aproximate median value - local numChars = columnWidth / pixelsPerChar - row._label = row._label:sub(1, numChars) - rowTitle.text = row._label - - end - - -- Align the text as requested - if "center" == alignment then - - local rowTitleX - if isGraphicsV1 then - rowTitleX = row.x - else - rowTitleX = row.x + columnWidth * 0.5 - end - rowTitle.x = rowTitleX - - elseif "left" == alignment then - - local rowTitleX - if isGraphicsV1 then - rowTitleX = ( rowTitle.contentWidth * 0.5 ) + 6 - else - rowTitleX = row.x + 6 - rowTitle.anchorX = 0 - end - rowTitle.x = rowTitleX - - elseif "right" == alignment then - - local rowTitleX - if isGraphicsV1 then - rowTitleX = row.x + ( row.contentWidth * 0.5 ) - ( rowTitle.contentWidth * 0.5 ) - 6 - else - rowTitleX = row.x + columnWidth - 6 - rowTitle.anchorX = 1 - end - rowTitle.x = rowTitleX - - end - - - end - - -- Create a background to sit behind the pickerWheel - viewBackground = display.newImageRect( view, imageSheet, opt.backgroundFrame, opt.overlayFrameWidth, opt.overlayFrameHeight ) - viewBackground.x = viewOverlay.x - viewBackground.y = viewOverlay.y - - -- Function to create the column seperator - function view:_createSeperator( x ) - local seperator = display.newImageRect( self, imageSheet, opt.seperatorFrame, opt.seperatorFrameWidth + 4, opt.backgroundFrameHeight ) - seperator.x = x - - return seperator - end - - -- The available width for the whole pickerWheel (to fit columns) - local availableWidth = viewOverlay.width - 28 - - -- local method that handles scrolling to the tapped / touched index - function didTapValue( event ) - local phase = event.phase - local row = event.target - if "tap" == phase or "release" == phase then - view._columns[ row.id ]:scrollToIndex( row.index ) - view._didTap = true - end - end - - -- Create the pickerWheel Columns (which are tableView's) - local topPadding = 84 - local bottomPadding = 96 - if isGraphicsV1 then - topPadding = 90 - bottomPadding = 92 - end - - local initialX = 0 - local initialPos = - 144 - - for i = 1, #opt.columnData do - - if i > 1 then - initialPos = viewColumns[i-1].x + viewColumns[i-1]._view._width * 0.5 - end - - viewColumns[i] = _widget.newTableView - { - left = initialPos, - top = -110, - width = opt.columnData[i].width or availableWidth / #opt.columnData, - height = opt.overlayFrameHeight - 1, - topPadding = topPadding, - bottomPadding = bottomPadding, - noLines = true, - hideBackground = false, - hideScrollBar = true, - friction = 0.92, - rowColor = opt.columnColor, - backgroundColor = opt.backgroundColor or defaultRowColor, - onRowRender = _renderColumns, - maskFile = opt.maskFile, - listener = nil, - onRowTouch = didTapValue - } - viewColumns[i]._view._isUsedInPickerWheel = true - - -- Column properties - viewColumns[i]._align = opt.columnData[i].align - viewColumns[i]._fontSize = opt.fontSize - - -- Set the volumns initial values - viewColumns[i]._values = - { - index = opt.columnData[i].startIndex, - value = opt.columnData[i].labels[opt.columnData[i].startIndex], - } - - -- Create the columns row's - for j = 1, #opt.columnData[i].labels do - viewColumns[i]:insertRow - { - rowHeight = 40, - rowColor = { - default = opt.columnColor, - over = opt.columnColor, - }, - label = opt.columnData[i].labels[j], - id = i - } - end - - -- Insert the pickerWheel column into the view - view:insert( viewColumns[i] ) - - -- Scroll to the defined index -- TODO needs failsafe - viewColumns[i]:scrollToIndex( opt.columnData[i].startIndex, 0 ) - end - - -- Create the column seperators - for i = 1, #opt.columnData - 1 do - view:_createSeperator( viewColumns[i].x + viewColumns[i]._view._width * 0.5 ) - end - - -- Push the view's background to the front. - viewOverlay:toFront() - - ---------------------------------------------------------- - -- PUBLIC METHODS - ---------------------------------------------------------- - - -- Function to retrieve the column values - function pickerWheel:getValues() - return self._view:_getValues() - end - - -- Function to scroll to a specific pickerWheel column row - function pickerWheel:scrollToIndex( ... ) - local arg = { ... } - - local column = nil - - -- If the first arg is a number, set the column to that - if "number" == type( arg[1] ) then - column = arg[1] - - -- We have retrieved the column, now set arg1 to arg2 (which is the index to scroll to) so scrollTo index gets called as expected - arg[1] = arg[2] - end - - arg[4] = self._view:_getValues() - - -- Scroll to the specified column index - return self._view._columns[column]:scrollToIndex( unpack( arg ) ) - end - - ---------------------------------------------------------- - -- PRIVATE METHODS - ---------------------------------------------------------- - - -- Override scale function as pickerWheels don't support it - function pickerWheel:scale() - print( M._widgetName, "Does not support scaling" ) - end - - -- EnterFrame listener for our pickerWheel - function view:enterFrame( event ) - local _pickerWheel = self.parent - - -- Update the y position - self._yPosition = _pickerWheel.y + ( self._height * 0.5 ) - - - - -- Manage the Picker Wheels columns - for i = 1, #self._columns do - - if "ended" == self._columns[i]._view._phase and not self._columns[i]._view._updateRuntime then - if not self._didTap then - local calculatePosition = self._yPosition - self.parent.contentHeight * 0.5 - if isGraphicsV1 then - calculatePosition = self._yPosition - end - self._columns[i]._values = self._columns[i]._view:_getRowAtPosition( calculatePosition ) - else - self._columns[i]._values = self._columns[i]._view:_getRowAtIndex( self._columns[ i ]._view._lastRowIndex ) - self._didTap = false - end - self._columns[i]._view._phase = "none" - - -- update the actual values, by rerendering row - if _widget.isSeven() and nil ~= self._columns[i]._values then - self._columns[i]._view._rows[self._columns[i]._values.index]._view[ 2 ]:setFillColor( 0 ) - end - end - end - - -- Constrain x/y scale values to 1.0 - if _pickerWheel.xScale ~= 1.0 then - _pickerWheel.xScale = 1.0 - print( M._widgetName, "Does not support scaling" ) - end - - if _pickerWheel.yScale ~= 1.0 then - _pickerWheel.yScale = 1.0 - print( M._widgetName, "Does not support scaling" ) - end - - return true - end - - Runtime:addEventListener( "enterFrame", view ) - - -- Function to retrieve the column values - function view:_getValues() - local values = {} - - -- Loop through all the columns and retrieve the values - for i = 1, #self._columns do - values[i] = self._columns[i]._values - end - - return values - end - - -- Finalize function for the pickerWheel - function pickerWheel:_finalize() - -- Remove the event listener - Runtime:removeEventListener( "enterFrame", self._view ) - - -- Set the ImageSheet to nil - self._imageSheet = nil - end - - return pickerWheel + -- Create a local reference to our options table + local opt = options + + -- Forward references + local imageSheet, view, viewBackground, viewOverlay, viewColumns + + -- Create the imageSheet + if opt.sheet then + imageSheet = opt.sheet + else + local themeData = require( opt.themeData ) + imageSheet = graphics.newImageSheet( opt.themeSheetFile, themeData:getSheet() ) + end + + -- Create the view + view = display.newGroup() + + -- The view's background + viewOverlay = display.newImageRect( pickerWheel, imageSheet, opt.overlayFrame, opt.overlayFrameWidth, opt.overlayFrameHeight ) + + ---------------------------------- + -- Properties + ---------------------------------- + + -- The table which holds our pickerWheel columns + viewColumns = {} + + ------------------------------------------------------- + -- Assign properties to the view + ------------------------------------------------------- + + -- Assign properties to our view + view._width = opt.overlayFrameWidth + view._height = opt.overlayFrameHeight + view._top = opt.top + view._yPosition = pickerWheel.y + ( view._height * 0.5 ) + + -- Assign objects to our view + view._overlay = viewOverlay + view._background = viewBackground + view._columns = viewColumns + view._didTap = false + if "function" == type(opt.onScroll) then + view._onScroll = opt.onScroll; + end; + + ------------------------------------------------------- + -- Assign properties/objects to the pickerWheel + ------------------------------------------------------- + + -- Assign objects to the pickerWheel + pickerWheel._imageSheet = imageSheet + pickerWheel._view = view + pickerWheel:insert( view ) + + -- Function to render the pickerWheels columns + local function _renderColumns( event ) + local phase = event.phase + local row = event.row + local fontSize = event.target._fontSize + local alignment = event.target._align + + -- Create the column's title text + local rowTitle = display.newText( row, row._label, 0, 0, opt.font, fontSize ) + rowTitle.y = row.contentHeight * 0.5 + + if _widget.isSeven() and row.index == pickerWheel._view._columns[row.id]._values.index then + rowTitle:setFillColor( 0 ) + else + rowTitle:setFillColor( unpack( opt.fontColor ) ) + end + + row.value = rowTitle.text + + -- check if the text is greater than the actual column size + local availableWidth = viewOverlay.width - 28 + local columnWidth = view._columns[ row.id ].width or availableWidth / #view._columns + local textWidth = rowTitle.contentWidth + if textWidth > columnWidth - 1 then + --cap the text + local pixelsPerChar = 23 -- aproximate median value + local numChars = columnWidth / pixelsPerChar + row._label = row._label:sub(1, numChars) + rowTitle.text = row._label + + end + + -- Align the text as requested + if "center" == alignment then + + local rowTitleX + if isGraphicsV1 then + rowTitleX = row.x + else + rowTitleX = row.x + columnWidth * 0.5 + end + rowTitle.x = rowTitleX + + elseif "left" == alignment then + + local rowTitleX + if isGraphicsV1 then + rowTitleX = ( rowTitle.contentWidth * 0.5 ) + 6 + else + rowTitleX = row.x + 6 + rowTitle.anchorX = 0 + end + rowTitle.x = rowTitleX + + elseif "right" == alignment then + + local rowTitleX + if isGraphicsV1 then + rowTitleX = row.x + ( row.contentWidth * 0.5 ) - ( rowTitle.contentWidth * 0.5 ) - 6 + else + rowTitleX = row.x + columnWidth - 6 + rowTitle.anchorX = 1 + end + rowTitle.x = rowTitleX + + end + + + end + + -- Create a background to sit behind the pickerWheel + viewBackground = display.newImageRect( view, imageSheet, opt.backgroundFrame, opt.overlayFrameWidth, opt.overlayFrameHeight ) + viewBackground.x = viewOverlay.x + viewBackground.y = viewOverlay.y + + -- Function to create the column seperator + function view:_createSeperator( x ) + local seperator = display.newImageRect( self, imageSheet, opt.seperatorFrame, opt.seperatorFrameWidth + 4, opt.backgroundFrameHeight ) + seperator.x = x + + return seperator + end + + -- The available width for the whole pickerWheel (to fit columns) + local availableWidth = viewOverlay.width - 28 + + -- local method that handles scrolling to the tapped / touched index + function didTapValue( event ) + local phase = event.phase + local row = event.target + if "tap" == phase or "release" == phase then + view._columns[ row.id ]:scrollToIndex( row.index ) + view._didTap = true + end + end + + -- Create the pickerWheel Columns (which are tableView's) + local topPadding = 84 + local bottomPadding = 96 + if isGraphicsV1 then + topPadding = 90 + bottomPadding = 92 + end + + local initialX = 0 + local initialPos = - 144 + + for i = 1, #opt.columnData do + + if i > 1 then + initialPos = viewColumns[i-1].x + viewColumns[i-1]._view._width * 0.5 + end + + viewColumns[i] = _widget.newTableView + { + left = initialPos, + top = -110, + width = opt.columnData[i].width or availableWidth / #opt.columnData, + height = opt.overlayFrameHeight - 1, + topPadding = topPadding, + bottomPadding = bottomPadding, + noLines = true, + hideBackground = false, + hideScrollBar = true, + friction = 0.92, + rowColor = opt.columnColor, + backgroundColor = opt.backgroundColor or defaultRowColor, + onRowRender = _renderColumns, + maskFile = opt.maskFile, + listener = nil, + onRowTouch = didTapValue + } + viewColumns[i]._view._isUsedInPickerWheel = true + + -- Column properties + viewColumns[i]._align = opt.columnData[i].align + viewColumns[i]._fontSize = opt.fontSize + + -- Set the volumns initial values + viewColumns[i]._values = + { + index = opt.columnData[i].startIndex, + value = opt.columnData[i].labels[opt.columnData[i].startIndex], + } + + -- Create the columns row's + for j = 1, #opt.columnData[i].labels do + viewColumns[i]:insertRow + { + rowHeight = 40, + rowColor = { + default = opt.columnColor, + over = opt.columnColor, + }, + label = opt.columnData[i].labels[j], + id = i + } + end + + -- Insert the pickerWheel column into the view + view:insert( viewColumns[i] ) + + -- Scroll to the defined index -- TODO needs failsafe + viewColumns[i]:scrollToIndex( opt.columnData[i].startIndex, 0 ) + end + + -- Create the column seperators + for i = 1, #opt.columnData - 1 do + view:_createSeperator( viewColumns[i].x + viewColumns[i]._view._width * 0.5 ) + end + + -- Push the view's background to the front. + viewOverlay:toFront() + + ---------------------------------------------------------- + -- PUBLIC METHODS + ---------------------------------------------------------- + + -- Function to retrieve the column values + function pickerWheel:getValues() + return self._view:_getValues() + end + + -- Function to scroll to a specific pickerWheel column row + function pickerWheel:scrollToIndex( ... ) + local arg = { ... } + + local column = nil + + -- If the first arg is a number, set the column to that + if "number" == type( arg[1] ) then + column = arg[1] + + -- We have retrieved the column, now set arg1 to arg2 (which is the index to scroll to) so scrollTo index gets called as expected + arg[1] = arg[2] + end + + arg[4] = self._view:_getValues() + + -- Scroll to the specified column index + return self._view._columns[column]:scrollToIndex( unpack( arg ) ) + end + + ---------------------------------------------------------- + -- PRIVATE METHODS + ---------------------------------------------------------- + + -- Override scale function as pickerWheels don't support it + function pickerWheel:scale() + print( M._widgetName, "Does not support scaling" ) + end + + -- EnterFrame listener for our pickerWheel + function view:enterFrame( event ) + local _pickerWheel = self.parent + + -- Update the y position + self._yPosition = _pickerWheel.y + ( self._height * 0.5 ) + + + + -- Manage the Picker Wheels columns + for i = 1, #self._columns do + + if "ended" == self._columns[i]._view._phase and not self._columns[i]._view._updateRuntime then + if not self._didTap then + local calculatePosition = self._yPosition - self.parent.contentHeight * 0.5 + if isGraphicsV1 then + calculatePosition = self._yPosition + end + self._columns[i]._values = self._columns[i]._view:_getRowAtPosition( calculatePosition ) + else + self._columns[i]._values = self._columns[i]._view:_getRowAtIndex( self._columns[ i ]._view._lastRowIndex ) + self._didTap = false + end + if view._onScroll then + local event = + {column = i, + value = self._columns[i]._values + } + view._onScroll(event) + end + + self._columns[i]._view._phase = "none" + + -- update the actual values, by rerendering row + if _widget.isSeven() and nil ~= self._columns[i]._values then + self._columns[i]._view._rows[self._columns[i]._values.index]._view[ 2 ]:setFillColor( 0 ) + end + end + end + + -- Constrain x/y scale values to 1.0 + if _pickerWheel.xScale ~= 1.0 then + _pickerWheel.xScale = 1.0 + print( M._widgetName, "Does not support scaling" ) + end + + if _pickerWheel.yScale ~= 1.0 then + _pickerWheel.yScale = 1.0 + print( M._widgetName, "Does not support scaling" ) + end + + return true + end + + Runtime:addEventListener( "enterFrame", view ) + + -- Function to retrieve the column values + function view:_getValues() + local values = {} + + -- Loop through all the columns and retrieve the values + for i = 1, #self._columns do + values[i] = self._columns[i]._values + end + + return values + end + + -- Finalize function for the pickerWheel + function pickerWheel:_finalize() + -- Remove the event listener + Runtime:removeEventListener( "enterFrame", self._view ) + + -- Set the ImageSheet to nil + self._imageSheet = nil + end + + return pickerWheel end -- Function to create a new pickerWheel object ( widget.newPickerWheel ) -function M.new( options, theme ) - local customOptions = options or {} - local themeOptions = theme or {} - - -- Create a local reference to our options table - local opt = M._options - - -- Check if the requirements for creating a widget has been met (throws an error if not) - _widget._checkRequirements( customOptions, themeOptions, M._widgetName ) - - ------------------------------------------------------- - -- Properties - ------------------------------------------------------- - - -- Positioning & properties - opt.left = customOptions.left or 0 - opt.top = customOptions.top or 0 - opt.x = customOptions.x or nil - opt.y = customOptions.y or nil - if customOptions.x and customOptions.y then - opt.left = 0 - opt.top = 0 - end - opt.id = customOptions.id - opt.baseDir = customOptions.baseDir or system.ResourceDirectory - opt.maskFile = customOptions.maskFile or themeOptions.maskFile - opt.font = customOptions.font or native.systemFontBold - opt.fontSize = customOptions.fontSize or 22 - opt.fontColor = customOptions.fontColor or blackColor - opt.columnColor = customOptions.columnColor or defaultRowColor - opt.backgroundColor = customOptions.backgroundColor or defaultRowColor - - if _widget.isSeven() then - opt.font = customOptions.font or themeOptions.font or "HelveticaNeue-Medium" - opt.fontSize = customOptions.fontSize or themeOptions.fontSize or 20 - opt.fontColor = labelColor - end - - -- Properties - opt.rowHeight = customOptions.rowHeight or 40 - opt.columnData = customOptions.columns - - -- Frames & images - opt.sheet = customOptions.sheet - opt.themeSheetFile = themeOptions.sheet - opt.themeData = themeOptions.data - - opt.backgroundFrame = customOptions.backgroundFrame or _widget._getFrameIndex( themeOptions, themeOptions.backgroundFrame ) - opt.backgroundFrameWidth = customOptions.backgroundFrameWidth or themeOptions.backgroundFrameWidth - opt.backgroundFrameHeight = customOptions.backgroundFrameHeight or themeOptions.backgroundFrameHeight - - opt.overlayFrame = customOptions.overlayFrame or _widget._getFrameIndex( themeOptions, themeOptions.overlayFrame ) - opt.overlayFrameWidth = customOptions.overlayFrameWidth or themeOptions.overlayFrameWidth - opt.overlayFrameHeight = customOptions.overlayFrameHeight or themeOptions.overlayFrameHeight - - opt.seperatorFrame = customOptions.seperatorFrame or _widget._getFrameIndex( themeOptions, themeOptions.seperatorFrame ) - opt.seperatorFrameWidth = customOptions.seperatorFrameWidth or themeOptions.seperatorFrameWidth - opt.seperatorFrameHeight = customOptions.seperatorFrameHeight or themeOptions.seperatorFrameHeight - - ------------------------------------------------------- - -- Create the pickerWheel - ------------------------------------------------------- - - -- Create the pickerWheel object - local pickerWheel = _widget._new - { - left = opt.left, - top = opt.top, - id = opt.id or "widget_pickerWheel", - baseDir = opt.baseDir, - } - - -- Create the pickerWheel - createPickerWheel( pickerWheel, opt ) - - if isGraphicsV1 then - pickerWheel:setReferencePoint( display.TopLeftReferencePoint ) - end - - local x, y = _widget._calculatePosition( pickerWheel, opt ) - pickerWheel.x, pickerWheel.y = x, y - - return pickerWheel +function M.new( options, theme ) + local customOptions = options or {} + local themeOptions = theme or {} + + -- Create a local reference to our options table + local opt = M._options + + -- Check if the requirements for creating a widget has been met (throws an error if not) + _widget._checkRequirements( customOptions, themeOptions, M._widgetName ) + + ------------------------------------------------------- + -- Properties + ------------------------------------------------------- + + -- Positioning & properties + opt.left = customOptions.left or 0 + opt.top = customOptions.top or 0 + opt.x = customOptions.x or nil + opt.y = customOptions.y or nil + if customOptions.x and customOptions.y then + opt.left = 0 + opt.top = 0 + end + opt.id = customOptions.id + opt.baseDir = customOptions.baseDir or system.ResourceDirectory + opt.maskFile = customOptions.maskFile or themeOptions.maskFile + opt.font = customOptions.font or native.systemFontBold + opt.fontSize = customOptions.fontSize or 22 + opt.fontColor = customOptions.fontColor or blackColor + opt.columnColor = customOptions.columnColor or defaultRowColor + opt.backgroundColor = customOptions.backgroundColor or defaultRowColor + + if _widget.isSeven() then + opt.font = customOptions.font or themeOptions.font or "HelveticaNeue-Medium" + opt.fontSize = customOptions.fontSize or themeOptions.fontSize or 20 + opt.fontColor = labelColor + end + + -- Properties + opt.rowHeight = customOptions.rowHeight or 40 + opt.columnData = customOptions.columns + opt.onScroll = customOptions.onScroll + + -- Frames & images + opt.sheet = customOptions.sheet + opt.themeSheetFile = themeOptions.sheet + opt.themeData = themeOptions.data + + opt.backgroundFrame = customOptions.backgroundFrame or _widget._getFrameIndex( themeOptions, themeOptions.backgroundFrame ) + opt.backgroundFrameWidth = customOptions.backgroundFrameWidth or themeOptions.backgroundFrameWidth + opt.backgroundFrameHeight = customOptions.backgroundFrameHeight or themeOptions.backgroundFrameHeight + + opt.overlayFrame = customOptions.overlayFrame or _widget._getFrameIndex( themeOptions, themeOptions.overlayFrame ) + opt.overlayFrameWidth = customOptions.overlayFrameWidth or themeOptions.overlayFrameWidth + opt.overlayFrameHeight = customOptions.overlayFrameHeight or themeOptions.overlayFrameHeight + + opt.seperatorFrame = customOptions.seperatorFrame or _widget._getFrameIndex( themeOptions, themeOptions.seperatorFrame ) + opt.seperatorFrameWidth = customOptions.seperatorFrameWidth or themeOptions.seperatorFrameWidth + opt.seperatorFrameHeight = customOptions.seperatorFrameHeight or themeOptions.seperatorFrameHeight + + ------------------------------------------------------- + -- Create the pickerWheel + ------------------------------------------------------- + + -- Create the pickerWheel object + local pickerWheel = _widget._new + { + left = opt.left, + top = opt.top, + id = opt.id or "widget_pickerWheel", + baseDir = opt.baseDir, + } + + -- Create the pickerWheel + createPickerWheel( pickerWheel, opt ) + + if isGraphicsV1 then + pickerWheel:setReferencePoint( display.TopLeftReferencePoint ) + end + + local x, y = _widget._calculatePosition( pickerWheel, opt ) + pickerWheel.x, pickerWheel.y = x, y + + return pickerWheel end -return M +return M \ No newline at end of file diff --git a/widgetLibrary/widget_segmentedControl.lua b/widgetLibrary/widget_segmentedControl.lua old mode 100644 new mode 100755 index 434bc3f..1d820a8 --- a/widgetLibrary/widget_segmentedControl.lua +++ b/widgetLibrary/widget_segmentedControl.lua @@ -28,8 +28,8 @@ local M = { - _options = {}, - _widgetName = "widget.newSegmentedControl", + _options = {}, + _widgetName = "widget.newSegmentedControl", } @@ -52,488 +52,483 @@ end -- Creates a new segmentedControl from an image local function initWithImage( segmentedControl, options ) - -- Create a local reference to our options table - local opt = options - - -- Forward references - local imageSheet, view, segmentLabels, segmentDividers - - -- Create the imageSheet - if opt.sheet then - imageSheet = opt.sheet - else - local themeData = require( opt.themeData ) - imageSheet = graphics.newImageSheet( opt.themeSheetFile, themeData:getSheet() ) - end - - -- The view is the segmentedControl (group) - view = segmentedControl - view._segmentWidth = M.segmentWidth - view._labelColor = opt.labelColor - - -- Create the sequenceData table - local leftSegmentOptions = - { - { - name = "leftSegmentOff", - start = opt.leftSegmentFrame, - count = 1, - time = 1, - }, - - { - name = "leftSegmentOn", - start = opt.leftSegmentSelectedFrame, - count = 1, - time = 1, - }, - } - - local rightSegmentOptions = - { - { - name = "rightSegmentOff", - start = opt.rightSegmentFrame, - count = 1, - time = 1, - }, - - { - name = "rightSegmentOn", - start = opt.rightSegmentSelectedFrame, - count = 1, - time = 1, - }, - } - - local middleSegmentOptions = - { - { - name = "middleSegmentOff", - start = opt.middleSegmentFrame, - count = 1, - time = 1, - }, - - { - name = "middleSegmentOn", - start = opt.middleSegmentSelectedFrame, - count = 1, - time = 1, - }, - } - - local dividerSegmentOptions = - { - { - name = "middleSegmentOff", - start = opt.middleSegmentFrame, - count = 1, - time = 1, - }, - - { - name = "middleSegmentOn", - start = opt.middleSegmentSelectedFrame, - count = 1, - time = 1, - }, - } - - -- Reference the passed in segments - local segments = opt.segments - - -- Create the view's segments - segmentLabels = {} - segmentDividers = {} - - local overallControlWidth = ( view._segmentWidth * #segments ) - local segmentWidth = overallControlWidth / #segments - - -- The left segment edge - local leftSegment = display.newSprite( segmentedControl, imageSheet, leftSegmentOptions ) - leftSegment.x = segmentedControl.x + ( opt.width * 0.5 ) - leftSegment.y = segmentedControl.y + ( leftSegment.contentHeight * 0.5 ) - leftSegment:setSequence( "leftSegmentOff" ) - leftSegment.width = opt.width - -- The segment fill - local middleSegment = display.newSprite( segmentedControl, imageSheet, middleSegmentOptions ) - middleSegment:setSequence( "middleSegmentOff" ) - - middleSegment.width = ( overallControlWidth ) - ( opt.width * 2 ) - middleSegment.x = leftSegment.x + leftSegment.contentWidth * 0.5 + ( middleSegment.width * 0.5 ) - middleSegment.y = segmentedControl.y + ( middleSegment.contentHeight * 0.5 ) - - -- The right segment edge - local rightSegment = display.newSprite( segmentedControl, imageSheet, rightSegmentOptions ) - rightSegment:setSequence( "rightSegmentOff" ) - rightSegment.width = opt.width - rightSegment.x = middleSegment.x + ( middleSegment.width * 0.5 ) + opt.width * 0.5 - rightSegment.y = segmentedControl.y + ( rightSegment.contentHeight * 0.5 ) - - -- Create the segment labels & dividers - for i = 1, #segments do - -- Create the labels - local label - if _widget.isSeven() then - label = display.newText( segmentedControl, segments[i], 0, 0, opt.labelFont, opt.labelSize ) - if view._segmentNumber == i or opt.defaultSegment == i then - label:setFillColor( unpack( view._labelColor.over ) ) - else - label:setFillColor( unpack( view._labelColor.default ) ) - end - else - label = display.newEmbossedText( segmentedControl, segments[i], 0, 0, opt.labelFont, opt.labelSize ) - label:setFillColor( unpack( whiteColor ) ) - end - - - - label.x = leftSegment.x + ( segmentWidth * 0.5 + segmentWidth * ( i - 1 ) ) - leftSegment.width * 0.5 - label.y = leftSegment.y - label.segmentName = segments[i] - segmentLabels[i] = label + -- Create a local reference to our options table + local opt = options + + -- Forward references + local imageSheet, view, segmentLabels, segmentDividers + + -- Create the imageSheet + if opt.sheet then + imageSheet = opt.sheet + else + local themeData = require( opt.themeData ) + imageSheet = graphics.newImageSheet( opt.themeSheetFile, themeData:getSheet() ) + end + + -- The view is the segmentedControl (group) + view = segmentedControl + view._segmentWidth = M.segmentWidth + view._labelColor = opt.labelColor + + -- Create the sequenceData table + local leftSegmentOptions = + { + { + name = "leftSegmentOff", + start = opt.leftSegmentFrame, + count = 1, + time = 1, + }, + + { + name = "leftSegmentOn", + start = opt.leftSegmentSelectedFrame, + count = 1, + time = 1, + }, + } + + local rightSegmentOptions = + { + { + name = "rightSegmentOff", + start = opt.rightSegmentFrame, + count = 1, + time = 1, + }, + + { + name = "rightSegmentOn", + start = opt.rightSegmentSelectedFrame, + count = 1, + time = 1, + }, + } + + local middleSegmentOptions = + { + { + name = "middleSegmentOff", + start = opt.middleSegmentFrame, + count = 1, + time = 1, + }, + + { + name = "middleSegmentOn", + start = opt.middleSegmentSelectedFrame, + count = 1, + time = 1, + }, + } + + local dividerSegmentOptions = + { + { + name = "middleSegmentOff", + start = opt.middleSegmentFrame, + count = 1, + time = 1, + }, + + { + name = "middleSegmentOn", + start = opt.middleSegmentSelectedFrame, + count = 1, + time = 1, + }, + } + + -- Reference the passed in segments + local segments = opt.segments + + -- Create the view's segments + segmentLabels = {} + segmentDividers = {} + + local overallControlWidth = ( view._segmentWidth * #segments ) + local segmentWidth = overallControlWidth / #segments + + -- The left segment edge + local leftSegment = display.newSprite( segmentedControl, imageSheet, leftSegmentOptions ) + leftSegment.x = segmentedControl.x + ( opt.width * 0.5 ) + leftSegment.y = segmentedControl.y + ( leftSegment.contentHeight * 0.5 ) + leftSegment:setSequence( "leftSegmentOff" ) + leftSegment.width = opt.width + -- The segment fill + local middleSegment = display.newSprite( segmentedControl, imageSheet, middleSegmentOptions ) + middleSegment:setSequence( "middleSegmentOff" ) + + middleSegment.width = ( overallControlWidth ) - ( opt.width * 2 ) + middleSegment.x = leftSegment.x + leftSegment.contentWidth * 0.5 + ( middleSegment.width * 0.5 ) + middleSegment.y = segmentedControl.y + ( middleSegment.contentHeight * 0.5 ) + + -- The right segment edge + local rightSegment = display.newSprite( segmentedControl, imageSheet, rightSegmentOptions ) + rightSegment:setSequence( "rightSegmentOff" ) + rightSegment.width = opt.width + rightSegment.x = middleSegment.x + ( middleSegment.width * 0.5 ) + opt.width * 0.5 + rightSegment.y = segmentedControl.y + ( rightSegment.contentHeight * 0.5 ) + + -- Create the segment labels & dividers + for i = 1, #segments do + -- Create the labels + local label + if _widget.isSeven() then + label = display.newText( segmentedControl, segments[i], 0, 0, opt.labelFont, opt.labelSize ) + if view._segmentNumber == i or opt.defaultSegment == i then + label:setFillColor( unpack( view._labelColor.over ) ) + else + label:setFillColor( unpack( view._labelColor.default ) ) + end + else + label = display.newEmbossedText( segmentedControl, segments[i], 0, 0, opt.labelFont, opt.labelSize ) + label:setFillColor( unpack( whiteColor ) ) + end + + + + label.x = leftSegment.x + ( segmentWidth * 0.5 + segmentWidth * ( i - 1 ) ) - leftSegment.width * 0.5 + label.y = leftSegment.y + label.segmentName = segments[i] + segmentLabels[i] = label - -- Create the dividers - if i < #segments then - local divider = display.newImageRect( segmentedControl, imageSheet, opt.dividerFrame, 1, 29 ) - divider.x = leftSegment.x + ( segmentWidth * i ) - ( leftSegment.width * 0.5 ) - divider.y = leftSegment.y - segmentDividers[i] = divider - end - end + -- Create the dividers + if i < #segments then + local divider = display.newImageRect( segmentedControl, imageSheet, opt.dividerFrame, 1, 29 ) + divider.x = leftSegment.x + ( segmentWidth * i ) - ( leftSegment.width * 0.5 ) + divider.y = leftSegment.y + segmentDividers[i] = divider + end + end - -- The "over" frame - local segmentOver = display.newSprite( segmentedControl, imageSheet, middleSegmentOptions ) - segmentOver:setSequence( "middleSegmentOn" ) - - segmentOver.width = opt.width - segmentOver.y = leftSegment.y - - ------------------------------------------------------- - -- Assign properties to the view - ------------------------------------------------------- - - -- Segment properties - view._segmentWidth = segmentWidth - view._edgeWidth = opt.width - view._totalSegments = #segments - - -- Create a reference to the segments - view._leftSegment = leftSegment - view._middleSegment = middleSegment - view._rightSegment = rightSegment - view._segmentOver = segmentOver - view._segmentLabels = segmentLabels - view._segmentDividers = segmentDividers - view._segmentNumber = opt.defaultSegment - view._onPress = opt.onPress - - -- Insert the segment labels into the view - for i = 1, #view._segmentLabels do - view:insert( view._segmentLabels[i] ) - end - - -- Insert the segment dividers into the view - for i = 1, #segmentDividers do - view:insert( view._segmentDividers[i] ) - end - - ------------------------------------------------------- - -- Assign properties/objects to the segmentedControl - ------------------------------------------------------- - - -- Assign objects to the segmentedControl - segmentedControl._imageSheet = imageSheet - segmentedControl._view = view - segmentedControl._onPress = opt.onPress - - -- Public properties - segmentedControl.segmentLabel = view._segmentLabels[1].text - segmentedControl.segmentNumber = view._segmentNumber - - ---------------------------------------------------------- - -- PUBLIC METHODS - ---------------------------------------------------------- - - -- Touch listener for our segmented control - function view:touch( event ) - local phase = event.phase - local _segmentedControl = self.parent - event.target = _segmentedControl - local firstSegment = 1 - local lastSegment = self._totalSegments + -- The "over" frame + local segmentOver = display.newSprite( segmentedControl, imageSheet, middleSegmentOptions ) + segmentOver:setSequence( "middleSegmentOn" ) + + segmentOver.width = opt.width + segmentOver.y = leftSegment.y + + ------------------------------------------------------- + -- Assign properties to the view + ------------------------------------------------------- + + -- Segment properties + view._segmentWidth = segmentWidth + view._edgeWidth = opt.width + view._totalSegments = #segments + + -- Create a reference to the segments + view._leftSegment = leftSegment + view._middleSegment = middleSegment + view._rightSegment = rightSegment + view._segmentOver = segmentOver + view._segmentLabels = segmentLabels + view._segmentDividers = segmentDividers + view._segmentNumber = opt.defaultSegment + view._onPress = opt.onPress + + -- Insert the segment labels into the view + for i = 1, #view._segmentLabels do + view:insert( view._segmentLabels[i] ) + end + + -- Insert the segment dividers into the view + for i = 1, #segmentDividers do + view:insert( view._segmentDividers[i] ) + end + + ------------------------------------------------------- + -- Assign properties/objects to the segmentedControl + ------------------------------------------------------- + + -- Assign objects to the segmentedControl + segmentedControl._imageSheet = imageSheet + segmentedControl._view = view + segmentedControl._onPress = opt.onPress + + -- Public properties + segmentedControl.segmentLabel = view._segmentLabels[1].text + segmentedControl.segmentNumber = view._segmentNumber + + ---------------------------------------------------------- + -- PUBLIC METHODS + ---------------------------------------------------------- + + -- Touch listener for our segmented control + function view:touch( event ) + local phase = event.phase + local _segmentedControl = self + event.target = _segmentedControl + local firstSegment = 1 + local lastSegment = self._totalSegments - if "began" == phase then - -- Loop through the segments - for i = 1, self._totalSegments do - local segmentedControlXPosition = self.x - ( self.contentWidth * 0.5 ) - -- for g2, we have to take into account the current anchorX for this position - if not isGraphicsV1 then - local oldAnchorX = self.anchorX - segmentedControlXPosition = segmentedControlXPosition + ( 0.5 - oldAnchorX ) * self.contentWidth - end - - local currentSegment = i - local segmentWidth = self._segmentWidth - - -- Work out the current segments position + if "began" == phase then + native.setKeyboardFocus(nil) + -- Loop through the segments + for i = 1, self._totalSegments do + local segmentedControlXPosition = self.x - ( self.contentWidth * 0.5 ) + -- for g2, we have to take into account the current anchorX for this position + if not isGraphicsV1 then + local oldAnchorX = self.anchorX + segmentedControlXPosition = segmentedControlXPosition + ( 0.5 - oldAnchorX ) * self.contentWidth + end + + local currentSegment = i + local segmentWidth = self._segmentWidth + local x,y = view:contentToLocal(event.x, event.y) + local currentSegmentLeftEdge = ( segmentWidth * currentSegment ) - segmentWidth + local currentSegmentRightEdge = ( segmentWidth * currentSegment ) + -- If the touch is within the segments range + if x >= currentSegmentLeftEdge and x <= currentSegmentRightEdge then + -- First segment (Near left) + if firstSegment == i then + self:setLeftSegmentActive() + -- Last segment (Far right) + elseif lastSegment == i then + self:setRightSegmentActive() + -- Any other segment + else + self:setMiddleSegmentActive( i ) + end + + -- Set the segment name + _segmentedControl.segmentLabel = self._segmentLabels[i].segmentName + + -- Set the segment number + _segmentedControl.segmentNumber = self._segmentNumber + + -- Execute onPress listener + if self._onPress and "function" == type( self._onPress ) then + self._onPress( event ) + end + + break + end + end + end + + return true + end + + view:addEventListener( "touch" ) - local parentOffsetX = 0 - - -- First, we check if the widget is in a group - if nil ~= self.parent and nil ~= self.parent.x then - parentOffsetX = self.parent.x - end - - --local currentSegmentLeftEdge = ( segmentedControlXPosition * 0.5 ) * currentSegment + parentOffsetX - --local currentSegmentRightEdge = segmentedControlXPosition + ( segmentWidth * currentSegment ) + parentOffsetX - - local currentSegmentLeftEdge = segmentedControlXPosition + ( segmentWidth * currentSegment ) - segmentWidth + parentOffsetX - local currentSegmentRightEdge = segmentedControlXPosition + ( segmentWidth * currentSegment ) + parentOffsetX - - -- If the touch is within the segments range - if event.x >= currentSegmentLeftEdge and event.x <= currentSegmentRightEdge then - -- First segment (Near left) - if firstSegment == i then - self:setLeftSegmentActive() - -- Last segment (Far right) - elseif lastSegment == i then - self:setRightSegmentActive() - -- Any other segment - else - self:setMiddleSegmentActive( i ) - end - - -- Set the segment name - _segmentedControl.segmentLabel = self._segmentLabels[i].segmentName - - -- Set the segment number - _segmentedControl.segmentNumber = self._segmentNumber - - -- Execute onPress listener - if self._onPress and "function" == type( self._onPress ) then - self._onPress( event ) - end - - break - end - end - end - - return true - end - - view:addEventListener( "touch" ) - - ---------------------------------------------------------- - -- PRIVATE METHODS - ---------------------------------------------------------- - - -- Function to set the left segment active - function view:setLeftSegmentActive() - -- Turn off the right segment - self._rightSegment:setSequence( "rightSegmentOff" ) - -- Turn on the left segment - self._leftSegment:setSequence( "leftSegmentOn" ) - -- Set the over segment's width - segmentOver.width = view._segmentWidth - self._leftSegment.width - 0.5 - -- Set the over segment's position - - segmentOver.x = self._leftSegment.x + self._leftSegment.width * 0.5 + segmentOver.width * 0.5 - - if isGraphicsV1 then - segmentOver:setReferencePoint( display.CenterReferencePoint ) - end - - -- Set the segment's name - self._segmentLabel = self._segmentLabels[1].text - - -- Set the segment number - self._segmentNumber = 1 - - -- Reset the colors if ios7 - if _widget.isSeven() then - for i = 1, #view._segmentLabels do - local currentSegment = view._segmentLabels[ i ] - currentSegment:setFillColor( unpack( view._labelColor.default ) ) - end - - view._segmentLabels[1]:setFillColor( unpack( whiteColor ) ) - - end - - end - - -- Function to set the right segment active - function view:setRightSegmentActive() - -- Turn off the left segment - self._leftSegment:setSequence( "leftSegmentOff" ) - -- Turn on the right segment - self._rightSegment:setSequence( "rightSegmentOn" ) - -- Set the over segment's width - segmentOver.width = view._segmentWidth - self._rightSegment.width - -- Set the over segment's position - segmentOver.x = self._rightSegment.x - self._rightSegment.width * 0.5 - segmentOver.width * 0.5 - - -- Set the segment's name - self._segmentLabel = self._segmentLabels[self._totalSegments].text - - -- Set the segment number - self._segmentNumber = self._totalSegments - - -- Reset the colors if ios7 - if _widget.isSeven() then - for i = 1, #view._segmentLabels do - local currentSegment = view._segmentLabels[ i ] - currentSegment:setFillColor( unpack( view._labelColor.default ) ) - end - - view._segmentLabels[ #view._segmentLabels ]:setFillColor( unpack( whiteColor ) ) - - end - - end - - -- Function to set the middle segment active - function view:setMiddleSegmentActive( segmentNum ) - -- Turn off the left segment - self._leftSegment:setSequence( "leftSegmentOff" ) - -- Turn off the right segment - self._rightSegment:setSequence( "rightSegmentOff" ) - -- Set the over segment's width - segmentOver.width = self._segmentWidth - -- Set the over segment's position - segmentOver.x = self._segmentDividers[segmentNum - 1].x + segmentOver.width * 0.5 - - -- Set the segment's name - self._segmentLabel = self._segmentLabels[segmentNum].text - - -- Set the segment number - self._segmentNumber = segmentNum - - -- Reset the colors if ios7 - if _widget.isSeven() then - for i = 1, #view._segmentLabels do - local currentSegment = view._segmentLabels[ i ] - currentSegment:setFillColor( unpack( view._labelColor.default ) ) - end - - view._segmentLabels[ segmentNum ]:setFillColor( unpack( whiteColor ) ) - - end - - end - - -- Set the intial segment to active - local function setDefaultSegment( segmentNum ) - if 1 == segmentNum then - view:setLeftSegmentActive() - elseif #segments == segmentNum then - view:setRightSegmentActive() - else - view:setMiddleSegmentActive( view._segmentNumber ) - end - end - - -- Finalize function for the segmentedControl - function segmentedControl:_finalize() - self._view._segments = nil - self._view = nil - - -- Set the ImageSheet to nil - self._imageSheet = nil - end - - -- Set the default segment - setDefaultSegment( view._segmentNumber ) - - return segmentedControl + ---------------------------------------------------------- + -- PRIVATE METHODS + ---------------------------------------------------------- + + -- Function to set the left segment active + function view:setLeftSegmentActive() + -- Turn off the right segment + self._rightSegment:setSequence( "rightSegmentOff" ) + -- Turn on the left segment + self._leftSegment:setSequence( "leftSegmentOn" ) + -- Set the over segment's width + segmentOver.width = view._segmentWidth - self._leftSegment.width - 0.5 + -- Set the over segment's position + + segmentOver.x = self._leftSegment.x + self._leftSegment.width * 0.5 + segmentOver.width * 0.5 + + if isGraphicsV1 then + segmentOver:setReferencePoint( display.CenterReferencePoint ) + end + + -- Set the segment's name + self._segmentLabel = self._segmentLabels[1].text + + -- Set the segment number + self._segmentNumber = 1 + + -- Reset the colors if ios7 + if _widget.isSeven() then + for i = 1, #view._segmentLabels do + local currentSegment = view._segmentLabels[ i ] + currentSegment:setFillColor( unpack( view._labelColor.default ) ) + end + + view._segmentLabels[1]:setFillColor( unpack( whiteColor ) ) + + end + + end + + -- Function to set the right segment active + function view:setRightSegmentActive() + -- Turn off the left segment + self._leftSegment:setSequence( "leftSegmentOff" ) + -- Turn on the right segment + self._rightSegment:setSequence( "rightSegmentOn" ) + -- Set the over segment's width + segmentOver.width = view._segmentWidth - self._rightSegment.width + -- Set the over segment's position + segmentOver.x = self._rightSegment.x - self._rightSegment.width * 0.5 - segmentOver.width * 0.5 + + -- Set the segment's name + self._segmentLabel = self._segmentLabels[self._totalSegments].text + + -- Set the segment number + self._segmentNumber = self._totalSegments + + -- Reset the colors if ios7 + if _widget.isSeven() then + for i = 1, #view._segmentLabels do + local currentSegment = view._segmentLabels[ i ] + currentSegment:setFillColor( unpack( view._labelColor.default ) ) + end + + view._segmentLabels[ #view._segmentLabels ]:setFillColor( unpack( whiteColor ) ) + + end + + end + + -- Function to set the middle segment active + function view:setMiddleSegmentActive( segmentNum ) + -- Turn off the left segment + self._leftSegment:setSequence( "leftSegmentOff" ) + -- Turn off the right segment + self._rightSegment:setSequence( "rightSegmentOff" ) + -- Set the over segment's width + segmentOver.width = self._segmentWidth + -- Set the over segment's position + segmentOver.x = self._segmentDividers[segmentNum - 1].x + segmentOver.width * 0.5 + + -- Set the segment's name + self._segmentLabel = self._segmentLabels[segmentNum].text + + -- Set the segment number + self._segmentNumber = segmentNum + + -- Reset the colors if ios7 + if _widget.isSeven() then + for i = 1, #view._segmentLabels do + local currentSegment = view._segmentLabels[ i ] + currentSegment:setFillColor( unpack( view._labelColor.default ) ) + end + + view._segmentLabels[ segmentNum ]:setFillColor( unpack( whiteColor ) ) + + end + + end + + -- Set the intial segment to active + local function setDefaultSegment( segmentNum ) + if 1 == segmentNum then + view:setLeftSegmentActive() + elseif #segments == segmentNum then + view:setRightSegmentActive() + else + view:setMiddleSegmentActive( segmentNum ) + end + end + + function segmentedControl:setValue(segmentNum) + setDefaultSegment(segmentNum) + end + + function segmentedControl:getValue() + return self._view._segmentNumber + end + -- Finalize function for the segmentedControl + function segmentedControl:_finalize() + self._view._segments = nil + self._view = nil + + -- Set the ImageSheet to nil + self._imageSheet = nil + end + + -- Set the default segment + setDefaultSegment( view._segmentNumber ) + + return segmentedControl end -- Function to create a new segmentedControl object ( widget.newSegmentedControl ) -function M.new( options, theme ) - local customOptions = options or {} - local themeOptions = theme or {} - - -- Create a local reference to our options table - local opt = M._options - - -- Check if the requirements for creating a widget has been met (throws an error if not) - _widget._checkRequirements( customOptions, themeOptions, M._widgetName ) - - ------------------------------------------------------- - -- Properties - ------------------------------------------------------- - -- Positioning & properties - opt.left = customOptions.left or 0 - opt.top = customOptions.top or 0 - opt.x = customOptions.x or nil - opt.y = customOptions.y or nil - if customOptions.x and customOptions.y then - opt.left = 0 - opt.top = 0 - end - opt.width = customOptions.width or themeOptions.width or error( "ERROR:" .. M._widgetName .. ": width expected, got nil", 3 ) - opt.height = customOptions.height or themeOptions.height or error( "ERROR:" .. M._widgetName .. ": height expected, got nil", 3 ) - opt.id = customOptions.id - opt.baseDir = customOptions.baseDir or system.ResourceDirectory - opt.segments = customOptions.segments or { "One", "Two" } - M.segmentWidth = customOptions.segmentWidth or 50 - opt.defaultSegment = customOptions.defaultSegment or 1 - opt.labelSize = customOptions.labelSize or 12 - opt.labelFont = customOptions.labelFont or native.systemFont - -- TODO: document this in the API - opt.labelColor = customOptions.labelColor or themeOptions.labelColor or buttonDefault - - if _widget.isSeven() then - opt.labelFont = customOptions.labelFont or "HelveticaNeue" - opt.labelSize = customOptions.labelSize or 13 - end - - opt.labelXOffset = customOptions.labelXOffset or 0 - opt.labelYOffset = customOptions.labelYOffset or 0 - opt.onPress = customOptions.onPress - - -- Frames & Images - opt.sheet = customOptions.sheet - opt.themeSheetFile = themeOptions.sheet - opt.themeData = themeOptions.data - - opt.leftSegmentFrame = customOptions.leftSegmentFrame or _widget._getFrameIndex( themeOptions, themeOptions.leftSegmentFrame ) - opt.leftSegmentSelectedFrame = customOptions.leftSegmentSelectedFrame or _widget._getFrameIndex( themeOptions, themeOptions.leftSegmentSelectedFrame ) - opt.rightSegmentFrame = customOptions.rightSegmentFrame or _widget._getFrameIndex( themeOptions, themeOptions.rightSegmentFrame ) - opt.rightSegmentSelectedFrame = customOptions.rightSegmentSelectedFrame or _widget._getFrameIndex( themeOptions,themeOptions.rightSegmentSelectedFrame ) - opt.middleSegmentFrame = customOptions.middleSegmentFrame or _widget._getFrameIndex( themeOptions, themeOptions.middleSegmentFrame ) - opt.middleSegmentSelectedFrame = customOptions.middleSegmentSelectedFrame or _widget._getFrameIndex( themeOptions, themeOptions.middleSegmentSelectedFrame) - opt.dividerFrame = customOptions.dividerFrame or _widget._getFrameIndex( themeOptions, themeOptions.dividerFrame) +function M.new( options, theme ) + local customOptions = options or {} + local themeOptions = theme or {} + + -- Create a local reference to our options table + local opt = M._options + + -- Check if the requirements for creating a widget has been met (throws an error if not) + _widget._checkRequirements( customOptions, themeOptions, M._widgetName ) + + ------------------------------------------------------- + -- Properties + ------------------------------------------------------- + -- Positioning & properties + opt.left = customOptions.left or 0 + opt.top = customOptions.top or 0 + opt.x = customOptions.x or nil + opt.y = customOptions.y or nil + if customOptions.x and customOptions.y then + opt.left = 0 + opt.top = 0 + end + opt.width = customOptions.width or themeOptions.width or error( "ERROR:" .. M._widgetName .. ": width expected, got nil", 3 ) + opt.height = customOptions.height or themeOptions.height or error( "ERROR:" .. M._widgetName .. ": height expected, got nil", 3 ) + opt.id = customOptions.id + opt.baseDir = customOptions.baseDir or system.ResourceDirectory + opt.segments = customOptions.segments or { "One", "Two" } + M.segmentWidth = customOptions.segmentWidth or 50 + opt.defaultSegment = customOptions.defaultSegment or 1 + opt.labelSize = customOptions.labelSize or 12 + opt.labelFont = customOptions.labelFont or native.systemFont + -- TODO: document this in the API + opt.labelColor = customOptions.labelColor or themeOptions.labelColor or buttonDefault + + if _widget.isSeven() then + opt.labelFont = customOptions.labelFont or "HelveticaNeue" + opt.labelSize = customOptions.labelSize or 13 + end + + opt.labelXOffset = customOptions.labelXOffset or 0 + opt.labelYOffset = customOptions.labelYOffset or 0 + opt.onPress = customOptions.onPress + + -- Frames & Images + opt.sheet = customOptions.sheet + opt.themeSheetFile = themeOptions.sheet + opt.themeData = themeOptions.data + + opt.leftSegmentFrame = customOptions.leftSegmentFrame or _widget._getFrameIndex( themeOptions, themeOptions.leftSegmentFrame ) + opt.leftSegmentSelectedFrame = customOptions.leftSegmentSelectedFrame or _widget._getFrameIndex( themeOptions, themeOptions.leftSegmentSelectedFrame ) + opt.rightSegmentFrame = customOptions.rightSegmentFrame or _widget._getFrameIndex( themeOptions, themeOptions.rightSegmentFrame ) + opt.rightSegmentSelectedFrame = customOptions.rightSegmentSelectedFrame or _widget._getFrameIndex( themeOptions,themeOptions.rightSegmentSelectedFrame ) + opt.middleSegmentFrame = customOptions.middleSegmentFrame or _widget._getFrameIndex( themeOptions, themeOptions.middleSegmentFrame ) + opt.middleSegmentSelectedFrame = customOptions.middleSegmentSelectedFrame or _widget._getFrameIndex( themeOptions, themeOptions.middleSegmentSelectedFrame) + opt.dividerFrame = customOptions.dividerFrame or _widget._getFrameIndex( themeOptions, themeOptions.dividerFrame) - ------------------------------------------------------- - -- Create the segmentedControl - ------------------------------------------------------- - - -- Create the segmentedControl object - local segmentedControl = _widget._new - { - left = opt.left, - top = opt.top, - id = opt.id or "widget_segmentedControl", - baseDir = opt.baseDir, - } - - -- Create the segmentedControl - initWithImage( segmentedControl, opt ) - - -- Set the segmentedControl's position ( set the reference point to center, just to be sure ) - if ( isGraphicsV1 ) then - segmentedControl:setReferencePoint( display.CenterReferencePoint ) - end - - local x, y = _widget._calculatePosition( segmentedControl, opt ) - segmentedControl.x, segmentedControl.y = x, y - - return segmentedControl + ------------------------------------------------------- + -- Create the segmentedControl + ------------------------------------------------------- + + -- Create the segmentedControl object + local segmentedControl = _widget._new + { + left = opt.left, + top = opt.top, + id = opt.id or "widget_segmentedControl", + baseDir = opt.baseDir, + } + + -- Create the segmentedControl + initWithImage( segmentedControl, opt ) + + -- Set the segmentedControl's position ( set the reference point to center, just to be sure ) + if ( isGraphicsV1 ) then + segmentedControl:setReferencePoint( display.CenterReferencePoint ) + end + + local x, y = _widget._calculatePosition( segmentedControl, opt ) + segmentedControl.x, segmentedControl.y = x, y + + return segmentedControl end -return M +return M \ No newline at end of file diff --git a/widgetLibrary/widget_tableviewext.lua b/widgetLibrary/widget_tableviewext.lua new file mode 100644 index 0000000..6e158f1 --- /dev/null +++ b/widgetLibrary/widget_tableviewext.lua @@ -0,0 +1,238 @@ +local _widget = require("widget") + +local fastSwipeTime = 400; +local fastSwipeDistance = 50; + +local M = {} + +function M.new(old_newTableView, options) + local tableview + + local function onTransitionComplete(event) + tableview._transition = nil; + end + + +local function tableViewListener(event) + local row = event.target; + if event.phase == "began" then + if tableview._beganPhase then + event.phase = "moved" + + else + tableview._beganPhase = true; + tableview._slideStarted = false; + if tableview._slideDistance > 0 then + if tableview._targetRow and (tableview._targetRow ~= row) and (tableview._targetRow.x ~= 0) then + tableview:cancelSlide(tableview._targetRow) + elseif tableview._slideDistance > 0 and row.hiddenGroup then + if tableview._transition then + transition.cancel(tableview._transition); + tableview._targetRow.x = tableview._targetX; + tableview._transition = nil + end + display.getCurrentStage():setFocus(tableview.view); + tableview._sliding = true; + tableview._startX = event.x; + tableview._startGroupX = row.x; + tableview._startTime = event.time; + if row.hiddenGroup.isVisible == false then + row.hiddenGroup.x = row.x; + row.hiddenGroup.y = row.y; + row.hiddenGroup.isVisible = true; + end; + end; + end; + + + end + end + if event.phase == "moved" and tableview._sliding then + + local totalDistance = event.x - tableview._startX; + if math.abs(totalDistance ) > 10 then + tableview._slideStarted = true; + end + if tableview._slideStarted then + if tableview._startGroupX == 0 then + if (totalDistance < 0 ) and math.abs(totalDistance) <= tableview._slideDistance then + row.x = tableview._startGroupX + totalDistance; + end + else + if (totalDistance > 0 ) and totalDistance <= tableview._slideDistance then + row.x = tableview._startGroupX + totalDistance; + end + end + end; + end + if (event.phase == "ended" or event.phase == "cancelled") then + tableview._beganPhase = false; + if tableview._sliding then + display.getCurrentStage():setFocus(nil); + + tableview._sliding = false; + --display.getCurrentStage():setFocus(nil); + local totalSwipeTime = system.getTimer() - tableview._startTime + local totalDistance = event.x - tableview._startX ; + local absDistance = math.abs(totalDistance); + local targetX; + if (event.phase == "ended") then + if ((totalSwipeTime <= fastSwipeTime and absDistance >= fastSwipeDistance) or + (absDistance > tableview._slideDistance /2 )) then + if totalDistance < 0 then + if tableview._startGroupX == 0 then + targetX = tableview._startGroupX - tableview._slideDistance; + else + targetX = tableview._startGroupX; + end + else + targetX = 0; + end + else + targetX = tableview._startGroupX; + end + + else + targetX = tableview._startGroupX; + end + tableview:slideTo(row, targetX, 300) + end + end + + if tableview._listener then + tableview._listener (event) + end + end + + + local function tableViewRowRender(event) + local row = event.row; + local hiddenGroup = tableview.hiddenRows[row.index]; + if hiddenGroup then + display.remove(hiddenGroup); + end + row.hiddenGroup = display.newGroup() + tableview.hiddenRows[row.index] = row.hiddenGroup; + + --row:insert(row.hiddenGroup); + tableview._view:insert(row.hiddenGroup) + row.hiddenGroup.x = row.x; + row.hiddenGroup.y = row.y; + row.hiddenGroup:toBack() + + if tableview._onRowRender then + tableview._onRowRender (event) + end + end + --install hooks only if the table needs to be sliding + local _listener, _onRowRender = nil, nil; + if options.slideDistance then + + if options.listener and "function" == type(options.listener) then + _listener = options.listener; + end; + options.listener = tableViewListener; + + + if options.onRowRender and "function" == type(options.onRowRender) then + _onRowRender = options.onRowRender; + end; + + options.onRowRender = tableViewRowRender; + end + tableview = old_newTableView(options) + --tableview = require("widgets.widget_tableview").new(options) + if options.slideDistance then + tableview._listener = _listener; + tableview.hiddenRows = {}; + tableview._onRowRender = _onRowRender; + end; + tableview._slideDistance = options.slideDistance; + + + + + function tableview:slideTo(row, targetX, transitionTime) + if row then + if self._transition then + transition.cancel(self._transition) + if self._targetRow then + self._targetRow.x = self._targetX; + end + self._transition = nil; + end + if transitionTime and transitionTime > 0 then + self._targetRow = row + self._targetX = targetX; + local currentX = row.x; + local speed = math.abs(targetX - currentX) / self._slideDistance; + self._transition = transition.to(row, + { x = targetX, + time = transitionTime * speed, + transition = easing.outQuad, + onComplete = onTransitionComplete}) + else + row.x = targetX; + end + end + + end + + function tableview:deleteRow( rowIndex ) + local row = self._view:_getRowAtIndex( rowIndex ) + if row.hiddenGroup then + if self._targetRow == row then + self._targetRow = nil; + end + display.remove(row.hiddenGroup) + tableview.hiddenRows[rowIndex] = nil; + row.hiddenGroup = nil; + end + local retVal = self._view:_deleteRow( rowIndex ) + for i = rowIndex , table.maxn(self._view._rows) do + row = self._view._rows[i]; + -- If the row is within the visible view + if row and "table" == type(row._view ) then + if row and row._view.hiddenGroup then + row._view.hiddenGroup.isVisible = false; + --row._view.hiddenGroup.x = row._view.x; + --row._view.hiddenGroup.y = row._view.y; + end + end; + end + return retVal; + end + + function tableview:deleteAllRows() + for i = 1 , table.maxn(self._view._rows) do + row = self._view._rows[i]; + -- If the row is within the visible view + if row and "table" == type(row._view ) then + if row and row._view.hiddenGroup then + display.remove(row._view.hiddenGroup) + row._view.hiddenGroup = nil; + end + end; + end + self.hiddenRows = {}; + return self._view:_deleteAllRows() + end + + function tableview:cancelSlide(row) + if row then + self:slideTo(row, 0, 0) + elseif self._targetRow then + self:cancelSlide(tableview._targetRow) + end + + end + + function tableview:takeFocus(event) + self._view:touch(event) + end + + return tableview; +end + + +return M; diff --git a/widgetLibrary/widgetext.lua b/widgetLibrary/widgetext.lua new file mode 100644 index 0000000..94db0a7 --- /dev/null +++ b/widgetLibrary/widgetext.lua @@ -0,0 +1,159 @@ +local widget = require("widget"); +local isGraphicsV1 = ( 1 == display.getDefault( "graphicsCompatibility" ) ) + +-- Function to retrieve a widget's theme settings +local function _getTheme( widgetTheme, options ) + local theme = nil + + -- If a theme has been set + if widget.theme then + theme = widget.theme[widgetTheme] + end + + -- If a theme exists + if theme then + -- Style parameter optionally set by user + if options and options.style then + local style = theme[options.style] + + -- For themes that support various "styles" per widget + if style then + theme = style + end + end + end + + return theme +end + + +-- Check if the theme is ios7 +local function isSeven() + return widget.themeName ~= "widget_theme_android" and + widget.themeName ~= "widget_theme_ios"; +end + +--widget.isSeven = isSeven; + +local function createWidget(createFunction, ...) + local defAnchorX, defAnchorY + if not isGraphicsV1 then + defAnchorX = display.getDefault( "anchorX") + defAnchorY = display.getDefault( "anchorY" ) + widget._oldAnchorX = defAnchorX + widget._oldAnchorY = defAnchorY + + display.setDefault( "anchorX", 0.5) + display.setDefault( "anchorY", 0.5 ) + + end + local w = createFunction(...) + if not isGraphicsV1 then + display.setDefault( "anchorX", defAnchorX) + display.setDefault( "anchorY", defAnchorY ) + w.anchorX = defAnchorX + w.anchorY = defAnchorY + end + return w +end + +local function newPanel( options ) + local theme = _getTheme( "panel", options ); + if theme == nil then + --if the current theme does not have a panel, revert to button + theme = _getTheme( "button", options ) + end; + local _panel = require( "widgets.widget_panel" ) + return createWidget(_panel.new, options, theme ) +end + +widget.newPanel = newPanel; + +local function newButton( options ) + local theme = _getTheme( "button", options ) + + local _button = require( "widgets.widget_button" ) + return createWidget(_button.new, options, theme ) +end + +widget.newButton = newButton; + + +local function newPageSlider( options ) + local theme = _getTheme( "pageslider", options ) + + local _pageslider = require( "widgets.widget_pageslider" ) + return createWidget(_pageslider.new, options, theme ) +end + +widget.newPageSlider = newPageSlider; + + +local function newEditField( options ) + local theme; + if options.theme then + theme = require(options.theme)["editField"] + else + theme = _getTheme( "editField", options ); + end + if theme == nil then + --if the current theme does not have a editfield, revert to searchfield + theme = _getTheme( "searchField", options ) + end; + local _editField = require( "widgets.widget_editfield" ) + return createWidget(_editField.new, options, theme ) +end + +widget.newEditField = newEditField; + +local function newSegmentedControl( options ) + local theme = _getTheme( "segmentedControl", options ) + local _segmentedControl = require( "widgets.widget_segmentedControl" ) + return createWidget(_segmentedControl.new, options, theme ) +end + +widget.newSegmentedControl = newSegmentedControl; + + +----------------------------------------------------------------------------------------- +-- newTableView widget +----------------------------------------------------------------------------------------- +local old_newTableView = nil; +local function newTableView( options ) + local _tableView = require( "widgets.widget_tableviewext" ) + return _tableView.new(old_newTableView, options ) +end + +if old_newTableView ~= widget.newTableView then + old_newTableView = widget.newTableView; +end +widget.newTableView = newTableView; + + +local old_newScrollView = nil; +local function newScrollView( options ) + local _scrollView = require( "widgets.widget_scrollviewext" ) + return _scrollView.new(old_newScrollView, options ) +end + +if old_newScrollView ~= widget.newScrollView then + old_newScrollView = widget.newScrollView; +end +widget.newScrollView = newScrollView; + + +local function newSwitch( options ) + local theme = _getTheme( "switch", options ) + local _switch = require( "widgets.widget_switch" ) + return _switch.new( options , theme) +end +widget.newSwitch = newSwitch; + + +local function newPickerWheel( options ) + local theme = _getTheme( "pickerWheel", options ) + local _pickerWheel = require( "widgets.widget_pickerWheel" ) + return _pickerWheel.new( options, theme ) +end +widget.newPickerWheel = newPickerWheel; +