├── android ├── lib │ └── .gitkeep ├── build.properties ├── .gitignore ├── hooks │ ├── README │ ├── uninstall.py │ ├── install.py │ ├── add.py │ └── remove.py ├── .settings │ ├── org.eclipse.jdt.core.prefs │ └── org.eclipse.jdt.apt.core.prefs ├── timodule.xml ├── assets │ └── README ├── manifest ├── build.xml ├── platform │ └── README ├── .project ├── README.md ├── .classpath ├── LICENSE ├── src │ └── ti │ │ └── draggable │ │ ├── DraggableModule.java │ │ ├── ViewProxy.java │ │ ├── DraggableImpl.java │ │ ├── ConfigProxy.java │ │ └── DraggableGesture.java ├── example │ └── app.js └── documentation │ └── index.md ├── ios ├── module.xcconfig ├── metadata.json ├── TiDraggable_Prefix.pch ├── hooks │ ├── README │ ├── uninstall.py │ ├── install.py │ ├── add.py │ └── remove.py ├── Classes │ ├── TiViewProxy+ViewProxyExtended.h │ ├── TiViewProxy+ViewProxyExtended.m │ ├── TiDraggableModuleAssets.h │ ├── TiDraggableModule.h │ ├── TiDraggableModuleAssets.m │ ├── TiDraggableGesture.h │ ├── TiDraggableModule.m │ └── TiDraggableGesture.m ├── draggable.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj ├── timodule.xml ├── assets │ └── README ├── titanium.xcconfig ├── manifest ├── platform │ └── README ├── README.md ├── LICENSE ├── example │ └── app.js ├── documentation │ └── index.md └── build.py ├── .gitignore └── README.md /android/lib/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android/build.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ios/module.xcconfig: -------------------------------------------------------------------------------- 1 | OTHER_LDFLAGS=$(inherited) -framework Foundation -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | bin 3 | build 4 | *.zip 5 | .apt_generated 6 | -------------------------------------------------------------------------------- /ios/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "exports" : ["UI.iOS.NavigationWindow"] 3 | } -------------------------------------------------------------------------------- /ios/TiDraggable_Prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif -------------------------------------------------------------------------------- /android/hooks/README: -------------------------------------------------------------------------------- 1 | These files are not yet supported as of 1.4.0 but will be in a near future release. 2 | -------------------------------------------------------------------------------- /ios/hooks/README: -------------------------------------------------------------------------------- 1 | These files are not yet supported as of 1.4.0 but will be in a near future release. 2 | -------------------------------------------------------------------------------- /android/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | #Thu Sep 02 15:18:34 CDT 2010 2 | eclipse.preferences.version=1 3 | org.eclipse.jdt.core.compiler.processAnnotations=enabled 4 | -------------------------------------------------------------------------------- /ios/Classes/TiViewProxy+ViewProxyExtended.h: -------------------------------------------------------------------------------- 1 | #import "TiViewProxy.h" 2 | 3 | @interface TiViewProxy (ViewProxyExtended) 4 | 5 | - (void)respositionEx; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /ios/draggable.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/timodule.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/timodule.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Global 2 | 3 | build/ 4 | tmp/ 5 | *.zip 6 | 7 | # iOS 8 | 9 | ios/DerivedData/ 10 | xcuserdata/ 11 | *.xccheckout 12 | 13 | # Android 14 | 15 | android/dist/ 16 | android/build-local.properties 17 | android/libs 18 | 19 | !android/build/docs/ 20 | !android/libs/.gitkeep 21 | -------------------------------------------------------------------------------- /ios/assets/README: -------------------------------------------------------------------------------- 1 | Place your assets like PNG files in this directory and they will be packaged with your module. 2 | 3 | If you create a file named ti.draggable.js in this directory, it will be 4 | compiled and used as your module. This allows you to run pure Javascript 5 | modules that are pre-compiled. 6 | 7 | -------------------------------------------------------------------------------- /android/assets/README: -------------------------------------------------------------------------------- 1 | Place your assets like PNG files in this directory and they will be packaged with your module. 2 | 3 | If you create a file named ti.draggable.js in this directory, it will be 4 | compiled and used as your module. This allows you to run pure Javascript 5 | modules that are pre-compiled. 6 | 7 | -------------------------------------------------------------------------------- /android/.settings/org.eclipse.jdt.apt.core.prefs: -------------------------------------------------------------------------------- 1 | #Thu Sep 02 15:18:34 CDT 2010 2 | eclipse.preferences.version=1 3 | org.eclipse.jdt.apt.aptEnabled=true 4 | org.eclipse.jdt.apt.genSrcDir=.apt_generated 5 | org.eclipse.jdt.apt.reconcileEnabled=true 6 | 7 | org.eclipse.jdt.apt.processorOptions/kroll.jsonFile=draggable.json 8 | -------------------------------------------------------------------------------- /ios/titanium.xcconfig: -------------------------------------------------------------------------------- 1 | TITANIUM_SDK_VERSION = 3.5.1.GA 2 | TITANIUM_SDK = ~/Library/Application Support/Titanium/mobilesdk/osx/$(TITANIUM_SDK_VERSION) 3 | TITANIUM_BASE_SDK = "$(TITANIUM_SDK)/iphone/include" 4 | TITANIUM_BASE_SDK2 = "$(TITANIUM_SDK)/iphone/include/TiCore" 5 | HEADER_SEARCH_PATHS= $(TITANIUM_BASE_SDK) $(TITANIUM_BASE_SDK2) 6 | -------------------------------------------------------------------------------- /ios/manifest: -------------------------------------------------------------------------------- 1 | version: 2.0.4 2 | apiversion: 2 3 | architectures: armv7 arm64 i386 x86_64 4 | description: Ti.Draggable 5 | author: Seth Benjamin 6 | license: GPLv3 7 | copyright: Copyright (c) 2012 by Seth Benjamin 8 | 9 | name: draggable 10 | moduleid: ti.draggable 11 | guid: e8c13998-8fa8-4cee-8078-353c27e84d19 12 | platform: iphone 13 | minsdk: 3.5.1.GA 14 | -------------------------------------------------------------------------------- /android/manifest: -------------------------------------------------------------------------------- 1 | version: 2.0.4 2 | apiversion: 2 3 | architectures: armeabi armeabi-v7a x86 4 | description: Ti.Draggable 5 | author: Seth Benjamin 6 | license: GPLv3 7 | copyright: Copyright (c) 2013 Seth Benjamin 8 | 9 | name: draggable 10 | moduleid: ti.draggable 11 | guid: b548a1e9-dbc1-48e9-ab85-caaa914fc0ea 12 | platform: android 13 | minsdk: 3.5.1.GA 14 | -------------------------------------------------------------------------------- /ios/hooks/uninstall.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module uninstall hook that will be 4 | # called when your module is uninstalled 5 | # 6 | import os, sys 7 | 8 | def main(args,argc): 9 | 10 | # TODO: write your uninstall hook here (optional) 11 | 12 | # exit 13 | sys.exit(0) 14 | 15 | 16 | if __name__ == '__main__': 17 | main(sys.argv,len(sys.argv)) 18 | 19 | -------------------------------------------------------------------------------- /android/hooks/uninstall.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module uninstall hook that will be 4 | # called when your module is uninstalled 5 | # 6 | import os, sys 7 | 8 | def main(args,argc): 9 | 10 | # TODO: write your uninstall hook here (optional) 11 | 12 | # exit 13 | sys.exit(0) 14 | 15 | 16 | if __name__ == '__main__': 17 | main(sys.argv,len(sys.argv)) 18 | 19 | -------------------------------------------------------------------------------- /ios/hooks/install.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module install hook that will be 4 | # called when your module is first installed 5 | # 6 | import os, sys 7 | 8 | def main(args,argc): 9 | 10 | # TODO: write your install hook here (optional) 11 | 12 | # exit 13 | sys.exit(0) 14 | 15 | 16 | 17 | if __name__ == '__main__': 18 | main(sys.argv,len(sys.argv)) 19 | 20 | -------------------------------------------------------------------------------- /android/hooks/install.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module install hook that will be 4 | # called when your module is first installed 5 | # 6 | import os, sys 7 | 8 | def main(args,argc): 9 | 10 | # TODO: write your install hook here (optional) 11 | 12 | # exit 13 | sys.exit(0) 14 | 15 | 16 | 17 | if __name__ == '__main__': 18 | main(sys.argv,len(sys.argv)) 19 | 20 | -------------------------------------------------------------------------------- /android/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Ant build script for Titanium Android module draggable 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ios/platform/README: -------------------------------------------------------------------------------- 1 | You can place platform-specific files here in sub-folders named "android" and/or "iphone", just as you can with normal Titanium Mobile SDK projects. Any folders and files you place here will be merged with the platform-specific files in a Titanium Mobile project that uses this module. 2 | 3 | When a Titanium Mobile project that uses this module is built, the files from this platform/ folder will be treated the same as files (if any) from the Titanium Mobile project's platform/ folder. 4 | -------------------------------------------------------------------------------- /android/platform/README: -------------------------------------------------------------------------------- 1 | You can place platform-specific files here in sub-folders named "android" and/or "iphone", just as you can with normal Titanium Mobile SDK projects. Any folders and files you place here will be merged with the platform-specific files in a Titanium Mobile project that uses this module. 2 | 3 | When a Titanium Mobile project that uses this module is built, the files from this platform/ folder will be treated the same as files (if any) from the Titanium Mobile project's platform/ folder. 4 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | draggable 4 | 5 | 6 | 7 | 8 | 9 | com.appcelerator.titanium.core.builder 10 | 11 | 12 | 13 | 14 | com.aptana.ide.core.unifiedBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.jdt.core.javanature 26 | com.appcelerator.titanium.mobile.module.nature 27 | com.aptana.projects.webnature 28 | 29 | 30 | -------------------------------------------------------------------------------- /ios/hooks/add.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module project add hook that will be 4 | # called when your module is added to a project 5 | # 6 | import os, sys 7 | 8 | def dequote(s): 9 | if s[0:1] == '"': 10 | return s[1:-1] 11 | return s 12 | 13 | def main(args,argc): 14 | # You will get the following command line arguments 15 | # in the following order: 16 | # 17 | # project_dir = the full path to the project root directory 18 | # project_type = the type of project (desktop, mobile, ipad) 19 | # project_name = the name of the project 20 | # 21 | project_dir = dequote(os.path.expanduser(args[1])) 22 | project_type = dequote(args[2]) 23 | project_name = dequote(args[3]) 24 | 25 | # TODO: write your add hook here (optional) 26 | 27 | 28 | # exit 29 | sys.exit(0) 30 | 31 | 32 | 33 | if __name__ == '__main__': 34 | main(sys.argv,len(sys.argv)) 35 | 36 | -------------------------------------------------------------------------------- /android/hooks/add.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module project add hook that will be 4 | # called when your module is added to a project 5 | # 6 | import os, sys 7 | 8 | def dequote(s): 9 | if s[0:1] == '"': 10 | return s[1:-1] 11 | return s 12 | 13 | def main(args,argc): 14 | # You will get the following command line arguments 15 | # in the following order: 16 | # 17 | # project_dir = the full path to the project root directory 18 | # project_type = the type of project (desktop, mobile, ipad) 19 | # project_name = the name of the project 20 | # 21 | project_dir = dequote(os.path.expanduser(args[1])) 22 | project_type = dequote(args[2]) 23 | project_name = dequote(args[3]) 24 | 25 | # TODO: write your add hook here (optional) 26 | 27 | 28 | # exit 29 | sys.exit(0) 30 | 31 | 32 | 33 | if __name__ == '__main__': 34 | main(sys.argv,len(sys.argv)) 35 | 36 | -------------------------------------------------------------------------------- /ios/hooks/remove.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module project remove hook that will be 4 | # called when your module is remove from a project 5 | # 6 | import os, sys 7 | 8 | def dequote(s): 9 | if s[0:1] == '"': 10 | return s[1:-1] 11 | return s 12 | 13 | def main(args,argc): 14 | # You will get the following command line arguments 15 | # in the following order: 16 | # 17 | # project_dir = the full path to the project root directory 18 | # project_type = the type of project (desktop, mobile, ipad) 19 | # project_name = the name of the project 20 | # 21 | project_dir = dequote(os.path.expanduser(args[1])) 22 | project_type = dequote(args[2]) 23 | project_name = dequote(args[3]) 24 | 25 | # TODO: write your remove hook here (optional) 26 | 27 | # exit 28 | sys.exit(0) 29 | 30 | 31 | 32 | if __name__ == '__main__': 33 | main(sys.argv,len(sys.argv)) 34 | 35 | -------------------------------------------------------------------------------- /android/hooks/remove.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # This is the module project remove hook that will be 4 | # called when your module is remove from a project 5 | # 6 | import os, sys 7 | 8 | def dequote(s): 9 | if s[0:1] == '"': 10 | return s[1:-1] 11 | return s 12 | 13 | def main(args,argc): 14 | # You will get the following command line arguments 15 | # in the following order: 16 | # 17 | # project_dir = the full path to the project root directory 18 | # project_type = the type of project (desktop, mobile, ipad) 19 | # project_name = the name of the project 20 | # 21 | project_dir = dequote(os.path.expanduser(args[1])) 22 | project_type = dequote(args[2]) 23 | project_name = dequote(args[3]) 24 | 25 | # TODO: write your remove hook here (optional) 26 | 27 | # exit 28 | sys.exit(0) 29 | 30 | 31 | 32 | if __name__ == '__main__': 33 | main(sys.argv,len(sys.argv)) 34 | 35 | -------------------------------------------------------------------------------- /ios/Classes/TiViewProxy+ViewProxyExtended.m: -------------------------------------------------------------------------------- 1 | #import "TiViewProxy+ViewProxyExtended.h" 2 | 3 | @implementation TiViewProxy (ViewProxyExtended) 4 | 5 | - (void)respositionEx 6 | { 7 | if (! repositioning) 8 | { 9 | ENSURE_UI_THREAD_0_ARGS 10 | 11 | repositioning = YES; 12 | 13 | UIView *parentView = [parent parentViewForChild:self]; 14 | CGSize referenceSize = (parentView != nil) ? parentView.bounds.size : sandboxBounds.size; 15 | 16 | positionCache = PositionConstraintGivenSizeBoundsAddingResizing(&layoutProperties, self, sizeCache.size, 17 | [[view layer] anchorPoint], referenceSize, sandboxBounds.size, &autoresizeCache); 18 | 19 | positionCache.x += sizeCache.origin.x + sandboxBounds.origin.x; 20 | positionCache.y += sizeCache.origin.y + sandboxBounds.origin.y; 21 | 22 | [view setCenter:positionCache]; 23 | [self refreshPosition]; 24 | 25 | repositioning = NO; 26 | } 27 | 28 | } 29 | 30 | @end -------------------------------------------------------------------------------- /ios/README.md: -------------------------------------------------------------------------------- 1 | # [TiDraggable](https://github.com/animecyc/TiDraggable) - Native Draggable Views 2 | 3 | An enhanced fork of the original [TiDraggable](https://github.com/pec1985/TiDraggable) module by [Pedro](http://twitter.com/pecdev) [Enrique](https://github.com/pec1985), allows for simple creation of "draggable" views. 4 | 5 | ## Installation 6 | 7 | In your `tiapp.xml` file add the following to the `modules` node: 8 | 9 | ti.draggable 10 | 11 | ## Usage 12 | 13 | ```javascript 14 | var Draggable = require('ti.draggable'), 15 | mainWindow = Ti.UI.createWindow({ 16 | backgroundColor : 'white' 17 | }), 18 | draggableView = Draggable.createView({ 19 | width : 100, 20 | height : 100, 21 | backgroundColor : 'black' 22 | }); 23 | 24 | mainWindow.add(draggableView); 25 | mainWindow.open(); 26 | ``` 27 | 28 | ## Credits & Notes 29 | 30 | The work is largely based on [Pedro](http://twitter.com/pecdev) [Enrique's](https://github.com/pec1985) [TiDraggable](https://github.com/pec1985/TiDraggable) module license under the MIT (V2) license. -------------------------------------------------------------------------------- /android/README.md: -------------------------------------------------------------------------------- 1 | # [TiDraggable](https://github.com/animecyc/TiDraggable) - Native Draggable Views 2 | 3 | An enhanced fork of the original [TiDraggable](https://github.com/pec1985/TiDraggable) module by [Pedro](http://twitter.com/pecdev) [Enrique](https://github.com/pec1985), allows for simple creation of "draggable" views. 4 | 5 | ## Installation 6 | 7 | In your `tiapp.xml` file add the following to the `modules` node: 8 | 9 | ti.draggable 10 | 11 | ## Usage 12 | 13 | ```javascript 14 | var Draggable = require('ti.draggable'), 15 | mainWindow = Ti.UI.createWindow({ 16 | backgroundColor : 'white' 17 | }), 18 | draggableView = Draggable.createView({ 19 | width : 100, 20 | height : 100, 21 | backgroundColor : 'black' 22 | }); 23 | 24 | mainWindow.add(draggableView); 25 | mainWindow.open(); 26 | ``` 27 | 28 | ## Credits & Notes 29 | 30 | The work is largely based on [Pedro](http://twitter.com/pecdev) [Enrique's](https://github.com/pec1985) [TiDraggable](https://github.com/pec1985/TiDraggable) module license under the MIT (V2) license. -------------------------------------------------------------------------------- /android/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ios/LICENSE: -------------------------------------------------------------------------------- 1 | An enhanced fork of the original TiDraggable module by Pedro Enrique, 2 | allows for simple creation of "draggable" views. 3 | 4 | Copyright (C) 2013 Seth Benjamin 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | 19 | -- Original License -- 20 | 21 | Copyright 2012 Pedro Enrique 22 | 23 | Licensed under the Apache License, Version 2.0 (the "License"); 24 | you may not use this file except in compliance with the License. 25 | You may obtain a copy of the License at 26 | 27 | http://www.apache.org/licenses/LICENSE-2.0 28 | 29 | Unless required by applicable law or agreed to in writing, software 30 | distributed under the License is distributed on an "AS IS" BASIS, 31 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 | See the License for the specific language governing permissions and 33 | limitations under the License. -------------------------------------------------------------------------------- /android/LICENSE: -------------------------------------------------------------------------------- 1 | An enhanced fork of the original TiDraggable module by Pedro Enrique, 2 | allows for simple creation of "draggable" views. 3 | 4 | Copyright (C) 2013 Seth Benjamin 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | 19 | -- Original License -- 20 | 21 | Copyright 2012 Pedro Enrique 22 | 23 | Licensed under the Apache License, Version 2.0 (the "License"); 24 | you may not use this file except in compliance with the License. 25 | You may obtain a copy of the License at 26 | 27 | http://www.apache.org/licenses/LICENSE-2.0 28 | 29 | Unless required by applicable law or agreed to in writing, software 30 | distributed under the License is distributed on an "AS IS" BASIS, 31 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 | See the License for the specific language governing permissions and 33 | limitations under the License. -------------------------------------------------------------------------------- /ios/Classes/TiDraggableModuleAssets.h: -------------------------------------------------------------------------------- 1 | /** 2 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 3 | * allows for simple creation of "draggable" views. 4 | * 5 | * Copyright (C) 2013 Seth Benjamin 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | * -- Original License -- 21 | * 22 | * Copyright 2012 Pedro Enrique 23 | * 24 | * Licensed under the Apache License, Version 2.0 (the "License"); 25 | * you may not use this file except in compliance with the License. 26 | * You may obtain a copy of the License at 27 | * 28 | * http://www.apache.org/licenses/LICENSE-2.0 29 | * 30 | * Unless required by applicable law or agreed to in writing, software 31 | * distributed under the License is distributed on an "AS IS" BASIS, 32 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | * See the License for the specific language governing permissions and 34 | * limitations under the License. 35 | */ 36 | 37 | @interface TiDraggableModuleAssets : NSObject 38 | 39 | - (NSData*) moduleAsset; 40 | 41 | @end -------------------------------------------------------------------------------- /ios/Classes/TiDraggableModule.h: -------------------------------------------------------------------------------- 1 | /** 2 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 3 | * allows for simple creation of "draggable" views. 4 | * 5 | * Copyright (C) 2013 Seth Benjamin 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | * -- Original License -- 21 | * 22 | * Copyright 2012 Pedro Enrique 23 | * 24 | * Licensed under the Apache License, Version 2.0 (the "License"); 25 | * you may not use this file except in compliance with the License. 26 | * You may obtain a copy of the License at 27 | * 28 | * http://www.apache.org/licenses/LICENSE-2.0 29 | * 30 | * Unless required by applicable law or agreed to in writing, software 31 | * distributed under the License is distributed on an "AS IS" BASIS, 32 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | * See the License for the specific language governing permissions and 34 | * limitations under the License. 35 | */ 36 | 37 | #import "TiModule.h" 38 | #import "TiBase.h" 39 | #import "TiHost.h" 40 | #import "TiUtils.h" 41 | 42 | @interface TiDraggableModule : TiModule 43 | 44 | @end -------------------------------------------------------------------------------- /ios/Classes/TiDraggableModuleAssets.m: -------------------------------------------------------------------------------- 1 | /** 2 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 3 | * allows for simple creation of "draggable" views. 4 | * 5 | * Copyright (C) 2013 Seth Benjamin 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | * -- Original License -- 21 | * 22 | * Copyright 2012 Pedro Enrique 23 | * 24 | * Licensed under the Apache License, Version 2.0 (the "License"); 25 | * you may not use this file except in compliance with the License. 26 | * You may obtain a copy of the License at 27 | * 28 | * http://www.apache.org/licenses/LICENSE-2.0 29 | * 30 | * Unless required by applicable law or agreed to in writing, software 31 | * distributed under the License is distributed on an "AS IS" BASIS, 32 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | * See the License for the specific language governing permissions and 34 | * limitations under the License. 35 | */ 36 | 37 | #import "TiDraggableModuleAssets.h" 38 | 39 | extern NSData * dataWithHexString (NSString * hexString); 40 | 41 | @implementation TiDraggableModuleAssets 42 | 43 | - (NSData*) moduleAsset 44 | { 45 | //##TI_AUTOGEN_BEGIN asset 46 | //Compiler generates code for asset here 47 | return nil; // DEFAULT BEHAVIOR 48 | //##TI_AUTOGEN_END asset 49 | } 50 | 51 | @end -------------------------------------------------------------------------------- /ios/Classes/TiDraggableGesture.h: -------------------------------------------------------------------------------- 1 | /** 2 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 3 | * allows for simple creation of "draggable" views. 4 | * 5 | * Copyright (C) 2013 Seth Benjamin 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | * -- Original License -- 21 | * 22 | * Copyright 2012 Pedro Enrique 23 | * 24 | * Licensed under the Apache License, Version 2.0 (the "License"); 25 | * you may not use this file except in compliance with the License. 26 | * You may obtain a copy of the License at 27 | * 28 | * http://www.apache.org/licenses/LICENSE-2.0 29 | * 30 | * Unless required by applicable law or agreed to in writing, software 31 | * distributed under the License is distributed on an "AS IS" BASIS, 32 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | * See the License for the specific language governing permissions and 34 | * limitations under the License. 35 | */ 36 | 37 | #import 38 | #import "TiUtils.h" 39 | #import "TiUIView.h" 40 | #import "TiViewProxy.h" 41 | 42 | @interface TiDraggableGesture : TiProxy 43 | { 44 | CGPoint touchStart; 45 | CGPoint touchEnd; 46 | CGPoint lastAnimationPosition; 47 | } 48 | 49 | - (id)initWithProxy:(TiViewProxy*)proxy andOptions:(NSDictionary*)options; 50 | 51 | @property (nonatomic, assign) TiViewProxy* proxy; 52 | @property (nonatomic, retain) UIGestureRecognizer* gesture; 53 | 54 | typedef void (^CallbackBlock)(void); 55 | 56 | @end -------------------------------------------------------------------------------- /android/src/ti/draggable/DraggableModule.java: -------------------------------------------------------------------------------- 1 | /** 2 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 3 | * allows for simple creation of "draggable" views. 4 | * 5 | * Copyright (C) 2013 Seth Benjamin 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | * -- Original License -- 21 | * 22 | * Copyright 2012 Pedro Enrique 23 | * 24 | * Licensed under the Apache License, Version 2.0 (the "License"); 25 | * you may not use this file except in compliance with the License. 26 | * You may obtain a copy of the License at 27 | * 28 | * http://www.apache.org/licenses/LICENSE-2.0 29 | * 30 | * Unless required by applicable law or agreed to in writing, software 31 | * distributed under the License is distributed on an "AS IS" BASIS, 32 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | * See the License for the specific language governing permissions and 34 | * limitations under the License. 35 | */ 36 | package ti.draggable; 37 | 38 | import org.appcelerator.kroll.KrollModule; 39 | import org.appcelerator.kroll.annotations.Kroll; 40 | import org.appcelerator.kroll.common.Log; 41 | 42 | @Kroll.module(name="Draggable", id="ti.draggable") 43 | public class DraggableModule extends KrollModule 44 | { 45 | private static final String TAG = "TiDraggable"; 46 | private static Boolean DEBUGGING = true; 47 | 48 | public DraggableModule() 49 | { 50 | super(); 51 | } 52 | 53 | public static void debugLog(String message) 54 | { 55 | if (DEBUGGING) 56 | { 57 | Log.d(TAG, message); 58 | } 59 | } 60 | 61 | @Kroll.setProperty 62 | public void setDebug(Boolean debug) 63 | { 64 | DEBUGGING = debug; 65 | } 66 | 67 | @Kroll.getProperty 68 | public Boolean getDebug() 69 | { 70 | return DEBUGGING; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /android/src/ti/draggable/ViewProxy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 3 | * allows for simple creation of "draggable" views. 4 | * 5 | * Copyright (C) 2013 Seth Benjamin 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | * -- Original License -- 21 | * 22 | * Copyright 2012 Pedro Enrique 23 | * 24 | * Licensed under the Apache License, Version 2.0 (the "License"); 25 | * you may not use this file except in compliance with the License. 26 | * You may obtain a copy of the License at 27 | * 28 | * http://www.apache.org/licenses/LICENSE-2.0 29 | * 30 | * Unless required by applicable law or agreed to in writing, software 31 | * distributed under the License is distributed on an "AS IS" BASIS, 32 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | * See the License for the specific language governing permissions and 34 | * limitations under the License. 35 | */ 36 | package ti.draggable; 37 | 38 | import org.appcelerator.kroll.KrollDict; 39 | import org.appcelerator.kroll.KrollProxy; 40 | import org.appcelerator.kroll.annotations.Kroll; 41 | import org.appcelerator.titanium.proxy.TiViewProxy; 42 | import org.appcelerator.titanium.view.TiUIView; 43 | 44 | import android.app.Activity; 45 | 46 | @Kroll.proxy(creatableInModule = DraggableModule.class) 47 | public class ViewProxy extends TiViewProxy 48 | { 49 | 50 | public ViewProxy() 51 | { 52 | super(); 53 | } 54 | 55 | @Override 56 | public void handleCreationDict(KrollDict options) 57 | { 58 | super.handleCreationDict(options); 59 | 60 | ConfigProxy config = new ConfigProxy(options.getKrollDict("draggableConfig")); 61 | 62 | this.setProperty("draggable", config); 63 | } 64 | 65 | @Override 66 | public TiUIView createView(Activity activity) 67 | { 68 | if (this.view == null) 69 | { 70 | TiUIView view = new DraggableImpl(this); 71 | 72 | setView(view); 73 | 74 | return view; 75 | } 76 | 77 | return this.view; 78 | } 79 | 80 | @Kroll.getProperty @Kroll.method 81 | public KrollProxy getDraggable() 82 | { 83 | return (KrollProxy) this.getProperty("draggable"); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /ios/example/app.js: -------------------------------------------------------------------------------- 1 | /*global require,console,Ti*/ 2 | /*jslint devel: true, forin: true */ 3 | /** 4 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 5 | * allows for simple creation of "draggable" views. 6 | * 7 | * Copyright (C) 2013 Seth Benjamin 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | * 22 | * -- Original License -- 23 | * 24 | * Copyright 2012 Pedro Enrique 25 | * 26 | * Licensed under the Apache License, Version 2.0 (the "License"); 27 | * you may not use this file except in compliance with the License. 28 | * You may obtain a copy of the License at 29 | * 30 | * http://www.apache.org/licenses/LICENSE-2.0 31 | * 32 | * Unless required by applicable law or agreed to in writing, software 33 | * distributed under the License is distributed on an "AS IS" BASIS, 34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 35 | * See the License for the specific language governing permissions and 36 | * limitations under the License. 37 | */ 38 | 39 | (function () { 40 | 'use strict'; 41 | 42 | var Draggable = require('ti.draggable'), 43 | mainWindow = Ti.UI.createWindow({ 44 | backgroundColor : 'white', 45 | exitOnClose : true, 46 | fullscreen : true 47 | }), 48 | subscribe = function (proxy, observer) { 49 | var key, events, eIndex; 50 | 51 | for (key in observer) { 52 | if (typeof observer[key] === 'function') { 53 | events = key.split(' '); 54 | 55 | for (eIndex in events) { 56 | proxy.addEventListener(events[eIndex], observer[key]); 57 | } 58 | } 59 | } 60 | }, 61 | createDraggableSquare = function (name, color, axis) { 62 | var view = Draggable.createView({ 63 | width : 100, 64 | height : 100, 65 | borderRadius : 3, 66 | backgroundColor : color || 'black', 67 | draggableConfig : { 68 | axis : axis, 69 | minLeft : 0, 70 | maxLeft : Ti.Platform.displayCaps.platformWidth - 100, 71 | minTop : 0, 72 | maxTop : Ti.Platform.displayCaps.platformHeight - 100, 73 | } 74 | }); 75 | 76 | view.add(Ti.UI.createLabel({ 77 | text : name 78 | })); 79 | 80 | subscribe(view, { 81 | 'start move end cancel' : function (e) { 82 | console.log( 83 | 'Event: ' + e.type, 84 | 'Left: ' + e.left, 85 | 'Top: ' + e.top 86 | ); 87 | } 88 | }); 89 | 90 | return view; 91 | }; 92 | 93 | mainWindow.add(createDraggableSquare('Horizontal', 'red', 'x')); 94 | mainWindow.add(createDraggableSquare('Vertical', 'blue', 'y')); 95 | mainWindow.add(createDraggableSquare('Free', 'green')); 96 | 97 | mainWindow.open(); 98 | }()); -------------------------------------------------------------------------------- /android/example/app.js: -------------------------------------------------------------------------------- 1 | /*global require,console,Ti*/ 2 | /*jslint devel: true, forin: true */ 3 | /** 4 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 5 | * allows for simple creation of "draggable" views. 6 | * 7 | * Copyright (C) 2013 Seth Benjamin 8 | * 9 | * This program is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * This program is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with this program. If not, see . 21 | * 22 | * -- Original License -- 23 | * 24 | * Copyright 2012 Pedro Enrique 25 | * 26 | * Licensed under the Apache License, Version 2.0 (the "License"); 27 | * you may not use this file except in compliance with the License. 28 | * You may obtain a copy of the License at 29 | * 30 | * http://www.apache.org/licenses/LICENSE-2.0 31 | * 32 | * Unless required by applicable law or agreed to in writing, software 33 | * distributed under the License is distributed on an "AS IS" BASIS, 34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 35 | * See the License for the specific language governing permissions and 36 | * limitations under the License. 37 | */ 38 | 39 | (function () { 40 | 'use strict'; 41 | 42 | var Draggable = require('ti.draggable'), 43 | mainWindow = Ti.UI.createWindow({ 44 | backgroundColor : 'white', 45 | exitOnClose : true, 46 | fullscreen : true, 47 | navBarHidden : true 48 | }), 49 | subscribe = function (proxy, observer) { 50 | var key, events, eIndex; 51 | 52 | for (key in observer) { 53 | if (typeof observer[key] === 'function') { 54 | events = key.split(' '); 55 | 56 | for (eIndex in events) { 57 | proxy.addEventListener(events[eIndex], observer[key]); 58 | } 59 | } 60 | } 61 | }, 62 | createDraggableSquare = function (name, color, axis) { 63 | var view = Draggable.createView({ 64 | width : 100, 65 | height : 100, 66 | borderRadius : 3, 67 | backgroundColor : color || 'black', 68 | draggableConfig : { 69 | axis : axis, 70 | minLeft : 0, 71 | maxLeft : Ti.Platform.displayCaps.platformWidth, 72 | minTop : 0, 73 | maxTop : Ti.Platform.displayCaps.platformHeight, 74 | } 75 | }); 76 | 77 | view.add(Ti.UI.createLabel({ 78 | text : name 79 | })); 80 | 81 | subscribe(view, { 82 | 'start move end cancel' : function (e) { 83 | console.log( 84 | 'Event: ' + e.type, 85 | 'Left: ' + e.left, 86 | 'Top: ' + e.top 87 | ); 88 | } 89 | }); 90 | 91 | return view; 92 | }; 93 | 94 | mainWindow.add(createDraggableSquare('Horizontal', 'red', 'x')); 95 | mainWindow.add(createDraggableSquare('Vertical', 'blue', 'y')); 96 | mainWindow.add(createDraggableSquare('Free', 'green')); 97 | 98 | mainWindow.open(); 99 | }()); -------------------------------------------------------------------------------- /ios/Classes/TiDraggableModule.m: -------------------------------------------------------------------------------- 1 | /** 2 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 3 | * allows for simple creation of "draggable" views. 4 | * 5 | * Copyright (C) 2013 Seth Benjamin 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | * -- Original License -- 21 | * 22 | * Copyright 2012 Pedro Enrique 23 | * 24 | * Licensed under the Apache License, Version 2.0 (the "License"); 25 | * you may not use this file except in compliance with the License. 26 | * You may obtain a copy of the License at 27 | * 28 | * http://www.apache.org/licenses/LICENSE-2.0 29 | * 30 | * Unless required by applicable law or agreed to in writing, software 31 | * distributed under the License is distributed on an "AS IS" BASIS, 32 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | * See the License for the specific language governing permissions and 34 | * limitations under the License. 35 | */ 36 | 37 | #import 38 | #import 39 | #import "TiDraggableModule.h" 40 | #import "TiDraggableGesture.h" 41 | #import "TiUIiOSNavWindowProxy.h" 42 | 43 | @implementation TiDraggableModule 44 | 45 | #pragma mark Internal 46 | 47 | - (id)moduleGUID 48 | { 49 | return @"e8c13998-8fa8-4cee-8078-353c27e84d19"; 50 | } 51 | 52 | - (NSString*)moduleId 53 | { 54 | return @"ti.draggable"; 55 | } 56 | 57 | - (void)makeDraggable:(id)args 58 | { 59 | ENSURE_UI_THREAD_1_ARG(args); 60 | 61 | TiViewProxy* proxy = nil; 62 | NSDictionary* options = nil; 63 | 64 | ENSURE_ARG_AT_INDEX(proxy, args, 0, TiViewProxy); 65 | ENSURE_ARG_OR_NIL_AT_INDEX(options, args, 1, NSDictionary); 66 | 67 | if (proxy) 68 | { 69 | [[TiDraggableGesture alloc] initWithProxy:proxy andOptions:options]; 70 | } 71 | } 72 | 73 | - (id)createProxy:(NSArray*)args forName:(NSString*)name context:(id)context 74 | { 75 | TiViewProxy* proxy = nil; 76 | 77 | if ([name isEqualToString:@"createNavigationWindow"]) 78 | { 79 | proxy = [[[TiUIiOSNavWindowProxy alloc] _initWithPageContext:[self executionContext] args:args] autorelease]; 80 | } 81 | else 82 | { 83 | Ivar nameLookupIvar = class_getInstanceVariable([super class], "classNameLookup"); 84 | CFMutableDictionaryRef cnLookup = (CFMutableDictionaryRef) object_getIvar(self, nameLookupIvar); 85 | Class resultClass = (Class) CFDictionaryGetValue(cnLookup, name); 86 | 87 | if (resultClass == NULL) 88 | { 89 | NSRange range = [name rangeOfString:@"create"]; 90 | 91 | if (range.location == NSNotFound) 92 | { 93 | return nil; 94 | } 95 | 96 | NSString *className = [NSString stringWithFormat:@"Ti%@Proxy", [name substringFromIndex:range.location + 6]]; 97 | 98 | resultClass = NSClassFromString(className); 99 | 100 | if (! [resultClass isSubclassOfClass:[TiViewProxy class]]) 101 | { 102 | @throw [NSException exceptionWithName:@"ti.draggable" 103 | reason:[NSString stringWithFormat:@"invalid method (%@) passed to %@", name, [self class]] 104 | userInfo:nil]; 105 | } 106 | 107 | CFDictionarySetValue(cnLookup, name, resultClass); 108 | } 109 | 110 | proxy = [[[resultClass alloc] _initWithPageContext:context args:args] autorelease]; 111 | } 112 | 113 | if (proxy) 114 | { 115 | NSDictionary* options = [proxy valueForKeyPath:@"draggableConfig"]; 116 | 117 | [[TiDraggableGesture alloc] initWithProxy:proxy andOptions:options]; 118 | } 119 | 120 | return proxy; 121 | } 122 | 123 | @end -------------------------------------------------------------------------------- /android/src/ti/draggable/DraggableImpl.java: -------------------------------------------------------------------------------- 1 | /** 2 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 3 | * allows for simple creation of "draggable" views. 4 | * 5 | * Copyright (C) 2013 Seth Benjamin 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | * -- Original License -- 21 | * 22 | * Copyright 2012 Pedro Enrique 23 | * 24 | * Licensed under the Apache License, Version 2.0 (the "License"); 25 | * you may not use this file except in compliance with the License. 26 | * You may obtain a copy of the License at 27 | * 28 | * http://www.apache.org/licenses/LICENSE-2.0 29 | * 30 | * Unless required by applicable law or agreed to in writing, software 31 | * distributed under the License is distributed on an "AS IS" BASIS, 32 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | * See the License for the specific language governing permissions and 34 | * limitations under the License. 35 | */ 36 | package ti.draggable; 37 | 38 | import java.lang.ref.WeakReference; 39 | 40 | import org.appcelerator.kroll.KrollProxy; 41 | import org.appcelerator.titanium.TiC; 42 | import org.appcelerator.titanium.proxy.TiViewProxy; 43 | import org.appcelerator.titanium.util.TiConvert; 44 | import org.appcelerator.titanium.view.TiCompositeLayout; 45 | import org.appcelerator.titanium.view.TiCompositeLayout.LayoutArrangement; 46 | import org.appcelerator.titanium.view.TiUIView; 47 | 48 | import android.content.Context; 49 | import android.view.MotionEvent; 50 | 51 | public class DraggableImpl extends TiUIView { 52 | public DraggableGesture listener; 53 | protected TiCompositeLayout.LayoutParams layout; 54 | 55 | public class DraggableView extends TiCompositeLayout 56 | { 57 | public DraggableView(Context context, LayoutArrangement arrangement) 58 | { 59 | super(context, arrangement); 60 | } 61 | 62 | @Override 63 | public boolean onInterceptTouchEvent(MotionEvent event) 64 | { 65 | switch (event.getAction()) 66 | { 67 | case MotionEvent.ACTION_DOWN: 68 | listener.startDrag(event); 69 | case MotionEvent.ACTION_MOVE: 70 | listener.determineDrag(event); 71 | break; 72 | case MotionEvent.ACTION_UP: 73 | case MotionEvent.ACTION_CANCEL: 74 | listener.stopDrag(event); 75 | break; 76 | } 77 | 78 | return listener.isBeingDragged; 79 | } 80 | } 81 | 82 | public DraggableImpl(KrollProxy proxy) 83 | { 84 | super((TiViewProxy) proxy); 85 | 86 | LayoutArrangement arrangement = LayoutArrangement.DEFAULT; 87 | 88 | if (proxy.hasProperty(TiC.PROPERTY_LAYOUT)) 89 | { 90 | String layoutProperty = TiConvert.toString(proxy.getProperty(TiC.PROPERTY_LAYOUT)); 91 | 92 | if (layoutProperty.equals(TiC.LAYOUT_HORIZONTAL)) 93 | { 94 | arrangement = LayoutArrangement.HORIZONTAL; 95 | } 96 | else if (layoutProperty.equals(TiC.LAYOUT_VERTICAL)) 97 | { 98 | arrangement = LayoutArrangement.VERTICAL; 99 | } 100 | } 101 | 102 | setNativeView(new DraggableView(proxy.getActivity(), arrangement)); 103 | setupDraggableGesture(); 104 | } 105 | 106 | protected void setupDraggableGesture() 107 | { 108 | ConfigProxy draggableConfig = (ConfigProxy) proxy.getProperty("draggable"); 109 | WeakReference weakConfig = new WeakReference(draggableConfig); 110 | 111 | this.getLayoutParams().autoFillsHeight = true; 112 | this.getLayoutParams().autoFillsWidth = true; 113 | this.listener = new DraggableGesture((TiViewProxy) proxy, this, weakConfig); 114 | 115 | this.getOuterView().setOnTouchListener(listener); 116 | 117 | draggableConfig.setDraggableImpl(new WeakReference(this)); 118 | } 119 | 120 | @Override 121 | public void registerForTouch() 122 | { 123 | // 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /android/src/ti/draggable/ConfigProxy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 3 | * allows for simple creation of "draggable" views. 4 | * 5 | * Copyright (C) 2013 Seth Benjamin 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | * -- Original License -- 21 | * 22 | * Copyright 2012 Pedro Enrique 23 | * 24 | * Licensed under the Apache License, Version 2.0 (the "License"); 25 | * you may not use this file except in compliance with the License. 26 | * You may obtain a copy of the License at 27 | * 28 | * http://www.apache.org/licenses/LICENSE-2.0 29 | * 30 | * Unless required by applicable law or agreed to in writing, software 31 | * distributed under the License is distributed on an "AS IS" BASIS, 32 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | * See the License for the specific language governing permissions and 34 | * limitations under the License. 35 | */ 36 | package ti.draggable; 37 | 38 | import java.lang.ref.WeakReference; 39 | import java.util.List; 40 | 41 | import org.appcelerator.kroll.KrollDict; 42 | import org.appcelerator.kroll.KrollPropertyChange; 43 | import org.appcelerator.kroll.KrollProxy; 44 | import org.appcelerator.kroll.KrollProxyListener; 45 | import org.appcelerator.kroll.annotations.Kroll; 46 | import org.appcelerator.titanium.TiApplication; 47 | import org.appcelerator.titanium.TiDimension; 48 | import org.appcelerator.titanium.util.TiConvert; 49 | 50 | import android.view.View; 51 | 52 | @Kroll.proxy 53 | public class ConfigProxy extends KrollProxy implements KrollProxyListener 54 | { 55 | protected WeakReference draggableImpl; 56 | public int threshold = 0; 57 | 58 | public ConfigProxy(KrollDict config) 59 | { 60 | super(); 61 | 62 | properties.put("enabled", config != null && config.containsKeyAndNotNull("enabled") ? TiConvert.toBoolean(config, "enabled", true) : true); 63 | properties.put("ensureRight", config != null && config.containsKeyAndNotNull("ensureRight") ? TiConvert.toBoolean(config, "ensureRight", false) : false); 64 | properties.put("ensureBottom", config != null && config.containsKeyAndNotNull("ensureBottom") ? TiConvert.toBoolean(config, "ensureBottom", false) : false); 65 | properties.put("axis", config != null && config.containsKeyAndNotNull("axis") ? TiConvert.toString(config, "axis") : null); 66 | properties.put("maps", config != null && config.containsKeyAndNotNull("maps") ? (Object[]) config.get("maps") : null); 67 | properties.put("minLeft", config != null && config.containsKeyAndNotNull("minLeft") ? TiConvert.toTiDimension(config, "minLeft", TiDimension.TYPE_LEFT) : null); 68 | properties.put("maxLeft", config != null && config.containsKeyAndNotNull("minLeft") ? TiConvert.toTiDimension(config, "maxLeft", TiDimension.TYPE_LEFT) : null); 69 | properties.put("minTop", config != null && config.containsKeyAndNotNull("minTop") ? TiConvert.toTiDimension(config, "minTop", TiDimension.TYPE_TOP) : null); 70 | properties.put("maxTop", config != null && config.containsKeyAndNotNull("maxTop") ? TiConvert.toTiDimension(config, "maxTop", TiDimension.TYPE_TOP) : null); 71 | properties.put("threshold", config != null && config.containsKeyAndNotNull("threshold") ? TiConvert.toInt(config, "threshold") : null); 72 | 73 | setModelListener(this); 74 | } 75 | 76 | public Integer getDimensionAsPixels(String key) 77 | { 78 | KrollDict props = this.getProperties(); 79 | 80 | if (props.containsKeyAndNotNull(key) && props.get(key) instanceof TiDimension) 81 | { 82 | TiDimension dimension = (TiDimension) props.get(key); 83 | View decorView = this.getDecorView(); 84 | 85 | return dimension.getAsPixels(decorView); 86 | } 87 | 88 | return null; 89 | } 90 | 91 | public void setDraggableImpl(WeakReference draggableView) 92 | { 93 | draggableImpl = draggableView; 94 | } 95 | 96 | public DraggableImpl getDraggableImpl() 97 | { 98 | return draggableImpl != null ? draggableImpl.get() : null; 99 | } 100 | 101 | @Kroll.method 102 | public void setConfig(Object[] args) 103 | { 104 | if (args.length >= 2) 105 | { 106 | setPropertyAndFire((String) args[0], args[1]); 107 | } 108 | else if (args.length == 1) 109 | { 110 | applyProperties(args[0]); 111 | } 112 | } 113 | 114 | @Kroll.method 115 | @Kroll.getProperty 116 | public int getDefaultThreshold() 117 | { 118 | return this.threshold; 119 | } 120 | 121 | @Override 122 | public void listenerAdded(String type, int count, KrollProxy proxy) 123 | { 124 | // Unused 125 | } 126 | 127 | @Override 128 | public void listenerRemoved(String type, int count, KrollProxy proxy) 129 | { 130 | // Unused 131 | } 132 | 133 | @Override 134 | public void processProperties(KrollDict properties) 135 | { 136 | // Unused 137 | } 138 | 139 | @Override 140 | public void propertiesChanged(List changes, KrollProxy proxy) 141 | { 142 | // Unused 143 | } 144 | 145 | @Override 146 | public void propertyChanged(String key, Object oldValue, Object newValue, KrollProxy proxy) 147 | { 148 | if (key.equals("maps")) 149 | { 150 | DraggableImpl impl = getDraggableImpl(); 151 | 152 | if (impl != null) 153 | { 154 | getDraggableImpl().listener.prepareMappedProxies(); 155 | } 156 | } 157 | else if (key.equals("minLeft") || key.equals("maxLeft")) 158 | { 159 | properties.put(key, TiConvert.toTiDimension(TiConvert.toString(newValue), TiDimension.TYPE_LEFT)); 160 | } 161 | else if (key.equals("minTop") || key.equals("maxTop")) 162 | { 163 | properties.put(key, TiConvert.toTiDimension(TiConvert.toString(newValue), TiDimension.TYPE_TOP)); 164 | } 165 | } 166 | 167 | public View getDecorView() 168 | { 169 | return TiApplication.getAppCurrentActivity().getWindow().getDecorView(); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TiDraggable - Native Draggable Views 2 | 3 | An enhanced fork of the original [TiDraggable](https://github.com/pec1985/TiDraggable) module by [Pedro](http://twitter.com/pecdev) [Enrique](https://github.com/pec1985), allows for simple creation of "draggable" views. 4 | 5 | ## Enhancements & Fixes 6 | 7 | - Improved drag performance for iOS and Android. 8 | - Updated public APIs for more seamless integration. 9 | - Removed the InfiniteScroll class as it doesn't really have much to do with the overall module. 10 | - Removed unnecessary APIs to reduce overall module footprint. 11 | - Removed unused variables and organized imports. 12 | - Added ability to unset boundaries. 13 | - Mapped the missing `cancel` gesture to the `end` gesture (firing the respective event). 14 | - Added `ensureRight` and `ensureBottom`, this allows for stable dragging of views where the dimensions are not known. 15 | - Added `enabled` boolean property for toggeling drag 16 | - Views can be mapped and translated with a draggable view. 17 | - Draggable implementation now has its own configurable property called `draggable`. 18 | - iOS: Supports all Ti.UI.View subclasses and Ti.UI.View wrapped views (View, Window, Label) 19 | - Android: Fixed a bug where touch events were not correctly passed to children or bubbled to the parent. 20 | - Android: Fixed a bug where min and max bounds were being incorrectly reported after being set. 21 | - Android: Improved drag tracking. It plays nice with child views now. 22 | - Android: Added a touch threshold to ensure all child views have a chance to have their respective events fired. 23 | 24 | ## Usage 25 | 26 | ```javascript 27 | var Draggable = require('ti.draggable'), 28 | mainWindow = Ti.UI.createWindow({ 29 | backgroundColor : 'white' 30 | }), 31 | draggableView = Draggable.createView({ 32 | width : 100, 33 | height : 100, 34 | backgroundColor : 'black' 35 | }); 36 | 37 | mainWindow.add(draggableView); 38 | mainWindow.open(); 39 | ``` 40 | 41 | > If you are building the Android module, make sure you update the .classpath and build.properties files to match your setup. 42 | 43 | ## Module Reference 44 | 45 | ### Draggable.createView(viewOptions); 46 | 47 | Create a draggable view. All of Titanium's properties are supported along the additional `draggableConfig` property containing any options that should be set upon creation. See [Options](#options) 48 | 49 | > When the draggable proxy is created a new property is set called `draggable` which stores all the configuration properties and allows for options to be updated after creation. 50 | 51 | **iOS Notes** 52 | You can pass almost all of iOS' supported Ti.UI creation methods to the draggable module such as `Draggable.createView( ... )` or `Draggable.createWindow( ... )`. While `Ti.UI.View` and `Ti.UI.Window` are fully supported on iOS other APIs haven't been fully tested. 53 | 54 | **Android Notes** 55 | Android only supports the creation of Ti.UI.Views. At this time there are no plans to add support for other APIs. 56 | 57 | ## Options 58 | 59 | Options can be set on view creation using `draggableConfig` or after creation using `DraggableView.draggable.setConfig( ... )` 60 | 61 | *** 62 | 63 | The `setConfig` method can set options two different ways. You can pass an `object` containing the parameters you with to set or you can pass a key-value pair. 64 | 65 | **Setting Options With An Object** 66 | ```javascript 67 | DraggableView.draggable.setConfig('enabled', false); 68 | ``` 69 | 70 | **Setting Options With An Object** 71 | ```javascript 72 | DraggableView.draggable.setConfig({ 73 | enabled : false 74 | }); 75 | ``` 76 | 77 | *** 78 | 79 | ### `Boolean` - enabled 80 | Flag to enable or disable dragging. 81 | 82 | ### `Number` - minLeft 83 | The left-most boundary of the view being dragged. Can be set to `null` to disable property. 84 | 85 | ### `Number` - maxLeft 86 | The right-most boundary of the view being dragged. Can be set to `null` to disable property. 87 | 88 | ### `Number` - minTop 89 | The top-most boundary of the view being dragged. Can be set to `null` to disable property. 90 | 91 | ### `Number` - maxTop 92 | The bottom-most boundary of the view being dragged. Can be set to `null` to disable property. 93 | 94 | ### `Boolean` - ensureRight 95 | Ensure that that the `right` edge of the view being dragged keeps its integrity. Can be set to `null` to disable property. 96 | 97 | ### `Boolean` - ensureBottom 98 | Ensure that that the `bottom` edge of the view being dragged keeps its integrity. Can be set to `null` to disable property. 99 | 100 | ### `Array` - maps 101 | An array of views that should be translated along with the view being dragged. See [View Mapping](#view-mapping). 102 | 103 | ## View Mapping 104 | 105 | In the case where you want multiple views to be translated at the same time you can pass the `maps` property to the draggable config. This functionality is useful for creating parallax or 1:1 movements. 106 | 107 | The `maps` property accepts an array of objects containing any of the following. The `view` property is required. 108 | 109 | ### Map Options 110 | 111 | ### `Ti.UI.View` - view 112 | The view to translate. 113 | 114 | ### `Number` - parallaxAmount 115 | A positive or negative number. Numbers less than `|1|` such as `0.1`, `0.2`, or `0.3` will cause the translation to move *faster* then the translation. A `parallaxAmount` of 1 will translate mapped views 1:1. A parallaxAmount `> 1` will result in a slower translation. 116 | 117 | ### `Object` - constrain 118 | An object containing the boundaries of the mapped view. Can have the following: 119 | 120 | * **x** 121 | * **start** The start position for the mapped view. 122 | * **end** The end position for the mapped view. 123 | * **callback** A function that will receive the completed percentage of the mapped translation. . Android does not support this option. 124 | * **fromCenter** Translate the view from its center. Android does not support this option. 125 | * **y** 126 | * **start** The start position for the mapped view. 127 | * **end** The end position for the mapped view. 128 | * **callback** A function that will receive the completed percentage of the mapped translation. . Android does not support this option. 129 | * **fromCenter** Translate the view from its center. Android does not support this option. 130 | 131 | ## Credits & Notes 132 | 133 | The work is largely based on [Pedro](http://twitter.com/pecdev) [Enrique's](https://github.com/pec1985) [TiDraggable](https://github.com/pec1985/TiDraggable) module license under the MIT (V2) license. -------------------------------------------------------------------------------- /ios/documentation/index.md: -------------------------------------------------------------------------------- 1 | # TiDraggable - Native Draggable Views 2 | 3 | An enhanced fork of the original [TiDraggable](https://github.com/pec1985/TiDraggable) module by [Pedro](http://twitter.com/pecdev) [Enrique](https://github.com/pec1985), allows for simple creation of "draggable" views. 4 | 5 | ## Enhancements & Fixes 6 | 7 | - Improved drag performance for iOS and Android. 8 | - Updated public APIs for more seamless integration. 9 | - Removed the InfiniteScroll class as it doesn't really have much to do with the overall module. 10 | - Removed unnecessary APIs to reduce overall module footprint. 11 | - Removed unused variables and organized imports. 12 | - Added ability to unset boundaries. 13 | - Mapped the missing `cancel` gesture to the `end` gesture (firing the respective event). 14 | - Added `ensureRight` and `ensureBottom`, this allows for stable dragging of views where the dimensions are not known. 15 | - Added `enabled` boolean property for toggeling drag 16 | - Views can be mapped and translated with a draggable view. 17 | - Draggable implementation now has its own configurable property called `draggable`. 18 | - iOS: Supports all Ti.UI.View subclasses and Ti.UI.View wrapped views (View, Window, Label) 19 | - Android: Fixed a bug where touch events were not correctly passed to children or bubbled to the parent. 20 | - Android: Fixed a bug where min and max bounds were being incorrectly reported after being set. 21 | - Android: Improved drag tracking. It plays nice with child views now. 22 | - Android: Added a touch threshold to ensure all child views have a chance to have their respective events fired. 23 | 24 | ## Usage 25 | 26 | ```javascript 27 | var Draggable = require('ti.draggable'), 28 | mainWindow = Ti.UI.createWindow({ 29 | backgroundColor : 'white' 30 | }), 31 | draggableView = Draggable.createView({ 32 | width : 100, 33 | height : 100, 34 | backgroundColor : 'black' 35 | }); 36 | 37 | mainWindow.add(draggableView); 38 | mainWindow.open(); 39 | ``` 40 | 41 | > If you are building the Android module, make sure you update the .classpath and build.properties files to match your setup. 42 | 43 | ## Module Reference 44 | 45 | ### Draggable.createView(viewOptions); 46 | 47 | Create a draggable view. All of Titanium's properties are supported along the additional `draggableConfig` property containing any options that should be set upon creation. See [Options](#options) 48 | 49 | > When the draggable proxy is created a new property is set called `draggable` which stores all the configuration properties and allows for options to be updated after creation. 50 | 51 | **iOS Notes** 52 | You can pass almost all of iOS' supported Ti.UI creation methods to the draggable module such as `Draggable.createView( ... )` or `Draggable.createWindow( ... )`. While `Ti.UI.View` and `Ti.UI.Window` are fully supported on iOS other APIs haven't been fully tested. 53 | 54 | **Android Notes** 55 | Android only supports the creation of Ti.UI.Views. At this time there are no plans to add support for other APIs. 56 | 57 | ## Options 58 | 59 | Options can be set on view creation using `draggableConfig` or after creation using `DraggableView.draggable.setConfig( ... )` 60 | 61 | *** 62 | 63 | The `setConfig` method can set options two different ways. You can pass an `object` containing the parameters you with to set or you can pass a key-value pair. 64 | 65 | **Setting Options With An Object** 66 | ```javascript 67 | DraggableView.draggable.setConfig('enabled', false); 68 | ``` 69 | 70 | **Setting Options With An Object** 71 | ```javascript 72 | DraggableView.draggable.setConfig({ 73 | enabled : false 74 | }); 75 | ``` 76 | 77 | *** 78 | 79 | ### `Boolean` - enabled 80 | Flag to enable or disable dragging. 81 | 82 | ### `Number` - minLeft 83 | The left-most boundary of the view being dragged. Can be set to `null` to disable property. 84 | 85 | ### `Number` - maxLeft 86 | The right-most boundary of the view being dragged. Can be set to `null` to disable property. 87 | 88 | ### `Number` - minTop 89 | The top-most boundary of the view being dragged. Can be set to `null` to disable property. 90 | 91 | ### `Number` - maxTop 92 | The bottom-most boundary of the view being dragged. Can be set to `null` to disable property. 93 | 94 | ### `Boolean` - ensureRight 95 | Ensure that that the `right` edge of the view being dragged keeps its integrity. Can be set to `null` to disable property. 96 | 97 | ### `Boolean` - ensureBottom 98 | Ensure that that the `bottom` edge of the view being dragged keeps its integrity. Can be set to `null` to disable property. 99 | 100 | ### `Array` - maps 101 | An array of views that should be translated along with the view being dragged. See [View Mapping](#view-mapping). 102 | 103 | ## View Mapping 104 | 105 | In the case where you want multiple views to be translated at the same time you can pass the `maps` property to the draggable config. This functionality is useful for creating parallax or 1:1 movements. 106 | 107 | The `maps` property accepts an array of objects containing any of the following. The `view` property is required. 108 | 109 | ### Map Options 110 | 111 | ### `Ti.UI.View` - view 112 | The view to translate. 113 | 114 | ### `Number` - parallaxAmount 115 | A positive or negative number. Numbers less than `|1|` such as `0.1`, `0.2`, or `0.3` will cause the translation to move *faster* then the translation. A `parallaxAmount` of 1 will translate mapped views 1:1. A parallaxAmount `> 1` will result in a slower translation. 116 | 117 | ### `Object` - constrain 118 | An object containing the boundaries of the mapped view. Can have the following: 119 | 120 | * **x** 121 | * **start** The start position for the mapped view. 122 | * **end** The end position for the mapped view. 123 | * **callback** A function that will receive the completed percentage of the mapped translation. . Android does not support this option. 124 | * **fromCenter** Translate the view from its center. Android does not support this option. 125 | * **y** 126 | * **start** The start position for the mapped view. 127 | * **end** The end position for the mapped view. 128 | * **callback** A function that will receive the completed percentage of the mapped translation. . Android does not support this option. 129 | * **fromCenter** Translate the view from its center. Android does not support this option. 130 | 131 | ## Credits & Notes 132 | 133 | The work is largely based on [Pedro](http://twitter.com/pecdev) [Enrique's](https://github.com/pec1985) [TiDraggable](https://github.com/pec1985/TiDraggable) module license under the MIT (V2) license. -------------------------------------------------------------------------------- /android/documentation/index.md: -------------------------------------------------------------------------------- 1 | # TiDraggable - Native Draggable Views 2 | 3 | An enhanced fork of the original [TiDraggable](https://github.com/pec1985/TiDraggable) module by [Pedro](http://twitter.com/pecdev) [Enrique](https://github.com/pec1985), allows for simple creation of "draggable" views. 4 | 5 | ## Enhancements & Fixes 6 | 7 | - Improved drag performance for iOS and Android. 8 | - Updated public APIs for more seamless integration. 9 | - Removed the InfiniteScroll class as it doesn't really have much to do with the overall module. 10 | - Removed unnecessary APIs to reduce overall module footprint. 11 | - Removed unused variables and organized imports. 12 | - Added ability to unset boundaries. 13 | - Mapped the missing `cancel` gesture to the `end` gesture (firing the respective event). 14 | - Added `ensureRight` and `ensureBottom`, this allows for stable dragging of views where the dimensions are not known. 15 | - Added `enabled` boolean property for toggeling drag 16 | - Views can be mapped and translated with a draggable view. 17 | - Draggable implementation now has its own configurable property called `draggable`. 18 | - iOS: Supports all Ti.UI.View subclasses and Ti.UI.View wrapped views (View, Window, Label) 19 | - Android: Fixed a bug where touch events were not correctly passed to children or bubbled to the parent. 20 | - Android: Fixed a bug where min and max bounds were being incorrectly reported after being set. 21 | - Android: Improved drag tracking. It plays nice with child views now. 22 | - Android: Added a touch threshold to ensure all child views have a chance to have their respective events fired. 23 | 24 | ## Usage 25 | 26 | ```javascript 27 | var Draggable = require('ti.draggable'), 28 | mainWindow = Ti.UI.createWindow({ 29 | backgroundColor : 'white' 30 | }), 31 | draggableView = Draggable.createView({ 32 | width : 100, 33 | height : 100, 34 | backgroundColor : 'black' 35 | }); 36 | 37 | mainWindow.add(draggableView); 38 | mainWindow.open(); 39 | ``` 40 | 41 | > If you are building the Android module, make sure you update the .classpath and build.properties files to match your setup. 42 | 43 | ## Module Reference 44 | 45 | ### Draggable.createView(viewOptions); 46 | 47 | Create a draggable view. All of Titanium's properties are supported along the additional `draggableConfig` property containing any options that should be set upon creation. See [Options](#options) 48 | 49 | > When the draggable proxy is created a new property is set called `draggable` which stores all the configuration properties and allows for options to be updated after creation. 50 | 51 | **iOS Notes** 52 | You can pass almost all of iOS' supported Ti.UI creation methods to the draggable module such as `Draggable.createView( ... )` or `Draggable.createWindow( ... )`. While `Ti.UI.View` and `Ti.UI.Window` are fully supported on iOS other APIs haven't been fully tested. 53 | 54 | **Android Notes** 55 | Android only supports the creation of Ti.UI.Views. At this time there are no plans to add support for other APIs. 56 | 57 | ## Options 58 | 59 | Options can be set on view creation using `draggableConfig` or after creation using `DraggableView.draggable.setConfig( ... )` 60 | 61 | *** 62 | 63 | The `setConfig` method can set options two different ways. You can pass an `object` containing the parameters you with to set or you can pass a key-value pair. 64 | 65 | **Setting Options With An Object** 66 | ```javascript 67 | DraggableView.draggable.setConfig('enabled', false); 68 | ``` 69 | 70 | **Setting Options With An Object** 71 | ```javascript 72 | DraggableView.draggable.setConfig({ 73 | enabled : false 74 | }); 75 | ``` 76 | 77 | *** 78 | 79 | ### `Boolean` - enabled 80 | Flag to enable or disable dragging. 81 | 82 | ### `Number` - minLeft 83 | The left-most boundary of the view being dragged. Can be set to `null` to disable property. 84 | 85 | ### `Number` - maxLeft 86 | The right-most boundary of the view being dragged. Can be set to `null` to disable property. 87 | 88 | ### `Number` - minTop 89 | The top-most boundary of the view being dragged. Can be set to `null` to disable property. 90 | 91 | ### `Number` - maxTop 92 | The bottom-most boundary of the view being dragged. Can be set to `null` to disable property. 93 | 94 | ### `Boolean` - ensureRight 95 | Ensure that that the `right` edge of the view being dragged keeps its integrity. Can be set to `null` to disable property. 96 | 97 | ### `Boolean` - ensureBottom 98 | Ensure that that the `bottom` edge of the view being dragged keeps its integrity. Can be set to `null` to disable property. 99 | 100 | ### `Array` - maps 101 | An array of views that should be translated along with the view being dragged. See [View Mapping](#view-mapping). 102 | 103 | ## View Mapping 104 | 105 | In the case where you want multiple views to be translated at the same time you can pass the `maps` property to the draggable config. This functionality is useful for creating parallax or 1:1 movements. 106 | 107 | The `maps` property accepts an array of objects containing any of the following. The `view` property is required. 108 | 109 | ### Map Options 110 | 111 | ### `Ti.UI.View` - view 112 | The view to translate. 113 | 114 | ### `Number` - parallaxAmount 115 | A positive or negative number. Numbers less than `|1|` such as `0.1`, `0.2`, or `0.3` will cause the translation to move *faster* then the translation. A `parallaxAmount` of 1 will translate mapped views 1:1. A parallaxAmount `> 1` will result in a slower translation. 116 | 117 | ### `Object` - constrain 118 | An object containing the boundaries of the mapped view. Can have the following: 119 | 120 | * **x** 121 | * **start** The start position for the mapped view. 122 | * **end** The end position for the mapped view. 123 | * **callback** A function that will receive the completed percentage of the mapped translation. . Android does not support this option. 124 | * **fromCenter** Translate the view from its center. Android does not support this option. 125 | * **y** 126 | * **start** The start position for the mapped view. 127 | * **end** The end position for the mapped view. 128 | * **callback** A function that will receive the completed percentage of the mapped translation. . Android does not support this option. 129 | * **fromCenter** Translate the view from its center. Android does not support this option. 130 | 131 | ## Credits & Notes 132 | 133 | The work is largely based on [Pedro](http://twitter.com/pecdev) [Enrique's](https://github.com/pec1985) [TiDraggable](https://github.com/pec1985/TiDraggable) module license under the MIT (V2) license. -------------------------------------------------------------------------------- /ios/build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Appcelerator Titanium Module Packager 4 | # 5 | # 6 | import os, subprocess, sys, glob, string 7 | import zipfile 8 | from datetime import date 9 | 10 | cwd = os.path.abspath(os.path.dirname(sys._getframe(0).f_code.co_filename)) 11 | os.chdir(cwd) 12 | required_module_keys = ['name','version','moduleid','description','copyright','license','copyright','platform','minsdk'] 13 | module_defaults = { 14 | 'description':'My module', 15 | 'author': 'Your Name', 16 | 'license' : 'Specify your license', 17 | 'copyright' : 'Copyright (c) %s by Your Company' % str(date.today().year), 18 | } 19 | module_license_default = "TODO: place your license here and we'll include it in the module distribution" 20 | 21 | def find_sdk(config): 22 | sdk = config['TITANIUM_SDK'] 23 | return os.path.expandvars(os.path.expanduser(sdk)) 24 | 25 | def replace_vars(config,token): 26 | idx = token.find('$(') 27 | while idx != -1: 28 | idx2 = token.find(')',idx+2) 29 | if idx2 == -1: break 30 | key = token[idx+2:idx2] 31 | if not config.has_key(key): break 32 | token = token.replace('$(%s)' % key, config[key]) 33 | idx = token.find('$(') 34 | return token 35 | 36 | 37 | def read_ti_xcconfig(): 38 | contents = open(os.path.join(cwd,'titanium.xcconfig')).read() 39 | config = {} 40 | for line in contents.splitlines(False): 41 | line = line.strip() 42 | if line[0:2]=='//': continue 43 | idx = line.find('=') 44 | if idx > 0: 45 | key = line[0:idx].strip() 46 | value = line[idx+1:].strip() 47 | config[key] = replace_vars(config,value) 48 | return config 49 | 50 | def generate_doc(config): 51 | docdir = os.path.join(cwd,'documentation') 52 | if not os.path.exists(docdir): 53 | print "Couldn't find documentation file at: %s" % docdir 54 | return None 55 | 56 | try: 57 | import markdown2 as markdown 58 | except ImportError: 59 | import markdown 60 | documentation = [] 61 | for file in os.listdir(docdir): 62 | if file in ignoreFiles or os.path.isdir(os.path.join(docdir, file)): 63 | continue 64 | md = open(os.path.join(docdir,file)).read() 65 | html = markdown.markdown(md) 66 | documentation.append({file:html}); 67 | return documentation 68 | 69 | def compile_js(manifest,config): 70 | js_file = os.path.join(cwd,'assets','ti.draggable.js') 71 | if not os.path.exists(js_file): return 72 | 73 | from compiler import Compiler 74 | try: 75 | import json 76 | except: 77 | import simplejson as json 78 | 79 | compiler = Compiler(cwd, manifest['moduleid'], manifest['name'], 'commonjs') 80 | root_asset, module_assets = compiler.compile_module() 81 | 82 | root_asset_content = """ 83 | %s 84 | 85 | return filterDataInRange([NSData dataWithBytesNoCopy:data length:sizeof(data) freeWhenDone:NO], ranges[0]); 86 | """ % root_asset 87 | 88 | module_asset_content = """ 89 | %s 90 | 91 | NSNumber *index = [map objectForKey:path]; 92 | if (index == nil) { 93 | return nil; 94 | } 95 | return filterDataInRange([NSData dataWithBytesNoCopy:data length:sizeof(data) freeWhenDone:NO], ranges[index.integerValue]); 96 | """ % module_assets 97 | 98 | from tools import splice_code 99 | 100 | assets_router = os.path.join(cwd,'Classes','TiDraggableModuleAssets.m') 101 | splice_code(assets_router, 'asset', root_asset_content) 102 | splice_code(assets_router, 'resolve_asset', module_asset_content) 103 | 104 | # Generate the exports after crawling all of the available JS source 105 | exports = open('metadata.json','w') 106 | json.dump({'exports':compiler.exports }, exports) 107 | exports.close() 108 | 109 | def die(msg): 110 | print msg 111 | sys.exit(1) 112 | 113 | def warn(msg): 114 | print "[WARN] %s" % msg 115 | 116 | def validate_license(): 117 | c = open(os.path.join(cwd,'LICENSE')).read() 118 | if c.find(module_license_default)!=-1: 119 | warn('please update the LICENSE file with your license text before distributing') 120 | 121 | def validate_manifest(): 122 | path = os.path.join(cwd,'manifest') 123 | f = open(path) 124 | if not os.path.exists(path): die("missing %s" % path) 125 | manifest = {} 126 | for line in f.readlines(): 127 | line = line.strip() 128 | if line[0:1]=='#': continue 129 | if line.find(':') < 0: continue 130 | key,value = line.split(':') 131 | manifest[key.strip()]=value.strip() 132 | for key in required_module_keys: 133 | if not manifest.has_key(key): die("missing required manifest key '%s'" % key) 134 | if module_defaults.has_key(key): 135 | defvalue = module_defaults[key] 136 | curvalue = manifest[key] 137 | if curvalue==defvalue: warn("please update the manifest key: '%s' to a non-default value" % key) 138 | return manifest,path 139 | 140 | ignoreFiles = ['.DS_Store','.gitignore','libTitanium.a','titanium.jar','README'] 141 | ignoreDirs = ['.DS_Store','.svn','.git','CVSROOT'] 142 | 143 | def zip_dir(zf,dir,basepath,ignoreExt=[]): 144 | if not os.path.exists(dir): return 145 | for root, dirs, files in os.walk(dir): 146 | for name in ignoreDirs: 147 | if name in dirs: 148 | dirs.remove(name) # don't visit ignored directories 149 | for file in files: 150 | if file in ignoreFiles: continue 151 | e = os.path.splitext(file) 152 | if len(e) == 2 and e[1] in ignoreExt: continue 153 | from_ = os.path.join(root, file) 154 | to_ = from_.replace(dir, '%s/%s'%(basepath,dir), 1) 155 | zf.write(from_, to_) 156 | 157 | def glob_libfiles(): 158 | files = [] 159 | for libfile in glob.glob('build/**/*.a'): 160 | if libfile.find('Release-')!=-1: 161 | files.append(libfile) 162 | return files 163 | 164 | def build_module(manifest,config): 165 | from tools import ensure_dev_path 166 | ensure_dev_path() 167 | 168 | rc = os.system("xcodebuild -sdk iphoneos -configuration Release") 169 | if rc != 0: 170 | die("xcodebuild failed") 171 | rc = os.system("xcodebuild -sdk iphonesimulator -configuration Release") 172 | if rc != 0: 173 | die("xcodebuild failed") 174 | # build the merged library using lipo 175 | moduleid = manifest['moduleid'] 176 | libpaths = '' 177 | for libfile in glob_libfiles(): 178 | libpaths+='%s ' % libfile 179 | 180 | os.system("lipo %s -create -output build/lib%s.a" %(libpaths,moduleid)) 181 | 182 | def package_module(manifest,mf,config): 183 | name = manifest['name'].lower() 184 | moduleid = manifest['moduleid'].lower() 185 | version = manifest['version'] 186 | modulezip = '%s-iphone-%s.zip' % (moduleid,version) 187 | if os.path.exists(modulezip): os.remove(modulezip) 188 | zf = zipfile.ZipFile(modulezip, 'w', zipfile.ZIP_DEFLATED) 189 | modulepath = 'modules/iphone/%s/%s' % (moduleid,version) 190 | zf.write(mf,'%s/manifest' % modulepath) 191 | libname = 'lib%s.a' % moduleid 192 | zf.write('build/%s' % libname, '%s/%s' % (modulepath,libname)) 193 | docs = generate_doc(config) 194 | if docs!=None: 195 | for doc in docs: 196 | for file, html in doc.iteritems(): 197 | filename = string.replace(file,'.md','.html') 198 | zf.writestr('%s/documentation/%s'%(modulepath,filename),html) 199 | zip_dir(zf,'assets',modulepath,['.pyc','.js']) 200 | zip_dir(zf,'example',modulepath,['.pyc']) 201 | zip_dir(zf,'platform',modulepath,['.pyc','.js']) 202 | zf.write('LICENSE','%s/LICENSE' % modulepath) 203 | zf.write('module.xcconfig','%s/module.xcconfig' % modulepath) 204 | exports_file = 'metadata.json' 205 | if os.path.exists(exports_file): 206 | zf.write(exports_file, '%s/%s' % (modulepath, exports_file)) 207 | zf.close() 208 | 209 | 210 | if __name__ == '__main__': 211 | manifest,mf = validate_manifest() 212 | validate_license() 213 | config = read_ti_xcconfig() 214 | 215 | sdk = find_sdk(config) 216 | sys.path.insert(0,os.path.join(sdk,'iphone')) 217 | sys.path.append(os.path.join(sdk, "common")) 218 | 219 | compile_js(manifest,config) 220 | build_module(manifest,config) 221 | package_module(manifest,mf,config) 222 | sys.exit(0) 223 | 224 | -------------------------------------------------------------------------------- /android/src/ti/draggable/DraggableGesture.java: -------------------------------------------------------------------------------- 1 | /** 2 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 3 | * allows for simple creation of "draggable" views. 4 | * 5 | * Copyright (C) 2013 Seth Benjamin 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | * -- Original License -- 21 | * 22 | * Copyright 2012 Pedro Enrique 23 | * 24 | * Licensed under the Apache License, Version 2.0 (the "License"); 25 | * you may not use this file except in compliance with the License. 26 | * You may obtain a copy of the License at 27 | * 28 | * http://www.apache.org/licenses/LICENSE-2.0 29 | * 30 | * Unless required by applicable law or agreed to in writing, software 31 | * distributed under the License is distributed on an "AS IS" BASIS, 32 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | * See the License for the specific language governing permissions and 34 | * limitations under the License. 35 | */ 36 | package ti.draggable; 37 | 38 | import java.lang.ref.WeakReference; 39 | import java.util.HashMap; 40 | 41 | import org.appcelerator.kroll.KrollDict; 42 | import org.appcelerator.kroll.KrollProxy; 43 | import org.appcelerator.titanium.TiDimension; 44 | import org.appcelerator.titanium.proxy.TiViewProxy; 45 | import org.appcelerator.titanium.util.TiConvert; 46 | import org.appcelerator.titanium.view.TiCompositeLayout; 47 | import org.appcelerator.titanium.view.TiUIView; 48 | 49 | import android.view.MotionEvent; 50 | import android.view.VelocityTracker; 51 | import android.view.View; 52 | import android.view.View.OnTouchListener; 53 | import android.view.ViewConfiguration; 54 | 55 | public class DraggableGesture implements OnTouchListener 56 | { 57 | protected TiUIView draggableView; 58 | protected TiViewProxy draggableProxy; 59 | protected WeakReference config; 60 | protected VelocityTracker velocityTracker; 61 | protected ViewConfiguration vc; 62 | protected double lastX = 0; 63 | protected double lastY = 0; 64 | protected double distanceX = 0; 65 | protected double distanceY = 0; 66 | protected double startLeft = 0; 67 | protected double startTop = 0; 68 | protected double lastLeft; 69 | protected double lastTop; 70 | public boolean isBeingDragged = false; 71 | 72 | public DraggableGesture(TiViewProxy proxy, TiUIView view, WeakReference config) 73 | { 74 | this.draggableProxy = proxy; 75 | this.draggableView = view; 76 | this.vc = ViewConfiguration.get(this.draggableView.getOuterView().getContext()); 77 | this.config = config; 78 | 79 | ConfigProxy config_ = this.getConfig(); 80 | 81 | if(config_ != null) { 82 | config_.threshold = vc.getScaledPagingTouchSlop(); 83 | } 84 | 85 | this.prepareMappedProxies(); 86 | } 87 | 88 | public void determineDrag(MotionEvent event) 89 | { 90 | ConfigProxy config = this.getConfig(); 91 | KrollDict configProps = config.getProperties(); 92 | 93 | Object thresholdObj = config.getProperty("threshold"); 94 | int threshold = thresholdObj != null ? TiConvert.toInt(thresholdObj) : vc.getScaledPagingTouchSlop(); 95 | 96 | boolean xAxis = ! configProps.isNull("axis") && configProps.getString("axis").equals("x"); 97 | boolean yAxis = ! configProps.isNull("axis") && configProps.getString("axis").equals("y"); 98 | 99 | double xDelta = Math.abs(event.getRawX() - this.lastX); 100 | double yDelta = Math.abs(event.getRawY() - this.lastY); 101 | 102 | if(xAxis) { 103 | this.isBeingDragged = xDelta > threshold; 104 | } 105 | else if(yAxis) 106 | { 107 | this.isBeingDragged = yDelta > threshold; 108 | } 109 | else 110 | { 111 | this.isBeingDragged = xDelta > threshold || yDelta > threshold; 112 | } 113 | } 114 | 115 | @Override 116 | public boolean onTouch(View view, MotionEvent event) { 117 | ConfigProxy config = this.getConfig(); 118 | 119 | if (! this.isBeingDragged && event.getAction() == MotionEvent.ACTION_MOVE) 120 | { 121 | this.determineDrag(event); 122 | } 123 | 124 | if (! this.isBeingDragged) { 125 | return false; 126 | } 127 | 128 | if (this.velocityTracker != null) 129 | { 130 | this.velocityTracker.addMovement(event); 131 | } 132 | 133 | switch(event.getAction()) 134 | { 135 | case MotionEvent.ACTION_DOWN: 136 | this.startDrag(event); 137 | break; 138 | case MotionEvent.ACTION_UP: 139 | case MotionEvent.ACTION_CANCEL: 140 | this.stopDrag(event); 141 | break; 142 | case MotionEvent.ACTION_MOVE: 143 | if (TiConvert.toBoolean(config.getProperty("enabled")) == false) 144 | { 145 | return false; 146 | } 147 | 148 | this.drag(event); 149 | break; 150 | } 151 | 152 | return true; 153 | } 154 | 155 | public void drag(MotionEvent event) 156 | { 157 | ConfigProxy config = this.getConfig(); 158 | KrollDict configProps = config.getProperties(); 159 | View viewToDrag = this.draggableView.getOuterView(); 160 | 161 | float screenX = event.getRawX(); 162 | float screenY = event.getRawY(); 163 | 164 | if (this.isBeingDragged) 165 | { 166 | boolean xAxis = ! configProps.isNull("axis") && configProps.getString("axis").equals("x"); 167 | boolean yAxis = ! configProps.isNull("axis") && configProps.getString("axis").equals("y"); 168 | boolean noAxis = ! xAxis && ! yAxis; 169 | 170 | double leftEdge = screenX + startLeft; 171 | double topEdge = screenY + startTop; 172 | 173 | if (xAxis) 174 | { 175 | topEdge = viewToDrag.getTop(); 176 | } 177 | else if (yAxis) 178 | { 179 | leftEdge = viewToDrag.getLeft(); 180 | } 181 | 182 | if (noAxis || xAxis) 183 | { 184 | if (! configProps.isNull("minLeft")) 185 | { 186 | double _minLeft = config.getDimensionAsPixels("minLeft"); 187 | 188 | if (leftEdge <= _minLeft) 189 | { 190 | leftEdge = _minLeft; 191 | } 192 | } 193 | 194 | if (! configProps.isNull("maxLeft")) 195 | { 196 | double _maxLeft = config.getDimensionAsPixels("maxLeft"); 197 | 198 | if (leftEdge >= _maxLeft) 199 | { 200 | leftEdge = _maxLeft; 201 | } 202 | } 203 | } 204 | 205 | if (noAxis || yAxis) 206 | { 207 | if (! configProps.isNull("minTop")) 208 | { 209 | double _minTop = config.getDimensionAsPixels("minTop"); 210 | 211 | if (topEdge <= _minTop) 212 | { 213 | topEdge = _minTop; 214 | } 215 | } 216 | 217 | if (! configProps.isNull("maxTop")) 218 | { 219 | double _maxTop = config.getDimensionAsPixels("maxTop"); 220 | 221 | if (topEdge >= _maxTop) 222 | { 223 | topEdge = _maxTop; 224 | } 225 | } 226 | } 227 | 228 | double translationLeft = lastLeft - leftEdge; 229 | double translationTop = lastTop - topEdge; 230 | 231 | translateMappedProxies(translationLeft, translationTop); 232 | 233 | Float ensureRightValue = null; 234 | Float ensureBottomValue = null; 235 | 236 | if (TiConvert.toBoolean(configProps, "ensureRight")) 237 | { 238 | ensureRightValue = (float) -leftEdge; 239 | } 240 | 241 | if (TiConvert.toBoolean(configProps, "ensureBottom")) 242 | { 243 | ensureBottomValue = (float) -topEdge; 244 | } 245 | 246 | this.setViewPosition(draggableProxy, viewToDrag, (float) topEdge, (float) leftEdge, ensureBottomValue, ensureRightValue); 247 | 248 | distanceX += (lastLeft - leftEdge); 249 | distanceY += (lastTop - topEdge); 250 | lastLeft = leftEdge; 251 | lastTop = topEdge; 252 | 253 | if (draggableProxy.hasListeners("move")) 254 | { 255 | this.velocityTracker.computeCurrentVelocity(1000); 256 | 257 | KrollDict eventDict = new KrollDict(); 258 | KrollDict velocityDict = new KrollDict(); 259 | 260 | velocityDict.put("x", this.velocityTracker.getXVelocity()); 261 | velocityDict.put("y", this.velocityTracker.getYVelocity()); 262 | eventDict.put("left", viewToDrag.getLeft()); 263 | eventDict.put("top", viewToDrag.getTop()); 264 | eventDict.put("velocity", velocityDict); 265 | 266 | draggableProxy.fireEvent("move", eventDict); 267 | } 268 | } 269 | } 270 | 271 | public void startDrag(MotionEvent event) 272 | { 273 | View viewToDrag = this.draggableView.getOuterView(); 274 | 275 | this.lastX = event.getRawX(); 276 | this.lastY = event.getRawY(); 277 | 278 | float screenX = event.getRawX(); 279 | float screenY = event.getRawY(); 280 | 281 | this.startLeft = viewToDrag.getLeft() - screenX; 282 | this.startTop = viewToDrag.getTop() - screenY; 283 | this.lastLeft = viewToDrag.getLeft(); 284 | this.lastTop = viewToDrag.getTop(); 285 | this.distanceX = this.distanceY = 0; 286 | 287 | if (this.velocityTracker == null) 288 | { 289 | this.velocityTracker = VelocityTracker.obtain(); 290 | } 291 | 292 | if (draggableProxy.hasListeners("start")) 293 | { 294 | this.velocityTracker.computeCurrentVelocity(1000); 295 | 296 | KrollDict velocityDict = new KrollDict(); 297 | 298 | velocityDict.put("x", this.velocityTracker.getXVelocity()); 299 | velocityDict.put("y", this.velocityTracker.getYVelocity()); 300 | 301 | KrollDict eventDict = new KrollDict(); 302 | 303 | eventDict.put("left", viewToDrag.getLeft()); 304 | eventDict.put("top", viewToDrag.getTop()); 305 | eventDict.put("velocity", velocityDict); 306 | 307 | draggableProxy.fireEvent("start", eventDict); 308 | } 309 | } 310 | 311 | public void stopDrag(MotionEvent event) 312 | { 313 | if (this.isBeingDragged) 314 | { 315 | this.isBeingDragged = false; 316 | 317 | this.finalizeMappedTranslations(); 318 | 319 | if (draggableProxy.hasListeners("end") || draggableProxy.hasListeners("cancel")) 320 | { 321 | View viewToDrag = this.draggableView.getOuterView(); 322 | 323 | this.velocityTracker.computeCurrentVelocity(1000); 324 | 325 | KrollDict distanceDict = new KrollDict(); 326 | 327 | distanceDict.put("x", distanceX); 328 | distanceDict.put("y", distanceY); 329 | 330 | KrollDict velocityDict = new KrollDict(); 331 | 332 | velocityDict.put("x", this.velocityTracker.getXVelocity()); 333 | velocityDict.put("y", this.velocityTracker.getYVelocity()); 334 | 335 | KrollDict eventDict = new KrollDict(); 336 | 337 | eventDict.put("left", viewToDrag.getLeft()); 338 | eventDict.put("top", viewToDrag.getTop()); 339 | eventDict.put("velocity", velocityDict); 340 | eventDict.put("distance", distanceDict); 341 | 342 | draggableProxy.fireEvent(event.getAction() == MotionEvent.ACTION_UP ? "end" : "cancel", eventDict); 343 | } 344 | 345 | this.velocityTracker.clear(); 346 | } 347 | } 348 | 349 | protected void prepareMappedProxies() 350 | { 351 | KrollDict configProps = this.getConfig().getProperties(); 352 | 353 | if (configProps.containsKeyAndNotNull("maps")) 354 | { 355 | for (Object mapObject : (Object[]) configProps.get("maps")) 356 | { 357 | @SuppressWarnings({ "rawtypes", "unchecked" }) 358 | KrollDict map = new KrollDict((HashMap) mapObject); 359 | TiViewProxy mappedProxy = (TiViewProxy) map.get("view"); 360 | View mappedView = mappedProxy.peekView().getOuterView(); 361 | 362 | if (map.containsKeyAndNotNull("constrain")) 363 | { 364 | View parentView = this.getConfig().getDecorView(); 365 | KrollDict constraints = map.getKrollDict("constrain"); 366 | KrollDict constraintX = constraints.getKrollDict("x"); 367 | KrollDict constraintY = constraints.getKrollDict("y"); 368 | 369 | boolean didModifyPosition = false; 370 | 371 | double parallaxAmount = map.containsKeyAndNotNull("parallaxAmount") ? TiConvert.toDouble(map, "parallaxAmount") : 1; 372 | double newLeft = 0; 373 | double newTop = 0; 374 | 375 | if (constraintX != null && constraintX.containsKeyAndNotNull("start")) 376 | { 377 | double xStart = TiConvert.toTiDimension(constraintX, "start", TiDimension.TYPE_LEFT).getAsPixels(parentView); 378 | 379 | if (constraintX.containsKeyAndNotNull("end")) 380 | { 381 | double xEnd = TiConvert.toTiDimension(constraintX, "end", TiDimension.TYPE_LEFT).getAsPixels(parentView); 382 | 383 | newLeft = xStart / parallaxAmount - xEnd; 384 | } 385 | else 386 | { 387 | newLeft = mappedView.getLeft() / parallaxAmount; 388 | } 389 | 390 | didModifyPosition = true; 391 | } 392 | 393 | if (constraintY != null && constraintY.containsKeyAndNotNull("start")) 394 | { 395 | double yStart = TiConvert.toTiDimension(constraintX, "start", TiDimension.TYPE_TOP).getAsPixels(parentView); 396 | 397 | if (constraintY.containsKeyAndNotNull("end")) 398 | { 399 | double yEnd = TiConvert.toTiDimension(constraintX, "end", TiDimension.TYPE_TOP).getAsPixels(parentView); 400 | 401 | newTop = yStart / parallaxAmount - yEnd; 402 | } 403 | else 404 | { 405 | newTop = mappedView.getTop() / parallaxAmount; 406 | } 407 | 408 | didModifyPosition = true; 409 | } 410 | 411 | if (didModifyPosition) 412 | { 413 | this.setViewPosition(mappedProxy, mappedView, (float) newTop, (float) newLeft, null, null); 414 | } 415 | } 416 | } 417 | } 418 | } 419 | 420 | protected void translateMappedProxies(double translationX, double translationY) 421 | { 422 | KrollDict configProps = this.getConfig().getProperties(); 423 | 424 | if (configProps.containsKeyAndNotNull("maps")) 425 | { 426 | for (Object mapObject : (Object[]) configProps.get("maps")) 427 | { 428 | @SuppressWarnings({ "rawtypes", "unchecked" }) 429 | KrollDict map = new KrollDict((HashMap) mapObject); 430 | TiViewProxy mappedProxy = (TiViewProxy) map.get("view"); 431 | View mappedView = mappedProxy.peekView().getOuterView(); 432 | double parallaxAmount = map.containsKeyAndNotNull("parallaxAmount") ? TiConvert.toDouble(map, "parallaxAmount") : 1; 433 | 434 | translationX = mappedView.getTranslationX() - translationX / parallaxAmount; 435 | translationY = mappedView.getTranslationY() - translationY / parallaxAmount; 436 | 437 | mappedView.setTranslationX((float) translationX); 438 | mappedView.setTranslationY((float) translationY); 439 | } 440 | } 441 | } 442 | 443 | protected void finalizeMappedTranslations() 444 | { 445 | KrollDict configProps = this.getConfig().getProperties(); 446 | 447 | if (configProps.containsKeyAndNotNull("maps")) 448 | { 449 | for (Object mapObject : (Object[]) configProps.get("maps")) 450 | { 451 | @SuppressWarnings({ "rawtypes", "unchecked" }) 452 | KrollDict map = new KrollDict((HashMap) mapObject); 453 | TiViewProxy mappedProxy = (TiViewProxy) map.get("view"); 454 | View mappedView = mappedProxy.peekView().getOuterView(); 455 | 456 | this.setViewPosition(mappedProxy, mappedView, mappedView.getY(), mappedView.getX(), null, null); 457 | } 458 | } 459 | } 460 | 461 | protected void setViewPosition(KrollProxy proxy, View view, Float top, Float left, Float bottom, Float right) 462 | { 463 | TiCompositeLayout.LayoutParams layout = (TiCompositeLayout.LayoutParams) view.getLayoutParams(); 464 | 465 | layout.optionLeft = new TiDimension(left, TiDimension.TYPE_LEFT); 466 | layout.optionTop = new TiDimension(top, TiDimension.TYPE_TOP); 467 | 468 | if (right != null) 469 | { 470 | layout.optionRight = new TiDimension(right, TiDimension.TYPE_RIGHT); 471 | } 472 | 473 | if (bottom != null) 474 | { 475 | layout.optionBottom = new TiDimension(bottom, TiDimension.TYPE_BOTTOM); 476 | } 477 | 478 | view.setLayoutParams(layout); 479 | view.setTranslationX(0); 480 | view.setTranslationY(0); 481 | 482 | proxy.setProperty("left", left); 483 | proxy.setProperty("top", top); 484 | 485 | if (right != null) 486 | { 487 | proxy.setProperty("right", right); 488 | } 489 | 490 | if (bottom != null) 491 | { 492 | proxy.setProperty("bottom", bottom); 493 | } 494 | } 495 | 496 | protected ConfigProxy getConfig() 497 | { 498 | return this.config.get(); 499 | } 500 | } 501 | -------------------------------------------------------------------------------- /ios/draggable.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 45; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | 24416B8111C4CA220047AFDD /* Build & Test */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = 24416B8A11C4CA520047AFDD /* Build configuration list for PBXAggregateTarget "Build & Test" */; 13 | buildPhases = ( 14 | 24416B8011C4CA220047AFDD /* ShellScript */, 15 | ); 16 | dependencies = ( 17 | 24416B8511C4CA280047AFDD /* PBXTargetDependency */, 18 | ); 19 | name = "Build & Test"; 20 | productName = "Build & test"; 21 | }; 22 | /* End PBXAggregateTarget section */ 23 | 24 | /* Begin PBXBuildFile section */ 25 | 24DD6CF91134B3F500162E58 /* TiDraggableModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 24DD6CF71134B3F500162E58 /* TiDraggableModule.h */; }; 26 | 24DD6CFA1134B3F500162E58 /* TiDraggableModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 24DD6CF81134B3F500162E58 /* TiDraggableModule.m */; }; 27 | 24DE9E1111C5FE74003F90F6 /* TiDraggableModuleAssets.h in Headers */ = {isa = PBXBuildFile; fileRef = 24DE9E0F11C5FE74003F90F6 /* TiDraggableModuleAssets.h */; }; 28 | 24DE9E1211C5FE74003F90F6 /* TiDraggableModuleAssets.m in Sources */ = {isa = PBXBuildFile; fileRef = 24DE9E1011C5FE74003F90F6 /* TiDraggableModuleAssets.m */; }; 29 | AA747D9F0F9514B9006C5449 /* TiDraggable_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* TiDraggable_Prefix.pch */; }; 30 | AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; }; 31 | ED58400F187F0C5400AA3A61 /* TiViewProxy+ViewProxyExtended.h in Headers */ = {isa = PBXBuildFile; fileRef = ED58400D187F0C5400AA3A61 /* TiViewProxy+ViewProxyExtended.h */; }; 32 | ED584010187F0C5400AA3A61 /* TiViewProxy+ViewProxyExtended.m in Sources */ = {isa = PBXBuildFile; fileRef = ED58400E187F0C5400AA3A61 /* TiViewProxy+ViewProxyExtended.m */; }; 33 | EDB9F4E31829500B009E1A17 /* TiDraggableGesture.h in Headers */ = {isa = PBXBuildFile; fileRef = EDB9F4E11829500B009E1A17 /* TiDraggableGesture.h */; }; 34 | EDB9F4E41829500B009E1A17 /* TiDraggableGesture.m in Sources */ = {isa = PBXBuildFile; fileRef = EDB9F4E21829500B009E1A17 /* TiDraggableGesture.m */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXContainerItemProxy section */ 38 | 24416B8411C4CA280047AFDD /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; 41 | proxyType = 1; 42 | remoteGlobalIDString = D2AAC07D0554694100DB518D; 43 | remoteInfo = draggable; 44 | }; 45 | /* End PBXContainerItemProxy section */ 46 | 47 | /* Begin PBXFileReference section */ 48 | 24DD6CF71134B3F500162E58 /* TiDraggableModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TiDraggableModule.h; path = Classes/TiDraggableModule.h; sourceTree = ""; }; 49 | 24DD6CF81134B3F500162E58 /* TiDraggableModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TiDraggableModule.m; path = Classes/TiDraggableModule.m; sourceTree = ""; }; 50 | 24DD6D1B1134B66800162E58 /* titanium.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = titanium.xcconfig; sourceTree = ""; }; 51 | 24DE9E0F11C5FE74003F90F6 /* TiDraggableModuleAssets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TiDraggableModuleAssets.h; path = Classes/TiDraggableModuleAssets.h; sourceTree = ""; }; 52 | 24DE9E1011C5FE74003F90F6 /* TiDraggableModuleAssets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TiDraggableModuleAssets.m; path = Classes/TiDraggableModuleAssets.m; sourceTree = ""; }; 53 | AA747D9E0F9514B9006C5449 /* TiDraggable_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiDraggable_Prefix.pch; sourceTree = SOURCE_ROOT; }; 54 | AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 55 | D2AAC07E0554694100DB518D /* libTiDraggable.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTiDraggable.a; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | ED58400D187F0C5400AA3A61 /* TiViewProxy+ViewProxyExtended.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "TiViewProxy+ViewProxyExtended.h"; path = "Classes/TiViewProxy+ViewProxyExtended.h"; sourceTree = ""; }; 57 | ED58400E187F0C5400AA3A61 /* TiViewProxy+ViewProxyExtended.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "TiViewProxy+ViewProxyExtended.m"; path = "Classes/TiViewProxy+ViewProxyExtended.m"; sourceTree = ""; }; 58 | EDB9F4E11829500B009E1A17 /* TiDraggableGesture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TiDraggableGesture.h; path = Classes/TiDraggableGesture.h; sourceTree = ""; }; 59 | EDB9F4E21829500B009E1A17 /* TiDraggableGesture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TiDraggableGesture.m; path = Classes/TiDraggableGesture.m; sourceTree = ""; }; 60 | /* End PBXFileReference section */ 61 | 62 | /* Begin PBXFrameworksBuildPhase section */ 63 | D2AAC07C0554694100DB518D /* Frameworks */ = { 64 | isa = PBXFrameworksBuildPhase; 65 | buildActionMask = 2147483647; 66 | files = ( 67 | AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 034768DFFF38A50411DB9C8B /* Products */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | D2AAC07E0554694100DB518D /* libTiDraggable.a */, 78 | ); 79 | name = Products; 80 | sourceTree = ""; 81 | }; 82 | 0867D691FE84028FC02AAC07 /* draggable */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 08FB77AEFE84172EC02AAC07 /* Classes */, 86 | 32C88DFF0371C24200C91783 /* Other Sources */, 87 | 0867D69AFE84028FC02AAC07 /* Frameworks */, 88 | 034768DFFF38A50411DB9C8B /* Products */, 89 | ); 90 | name = draggable; 91 | sourceTree = ""; 92 | }; 93 | 0867D69AFE84028FC02AAC07 /* Frameworks */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | AACBBE490F95108600F1A2B1 /* Foundation.framework */, 97 | ); 98 | name = Frameworks; 99 | sourceTree = ""; 100 | }; 101 | 08FB77AEFE84172EC02AAC07 /* Classes */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | ED58400D187F0C5400AA3A61 /* TiViewProxy+ViewProxyExtended.h */, 105 | ED58400E187F0C5400AA3A61 /* TiViewProxy+ViewProxyExtended.m */, 106 | EDB9F4E11829500B009E1A17 /* TiDraggableGesture.h */, 107 | EDB9F4E21829500B009E1A17 /* TiDraggableGesture.m */, 108 | 24DE9E0F11C5FE74003F90F6 /* TiDraggableModuleAssets.h */, 109 | 24DE9E1011C5FE74003F90F6 /* TiDraggableModuleAssets.m */, 110 | 24DD6CF71134B3F500162E58 /* TiDraggableModule.h */, 111 | 24DD6CF81134B3F500162E58 /* TiDraggableModule.m */, 112 | ); 113 | name = Classes; 114 | sourceTree = ""; 115 | }; 116 | 32C88DFF0371C24200C91783 /* Other Sources */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | AA747D9E0F9514B9006C5449 /* TiDraggable_Prefix.pch */, 120 | 24DD6D1B1134B66800162E58 /* titanium.xcconfig */, 121 | ); 122 | name = "Other Sources"; 123 | sourceTree = ""; 124 | }; 125 | /* End PBXGroup section */ 126 | 127 | /* Begin PBXHeadersBuildPhase section */ 128 | D2AAC07A0554694100DB518D /* Headers */ = { 129 | isa = PBXHeadersBuildPhase; 130 | buildActionMask = 2147483647; 131 | files = ( 132 | AA747D9F0F9514B9006C5449 /* TiDraggable_Prefix.pch in Headers */, 133 | ED58400F187F0C5400AA3A61 /* TiViewProxy+ViewProxyExtended.h in Headers */, 134 | 24DD6CF91134B3F500162E58 /* TiDraggableModule.h in Headers */, 135 | 24DE9E1111C5FE74003F90F6 /* TiDraggableModuleAssets.h in Headers */, 136 | EDB9F4E31829500B009E1A17 /* TiDraggableGesture.h in Headers */, 137 | ); 138 | runOnlyForDeploymentPostprocessing = 0; 139 | }; 140 | /* End PBXHeadersBuildPhase section */ 141 | 142 | /* Begin PBXNativeTarget section */ 143 | D2AAC07D0554694100DB518D /* draggable */ = { 144 | isa = PBXNativeTarget; 145 | buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "draggable" */; 146 | buildPhases = ( 147 | D2AAC07A0554694100DB518D /* Headers */, 148 | D2AAC07B0554694100DB518D /* Sources */, 149 | D2AAC07C0554694100DB518D /* Frameworks */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = draggable; 156 | productName = draggable; 157 | productReference = D2AAC07E0554694100DB518D /* libTiDraggable.a */; 158 | productType = "com.apple.product-type.library.static"; 159 | }; 160 | /* End PBXNativeTarget section */ 161 | 162 | /* Begin PBXProject section */ 163 | 0867D690FE84028FC02AAC07 /* Project object */ = { 164 | isa = PBXProject; 165 | attributes = { 166 | }; 167 | buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "draggable" */; 168 | compatibilityVersion = "Xcode 3.1"; 169 | developmentRegion = English; 170 | hasScannedForEncodings = 1; 171 | knownRegions = ( 172 | English, 173 | Japanese, 174 | French, 175 | German, 176 | ); 177 | mainGroup = 0867D691FE84028FC02AAC07 /* draggable */; 178 | productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; 179 | projectDirPath = ""; 180 | projectRoot = ""; 181 | targets = ( 182 | D2AAC07D0554694100DB518D /* draggable */, 183 | 24416B8111C4CA220047AFDD /* Build & Test */, 184 | ); 185 | }; 186 | /* End PBXProject section */ 187 | 188 | /* Begin PBXShellScriptBuildPhase section */ 189 | 24416B8011C4CA220047AFDD /* ShellScript */ = { 190 | isa = PBXShellScriptBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | ); 194 | inputPaths = ( 195 | ); 196 | outputPaths = ( 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | shellPath = /bin/sh; 200 | shellScript = "# shell script goes here\n\npython \"${TITANIUM_SDK}/titanium.py\" run --dir=\"${PROJECT_DIR}\"\nexit $?\n"; 201 | }; 202 | /* End PBXShellScriptBuildPhase section */ 203 | 204 | /* Begin PBXSourcesBuildPhase section */ 205 | D2AAC07B0554694100DB518D /* Sources */ = { 206 | isa = PBXSourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | EDB9F4E41829500B009E1A17 /* TiDraggableGesture.m in Sources */, 210 | ED584010187F0C5400AA3A61 /* TiViewProxy+ViewProxyExtended.m in Sources */, 211 | 24DD6CFA1134B3F500162E58 /* TiDraggableModule.m in Sources */, 212 | 24DE9E1211C5FE74003F90F6 /* TiDraggableModuleAssets.m in Sources */, 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | }; 216 | /* End PBXSourcesBuildPhase section */ 217 | 218 | /* Begin PBXTargetDependency section */ 219 | 24416B8511C4CA280047AFDD /* PBXTargetDependency */ = { 220 | isa = PBXTargetDependency; 221 | target = D2AAC07D0554694100DB518D /* draggable */; 222 | targetProxy = 24416B8411C4CA280047AFDD /* PBXContainerItemProxy */; 223 | }; 224 | /* End PBXTargetDependency section */ 225 | 226 | /* Begin XCBuildConfiguration section */ 227 | 1DEB921F08733DC00010E9CD /* Debug */ = { 228 | isa = XCBuildConfiguration; 229 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 230 | buildSettings = { 231 | CODE_SIGN_IDENTITY = "iPhone Developer"; 232 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 233 | DSTROOT = /tmp/TiDraggable.dst; 234 | GCC_C_LANGUAGE_STANDARD = c99; 235 | GCC_OPTIMIZATION_LEVEL = 0; 236 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 237 | GCC_PREFIX_HEADER = TiDraggable_Prefix.pch; 238 | GCC_PREPROCESSOR_DEFINITIONS = "TI_VERSION=$(TI_VERSION)"; 239 | GCC_TREAT_WARNINGS_AS_ERRORS = NO; 240 | GCC_VERSION = ""; 241 | GCC_WARN_ABOUT_RETURN_TYPE = NO; 242 | GCC_WARN_MISSING_PARENTHESES = NO; 243 | GCC_WARN_SHADOW = NO; 244 | GCC_WARN_STRICT_SELECTOR_MATCH = NO; 245 | GCC_WARN_UNUSED_FUNCTION = YES; 246 | GCC_WARN_UNUSED_PARAMETER = NO; 247 | GCC_WARN_UNUSED_VALUE = NO; 248 | GCC_WARN_UNUSED_VARIABLE = NO; 249 | INSTALL_PATH = /usr/local/lib; 250 | LIBRARY_SEARCH_PATHS = ""; 251 | OTHER_CFLAGS = ( 252 | "-DDEBUG", 253 | "-DTI_POST_1_2", 254 | ); 255 | OTHER_LDFLAGS = "-ObjC"; 256 | PRODUCT_NAME = TiDraggable; 257 | PROVISIONING_PROFILE = ""; 258 | "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; 259 | RUN_CLANG_STATIC_ANALYZER = NO; 260 | SDKROOT = iphoneos; 261 | USER_HEADER_SEARCH_PATHS = ""; 262 | }; 263 | name = Debug; 264 | }; 265 | 1DEB922008733DC00010E9CD /* Release */ = { 266 | isa = XCBuildConfiguration; 267 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 268 | buildSettings = { 269 | ALWAYS_SEARCH_USER_PATHS = NO; 270 | DSTROOT = /tmp/TiDraggable.dst; 271 | GCC_C_LANGUAGE_STANDARD = c99; 272 | GCC_MODEL_TUNING = G5; 273 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 274 | GCC_PREFIX_HEADER = TiDraggable_Prefix.pch; 275 | GCC_PREPROCESSOR_DEFINITIONS = "TI_VERSION=$(TI_VERSION)"; 276 | GCC_TREAT_WARNINGS_AS_ERRORS = NO; 277 | GCC_VERSION = ""; 278 | GCC_WARN_ABOUT_RETURN_TYPE = NO; 279 | GCC_WARN_MISSING_PARENTHESES = NO; 280 | GCC_WARN_SHADOW = NO; 281 | GCC_WARN_STRICT_SELECTOR_MATCH = NO; 282 | GCC_WARN_UNUSED_FUNCTION = YES; 283 | GCC_WARN_UNUSED_PARAMETER = NO; 284 | GCC_WARN_UNUSED_VALUE = NO; 285 | GCC_WARN_UNUSED_VARIABLE = NO; 286 | INSTALL_PATH = /usr/local/lib; 287 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 288 | LIBRARY_SEARCH_PATHS = ""; 289 | OTHER_CFLAGS = "-DTI_POST_1_2"; 290 | OTHER_LDFLAGS = "-ObjC"; 291 | PRODUCT_NAME = TiDraggable; 292 | RUN_CLANG_STATIC_ANALYZER = NO; 293 | SDKROOT = iphoneos; 294 | USER_HEADER_SEARCH_PATHS = ""; 295 | }; 296 | name = Release; 297 | }; 298 | 1DEB922308733DC00010E9CD /* Debug */ = { 299 | isa = XCBuildConfiguration; 300 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 301 | buildSettings = { 302 | CODE_SIGN_IDENTITY = "iPhone Developer"; 303 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 304 | DSTROOT = /tmp/TiDraggable.dst; 305 | GCC_C_LANGUAGE_STANDARD = c99; 306 | GCC_OPTIMIZATION_LEVEL = 0; 307 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 308 | GCC_PREFIX_HEADER = TiDraggable_Prefix.pch; 309 | GCC_PREPROCESSOR_DEFINITIONS = "TI_VERSION=$(TI_VERSION)"; 310 | GCC_TREAT_WARNINGS_AS_ERRORS = NO; 311 | GCC_VERSION = ""; 312 | GCC_WARN_ABOUT_RETURN_TYPE = NO; 313 | GCC_WARN_MISSING_PARENTHESES = NO; 314 | GCC_WARN_SHADOW = NO; 315 | GCC_WARN_STRICT_SELECTOR_MATCH = NO; 316 | GCC_WARN_UNUSED_FUNCTION = YES; 317 | GCC_WARN_UNUSED_PARAMETER = NO; 318 | GCC_WARN_UNUSED_VALUE = NO; 319 | GCC_WARN_UNUSED_VARIABLE = NO; 320 | INSTALL_PATH = /usr/local/lib; 321 | OTHER_CFLAGS = ( 322 | "-DDEBUG", 323 | "-DTI_POST_1_2", 324 | ); 325 | OTHER_LDFLAGS = "-ObjC"; 326 | PRODUCT_NAME = TiDraggable; 327 | PROVISIONING_PROFILE = ""; 328 | "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; 329 | RUN_CLANG_STATIC_ANALYZER = NO; 330 | SDKROOT = iphoneos; 331 | USER_HEADER_SEARCH_PATHS = ""; 332 | }; 333 | name = Debug; 334 | }; 335 | 1DEB922408733DC00010E9CD /* Release */ = { 336 | isa = XCBuildConfiguration; 337 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 338 | buildSettings = { 339 | ALWAYS_SEARCH_USER_PATHS = NO; 340 | DSTROOT = /tmp/TiDraggable.dst; 341 | GCC_C_LANGUAGE_STANDARD = c99; 342 | GCC_MODEL_TUNING = G5; 343 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 344 | GCC_PREFIX_HEADER = TiDraggable_Prefix.pch; 345 | GCC_PREPROCESSOR_DEFINITIONS = "TI_VERSION=$(TI_VERSION)"; 346 | GCC_TREAT_WARNINGS_AS_ERRORS = NO; 347 | GCC_VERSION = ""; 348 | GCC_WARN_ABOUT_RETURN_TYPE = NO; 349 | GCC_WARN_MISSING_PARENTHESES = NO; 350 | GCC_WARN_SHADOW = NO; 351 | GCC_WARN_STRICT_SELECTOR_MATCH = NO; 352 | GCC_WARN_UNUSED_FUNCTION = YES; 353 | GCC_WARN_UNUSED_PARAMETER = NO; 354 | GCC_WARN_UNUSED_VALUE = NO; 355 | GCC_WARN_UNUSED_VARIABLE = NO; 356 | INSTALL_PATH = /usr/local/lib; 357 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 358 | OTHER_CFLAGS = "-DTI_POST_1_2"; 359 | OTHER_LDFLAGS = "-ObjC"; 360 | PRODUCT_NAME = TiDraggable; 361 | RUN_CLANG_STATIC_ANALYZER = NO; 362 | SDKROOT = iphoneos; 363 | USER_HEADER_SEARCH_PATHS = ""; 364 | }; 365 | name = Release; 366 | }; 367 | 24416B8211C4CA220047AFDD /* Debug */ = { 368 | isa = XCBuildConfiguration; 369 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 370 | buildSettings = { 371 | COPY_PHASE_STRIP = NO; 372 | GCC_DYNAMIC_NO_PIC = NO; 373 | GCC_OPTIMIZATION_LEVEL = 0; 374 | PRODUCT_NAME = "Build & test"; 375 | }; 376 | name = Debug; 377 | }; 378 | 24416B8311C4CA220047AFDD /* Release */ = { 379 | isa = XCBuildConfiguration; 380 | baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; 381 | buildSettings = { 382 | COPY_PHASE_STRIP = YES; 383 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 384 | GCC_ENABLE_FIX_AND_CONTINUE = NO; 385 | PRODUCT_NAME = "Build & test"; 386 | ZERO_LINK = NO; 387 | }; 388 | name = Release; 389 | }; 390 | /* End XCBuildConfiguration section */ 391 | 392 | /* Begin XCConfigurationList section */ 393 | 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "draggable" */ = { 394 | isa = XCConfigurationList; 395 | buildConfigurations = ( 396 | 1DEB921F08733DC00010E9CD /* Debug */, 397 | 1DEB922008733DC00010E9CD /* Release */, 398 | ); 399 | defaultConfigurationIsVisible = 0; 400 | defaultConfigurationName = Release; 401 | }; 402 | 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "draggable" */ = { 403 | isa = XCConfigurationList; 404 | buildConfigurations = ( 405 | 1DEB922308733DC00010E9CD /* Debug */, 406 | 1DEB922408733DC00010E9CD /* Release */, 407 | ); 408 | defaultConfigurationIsVisible = 0; 409 | defaultConfigurationName = Release; 410 | }; 411 | 24416B8A11C4CA520047AFDD /* Build configuration list for PBXAggregateTarget "Build & Test" */ = { 412 | isa = XCConfigurationList; 413 | buildConfigurations = ( 414 | 24416B8211C4CA220047AFDD /* Debug */, 415 | 24416B8311C4CA220047AFDD /* Release */, 416 | ); 417 | defaultConfigurationIsVisible = 0; 418 | defaultConfigurationName = Release; 419 | }; 420 | /* End XCConfigurationList section */ 421 | }; 422 | rootObject = 0867D690FE84028FC02AAC07 /* Project object */; 423 | } 424 | -------------------------------------------------------------------------------- /ios/Classes/TiDraggableGesture.m: -------------------------------------------------------------------------------- 1 | /** 2 | * An enhanced fork of the original TiDraggable module by Pedro Enrique, 3 | * allows for simple creation of "draggable" views. 4 | * 5 | * Copyright (C) 2013 Seth Benjamin 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | * 20 | * -- Original License -- 21 | * 22 | * Copyright 2012 Pedro Enrique 23 | * 24 | * Licensed under the Apache License, Version 2.0 (the "License"); 25 | * you may not use this file except in compliance with the License. 26 | * You may obtain a copy of the License at 27 | * 28 | * http://www.apache.org/licenses/LICENSE-2.0 29 | * 30 | * Unless required by applicable law or agreed to in writing, software 31 | * distributed under the License is distributed on an "AS IS" BASIS, 32 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | * See the License for the specific language governing permissions and 34 | * limitations under the License. 35 | */ 36 | 37 | #import 38 | #import 39 | #import "TiViewProxy+ViewProxyExtended.h" 40 | #import "TiDraggableGesture.h" 41 | 42 | @implementation TiDraggableGesture 43 | 44 | - (id)initWithProxy:(TiViewProxy*)proxy andOptions:(NSDictionary *)options 45 | { 46 | if (self = [super init]) 47 | { 48 | self.proxy = proxy; 49 | self.gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panDetected:)]; 50 | 51 | [self.proxy setValue:self forKey:@"draggable"]; 52 | [self.proxy setProxyObserver:self]; 53 | 54 | [self setValuesForKeysWithDictionary:options]; 55 | [self correctMappedProxyPositions]; 56 | } 57 | 58 | return self; 59 | } 60 | 61 | - (void)proxyDidRelayout:(id)sender 62 | { 63 | BOOL gestureIsAttached = [self.proxy.view.gestureRecognizers containsObject:self.gesture]; 64 | 65 | if (! gestureIsAttached && [self.proxy viewReady]) 66 | { 67 | [self.proxy.view addGestureRecognizer:self.gesture]; 68 | } 69 | } 70 | 71 | - (void)dealloc 72 | { 73 | RELEASE_TO_NIL(self.gesture); 74 | 75 | [super dealloc]; 76 | } 77 | 78 | - (void)setConfig:(id)args 79 | { 80 | BOOL didUpdateConfig = NO; 81 | 82 | if ([args isKindOfClass:[NSDictionary class]]) 83 | { 84 | [self setValuesForKeysWithDictionary:args]; 85 | 86 | didUpdateConfig = YES; 87 | } 88 | else if ([args isKindOfClass:[NSArray class]] && [args count] >= 2) 89 | { 90 | NSString* key; 91 | id value = [args objectAtIndex:1]; 92 | 93 | ENSURE_ARG_AT_INDEX(key, args, 0, NSString); 94 | 95 | NSMutableDictionary* params = [[self valueForKey:@"config"] mutableCopy]; 96 | 97 | if (! params) 98 | { 99 | params = [[NSMutableDictionary alloc] init]; 100 | } 101 | 102 | [params setValue:value forKeyPath:key]; 103 | 104 | [self setValuesForKeysWithDictionary:[[params copy] autorelease]]; 105 | 106 | [params release]; 107 | 108 | didUpdateConfig = YES; 109 | } 110 | 111 | if (didUpdateConfig) 112 | { 113 | [self correctMappedProxyPositions]; 114 | } 115 | } 116 | 117 | - (void)panDetected:(UIPanGestureRecognizer *)panRecognizer 118 | { 119 | ENSURE_UI_THREAD_1_ARG(panRecognizer); 120 | 121 | if ([TiUtils boolValue:[self valueForKey:@"enabled"] def:YES] == NO) 122 | { 123 | return; 124 | } 125 | 126 | NSString* axis = [self valueForKey:@"axis"]; 127 | NSInteger maxLeft = [[self valueForKey:@"maxLeft"] floatValue]; 128 | NSInteger minLeft = [[self valueForKey:@"minLeft"] floatValue]; 129 | NSInteger maxTop = [[self valueForKey:@"maxTop"] floatValue]; 130 | NSInteger minTop = [[self valueForKey:@"minTop"] floatValue]; 131 | BOOL hasMaxLeft = [self valueForKey:@"maxLeft"] != nil; 132 | BOOL hasMinLeft = [self valueForKey:@"minLeft"] != nil; 133 | BOOL hasMaxTop = [self valueForKey:@"maxTop"] != nil; 134 | BOOL hasMinTop = [self valueForKey:@"minTop"] != nil; 135 | BOOL ensureRight = [TiUtils boolValue:[self valueForKey:@"ensureRight"] def:NO]; 136 | BOOL ensureBottom = [TiUtils boolValue:[self valueForKey:@"ensureBottom"] def:NO]; 137 | BOOL cancelAnimations = [TiUtils boolValue:[self valueForKey:@"cancelAnimations"] def:YES]; 138 | 139 | if (cancelAnimations && [[self.proxy.view.layer animationKeys] count] > 0) 140 | { 141 | [self.proxy.view setFrame:[[self.proxy.view.layer presentationLayer] frame]]; 142 | [self.proxy.view.layer removeAllAnimations]; 143 | } 144 | 145 | CGPoint translation = [panRecognizer translationInView:self.proxy.view]; 146 | CGPoint newCenter = self.proxy.view.center; 147 | CGSize size = self.proxy.view.frame.size; 148 | 149 | float tmpTranslationX, tmpTranslationY; 150 | 151 | if ([panRecognizer state] == UIGestureRecognizerStateBegan) 152 | { 153 | touchStart = self.proxy.view.frame.origin; 154 | } 155 | else if ([panRecognizer state] == UIGestureRecognizerStateEnded) 156 | { 157 | touchEnd = self.proxy.view.frame.origin; 158 | } 159 | 160 | if([[self valueForKey:@"axis"] isEqualToString:@"x"]) 161 | { 162 | tmpTranslationX = translation.x; 163 | 164 | newCenter.x += translation.x; 165 | newCenter.y = newCenter.y; 166 | } 167 | else if([[self valueForKey:@"axis"] isEqualToString:@"y"]) 168 | { 169 | tmpTranslationY = translation.y; 170 | 171 | newCenter.x = newCenter.x; 172 | newCenter.y += translation.y; 173 | } 174 | else 175 | { 176 | tmpTranslationX = translation.x; 177 | tmpTranslationY = translation.y; 178 | 179 | newCenter.x += translation.x; 180 | newCenter.y += translation.y; 181 | } 182 | 183 | if(hasMaxLeft || hasMaxTop || hasMinLeft || hasMinTop) 184 | { 185 | if(hasMaxLeft && newCenter.x - size.width / 2 > maxLeft) 186 | { 187 | newCenter.x = maxLeft + size.width / 2; 188 | } 189 | else if(hasMinLeft && newCenter.x - size.width / 2 < minLeft) 190 | { 191 | newCenter.x = minLeft + size.width / 2; 192 | } 193 | 194 | if(hasMaxTop && newCenter.y - size.height / 2 > maxTop) 195 | { 196 | newCenter.y = maxTop + size.height / 2; 197 | } 198 | else if(hasMinTop && newCenter.y - size.height / 2 < minTop) 199 | { 200 | newCenter.y = minTop + size.height / 2; 201 | } 202 | } 203 | 204 | LayoutConstraint* layoutProperties = [self.proxy layoutProperties]; 205 | 206 | if ([self valueForKey:@"axis"] == nil || [[self valueForKey:@"axis"] isEqualToString:@"x"]) 207 | { 208 | layoutProperties->left = TiDimensionDip(newCenter.x - size.width / 2); 209 | 210 | if (ensureRight) 211 | { 212 | layoutProperties->right = TiDimensionDip(layoutProperties->left.value * -1); 213 | } 214 | } 215 | 216 | if ([self valueForKey:@"axis"] == nil || [[self valueForKey:@"axis"] isEqualToString:@"y"]) 217 | { 218 | layoutProperties->top = TiDimensionDip(newCenter.y - size.height / 2); 219 | 220 | if (ensureBottom) 221 | { 222 | layoutProperties->bottom = TiDimensionDip(layoutProperties->top.value * -1); 223 | } 224 | } 225 | 226 | [self.proxy refreshView:nil]; 227 | 228 | [panRecognizer setTranslation:CGPointZero inView:self.proxy.view]; 229 | 230 | [self mapProxyOriginToCollection:[self valueForKey:@"maps"] 231 | withTranslationX:tmpTranslationX 232 | andTranslationY:tmpTranslationY]; 233 | 234 | TiViewProxy* panningProxy = (TiViewProxy*)[self.proxy.view proxy]; 235 | 236 | float left = [panningProxy view].frame.origin.x; 237 | float top = [panningProxy view].frame.origin.y; 238 | 239 | NSMutableDictionary *tiProps = [NSMutableDictionary dictionaryWithObjectsAndKeys: 240 | [NSNumber numberWithFloat:left], @"left", 241 | [NSNumber numberWithFloat:top], @"top", 242 | [TiUtils pointToDictionary:self.proxy.view.center], @"center", 243 | [TiUtils pointToDictionary:[panRecognizer velocityInView:self.proxy.view]], @"velocity", 244 | nil]; 245 | 246 | if([panningProxy _hasListeners:@"start"] && [panRecognizer state] == UIGestureRecognizerStateBegan) 247 | { 248 | [panningProxy fireEvent:@"start" withObject:tiProps]; 249 | } 250 | else if([panningProxy _hasListeners:@"move"] && [panRecognizer state] == UIGestureRecognizerStateChanged) 251 | { 252 | [panningProxy fireEvent:@"move" withObject:tiProps]; 253 | } 254 | else if([panRecognizer state] == UIGestureRecognizerStateEnded || [panRecognizer state] == UIGestureRecognizerStateCancelled) 255 | { 256 | [tiProps setValue:[NSDictionary dictionaryWithObjectsAndKeys: 257 | [NSNumber numberWithFloat:touchEnd.x - touchStart.x], @"x", 258 | [NSNumber numberWithFloat:touchEnd.y - touchStart.y], @"y", 259 | nil] forKey:@"distance"]; 260 | 261 | [panningProxy fireEvent:([panRecognizer state] == UIGestureRecognizerStateCancelled ? @"cancel" : @"end") 262 | withObject:tiProps]; 263 | } 264 | } 265 | 266 | - (void)correctMappedProxyPositions 267 | { 268 | NSArray* maps = [self valueForKey:@"maps"]; 269 | 270 | if ([maps isKindOfClass:[NSArray class]]) 271 | { 272 | [maps enumerateObjectsUsingBlock:^(id map, NSUInteger index, BOOL *stop) { 273 | TiViewProxy* proxy = [map objectForKey:@"view"]; 274 | NSDictionary* constraints = [map objectForKey:@"constrain"]; 275 | NSDictionary* constraintX = [constraints objectForKey:@"x"]; 276 | NSDictionary* constraintY = [constraints objectForKey:@"y"]; 277 | BOOL fromCenterX = [TiUtils boolValue:[constraintX objectForKey:@"fromCenter"] def:NO]; 278 | BOOL fromCenterY = [TiUtils boolValue:[constraintY objectForKey:@"fromCenter"] def:NO]; 279 | 280 | CGSize proxySize = [proxy view].frame.size; 281 | CGPoint proxyCenter = [proxy view].center; 282 | 283 | NSNumber* parallaxAmount = [TiUtils numberFromObject:[map objectForKey:@"parallaxAmount"]]; 284 | 285 | if (! parallaxAmount) 286 | { 287 | parallaxAmount = [NSNumber numberWithInteger:1]; 288 | } 289 | 290 | if (constraintX) 291 | { 292 | NSNumber* startX = [constraintX objectForKey:@"start"]; 293 | 294 | if (fromCenterX) 295 | { 296 | proxyCenter.x = [startX floatValue]; 297 | proxyCenter.x += [proxy.parent view].frame.size.width / 2; 298 | } 299 | else 300 | { 301 | proxyCenter.x = [startX floatValue] / [parallaxAmount floatValue]; 302 | proxyCenter.x += proxySize.width / 2; 303 | } 304 | } 305 | 306 | if (constraintY) 307 | { 308 | NSNumber* startY = [constraintY objectForKey:@"start"]; 309 | 310 | if (fromCenterY) 311 | { 312 | proxyCenter.y = [startY floatValue]; 313 | proxyCenter.y += [proxy.parent view].frame.size.height / 2; 314 | } 315 | else 316 | { 317 | proxyCenter.y = [startY floatValue] / [parallaxAmount floatValue]; 318 | proxyCenter.y += proxySize.height / 2; 319 | } 320 | } 321 | 322 | if (constraintX || constraintY) 323 | { 324 | [proxy view].center = proxyCenter; 325 | } 326 | 327 | LayoutConstraint* layoutProperties = [proxy layoutProperties]; 328 | 329 | if (constraintX) 330 | { 331 | layoutProperties->left = TiDimensionDip([proxy view].frame.origin.x); 332 | } 333 | 334 | if (constraintY) 335 | { 336 | layoutProperties->top = TiDimensionDip([proxy view].frame.origin.y); 337 | } 338 | 339 | [proxy respositionEx]; 340 | }]; 341 | } 342 | } 343 | 344 | - (void)mapProxyOriginToCollection:(NSArray*)proxies withTranslationX:(float)translationX andTranslationY:(float)translationY 345 | { 346 | if ([proxies isKindOfClass:[NSArray class]]) 347 | { 348 | BOOL cancelAnimations = [TiUtils boolValue:[self valueForKey:@"cancelAnimations"] def:YES]; 349 | 350 | [proxies enumerateObjectsUsingBlock:^(id map, NSUInteger index, BOOL *stop) { 351 | TiViewProxy* proxy = [map objectForKey:@"view"]; 352 | 353 | if (cancelAnimations && [[proxy.view.layer animationKeys] count] > 0) 354 | { 355 | [proxy.view setFrame:[[proxy.view.layer presentationLayer] frame]]; 356 | [proxy.view.layer removeAllAnimations]; 357 | } 358 | 359 | CGPoint proxyCenter = [proxy view].center; 360 | CGSize proxySize = [proxy view].frame.size; 361 | CGSize parentSize = [[proxy.parent view] frame].size; 362 | 363 | NSNumber* parallaxAmount = [TiUtils numberFromObject:[map objectForKey:@"parallaxAmount"]]; 364 | 365 | if (! parallaxAmount) 366 | { 367 | parallaxAmount = [NSNumber numberWithInteger:1]; 368 | } 369 | 370 | NSDictionary* constraints = [map objectForKey:@"constrain"]; 371 | NSDictionary* xConstraint = [constraints objectForKey:@"x"]; 372 | NSDictionary* yConstraint = [constraints objectForKey:@"y"]; 373 | NSString* constraintAxis = [constraints objectForKey:@"axis"]; 374 | 375 | if (constraints) 376 | { 377 | if (xConstraint && ([constraintAxis isEqualToString:@"x"] || constraintAxis == nil)) 378 | { 379 | NSNumber* parentMinLeft = [self valueForKey:@"minLeft"]; 380 | NSNumber* parentMaxLeft = [self valueForKey:@"maxLeft"]; 381 | NSNumber* xStart = [xConstraint objectForKey:@"start"]; 382 | NSNumber* xEnd = [xConstraint objectForKey:@"end"]; 383 | 384 | float xDistance = [parentMaxLeft floatValue] - [parentMinLeft floatValue]; 385 | float xCalcCenter = proxySize.width / 2; 386 | float xWidth, xStartParallax, xEndParallax, xRatio; 387 | 388 | if (xStart && xEnd) 389 | { 390 | xStartParallax = [xStart floatValue] / [parallaxAmount floatValue]; 391 | xWidth = fabsf(xStartParallax) + fabsf([xEnd floatValue]); 392 | } 393 | else 394 | { 395 | xWidth = proxySize.width / [parallaxAmount floatValue]; 396 | } 397 | 398 | if (parentMinLeft || parentMaxLeft) 399 | { 400 | xRatio = xDistance == 0 ? 1 : xWidth / xDistance; 401 | } 402 | else 403 | { 404 | xRatio = xWidth / (parentSize.width / 2); 405 | } 406 | 407 | proxyCenter.x += (translationX * ([xEnd floatValue] < xStartParallax ? -1 : 1)) * xRatio; 408 | 409 | if(xStart && xEnd) 410 | { 411 | BOOL xFromCenter = [TiUtils boolValue:[xConstraint objectForKey:@"fromCenter"] def:NO]; 412 | float xLeftEdge = proxyCenter.x - xCalcCenter; 413 | 414 | if (xFromCenter) 415 | { 416 | xStart = [NSNumber numberWithFloat:[xStart floatValue] + xLeftEdge]; 417 | xEnd = [NSNumber numberWithFloat:[xEnd floatValue] + xLeftEdge]; 418 | } 419 | 420 | if ([xEnd floatValue] > [xStart floatValue]) 421 | { 422 | if(xLeftEdge > [xEnd floatValue]) 423 | { 424 | proxyCenter.x = [xEnd floatValue] + xCalcCenter; 425 | } 426 | else if(xLeftEdge < xStartParallax) 427 | { 428 | proxyCenter.x = xStartParallax + xCalcCenter; 429 | } 430 | } 431 | else 432 | { 433 | if(xLeftEdge < [xEnd floatValue]) 434 | { 435 | proxyCenter.x = [xEnd floatValue] + xCalcCenter; 436 | } 437 | else if(xLeftEdge > xStartParallax) 438 | { 439 | proxyCenter.x = xStartParallax + xCalcCenter; 440 | } 441 | } 442 | 443 | KrollCallback* xCallback = [xConstraint objectForKey:@"callback"]; 444 | 445 | if (xCallback) 446 | { 447 | float currentLeftEdge = proxyCenter.x - xCalcCenter; 448 | float translationCompleted = fabsf((currentLeftEdge - xStartParallax) / xWidth); 449 | 450 | [proxy.parent _fireEventToListener:@"translated" 451 | withObject:@{ @"completed" : [NSNumber numberWithFloat:translationCompleted] } 452 | listener:xCallback 453 | thisObject:nil]; 454 | } 455 | } 456 | } 457 | else if ([constraintAxis isEqualToString:@"x"]) 458 | { 459 | proxyCenter.x += translationX / [parallaxAmount floatValue]; 460 | } 461 | 462 | if (yConstraint && ([constraintAxis isEqualToString:@"y"] || constraintAxis == nil)) 463 | { 464 | NSNumber* parentMinTop = [self valueForKey:@"minTop"]; 465 | NSNumber* parentMaxTop = [self valueForKey:@"maxTop"]; 466 | NSNumber* yStart = [yConstraint objectForKey:@"start"]; 467 | NSNumber* yEnd = [yConstraint objectForKey:@"end"]; 468 | 469 | float yDistance = [parentMaxTop floatValue] - [parentMinTop floatValue]; 470 | float yCalcCenter = proxySize.height / 2; 471 | float yHeight, yStartParallax, yEndParallax, yRatio; 472 | 473 | if (yStart && yEnd) 474 | { 475 | yStartParallax = [yStart floatValue] / [parallaxAmount floatValue]; 476 | yHeight = fabsf(yStartParallax) + fabsf([yEnd floatValue]); 477 | } 478 | else 479 | { 480 | yHeight = proxySize.height / [parallaxAmount floatValue]; 481 | } 482 | 483 | if (parentMinTop || parentMaxTop) 484 | { 485 | yRatio = yDistance == 0 ? 1 : yHeight / yDistance; 486 | } 487 | else 488 | { 489 | yRatio = yHeight / (parentSize.height / 2); 490 | } 491 | 492 | proxyCenter.y += (translationY * ([yEnd floatValue] < yStartParallax ? -1 : 1)) * yRatio; 493 | 494 | if(yStart && yEnd) 495 | { 496 | BOOL yFromCenter = [TiUtils boolValue:[yConstraint objectForKey:@"fromCenter"] def:NO]; 497 | float yTopEdge = proxyCenter.y - yCalcCenter; 498 | 499 | if (yFromCenter) 500 | { 501 | yStart = [NSNumber numberWithFloat:[yStart floatValue] + yTopEdge]; 502 | yEnd = [NSNumber numberWithFloat:[yEnd floatValue] + yTopEdge]; 503 | } 504 | 505 | if ([yEnd floatValue] > [yStart floatValue]) 506 | { 507 | if(yTopEdge > [yEnd floatValue]) 508 | { 509 | proxyCenter.y = [yEnd floatValue] + yCalcCenter; 510 | } 511 | else if(yTopEdge < yStartParallax) 512 | { 513 | proxyCenter.y = yStartParallax + yCalcCenter; 514 | } 515 | } 516 | else 517 | { 518 | if(yTopEdge < [yEnd floatValue]) 519 | { 520 | proxyCenter.y = [yEnd floatValue] + yCalcCenter; 521 | } 522 | else if(yTopEdge > yStartParallax) 523 | { 524 | proxyCenter.y = yStartParallax + yCalcCenter; 525 | } 526 | } 527 | 528 | KrollCallback* yCallback = [yConstraint objectForKey:@"callback"]; 529 | 530 | if (yCallback) 531 | { 532 | float currentTopEdge = proxyCenter.y - yCalcCenter; 533 | float translationCompleted = fabsf((currentTopEdge - yStartParallax) / yHeight); 534 | 535 | [proxy.parent _fireEventToListener:@"translated" 536 | withObject:@{ @"completed" : [NSNumber numberWithFloat:translationCompleted] } 537 | listener:yCallback 538 | thisObject:nil]; 539 | } 540 | } 541 | } 542 | else if ([constraintAxis isEqualToString:@"y"]) 543 | { 544 | proxyCenter.y += translationY / [parallaxAmount floatValue]; 545 | } 546 | } 547 | else 548 | { 549 | proxyCenter.x += translationX / [parallaxAmount floatValue]; 550 | proxyCenter.y += translationY / [parallaxAmount floatValue]; 551 | } 552 | 553 | TiProxy* proxyDraggable = [proxy valueForKey:@"draggable"]; 554 | 555 | NSInteger maxLeft = [[proxyDraggable valueForKey:@"maxLeft"] floatValue]; 556 | NSInteger minLeft = [[proxyDraggable valueForKey:@"minLeft"] floatValue]; 557 | NSInteger maxTop = [[proxyDraggable valueForKey:@"maxTop"] floatValue]; 558 | NSInteger minTop = [[proxyDraggable valueForKey:@"minTop"] floatValue]; 559 | BOOL hasMaxLeft = [proxyDraggable valueForKey:@"maxLeft"] != nil; 560 | BOOL hasMinLeft = [proxyDraggable valueForKey:@"minLeft"] != nil; 561 | BOOL hasMaxTop = [proxyDraggable valueForKey:@"maxTop"] != nil; 562 | BOOL hasMinTop = [proxyDraggable valueForKey:@"minTop"] != nil; 563 | BOOL ensureRight = [TiUtils boolValue:[proxyDraggable valueForKey:@"ensureRight"] def:NO]; 564 | BOOL ensureBottom = [TiUtils boolValue:[proxyDraggable valueForKey:@"ensureBottom"] def:NO]; 565 | 566 | if(hasMaxLeft || hasMaxTop || hasMinLeft || hasMinTop) 567 | { 568 | if(hasMaxLeft && proxyCenter.x - proxySize.width / 2 > maxLeft) 569 | { 570 | proxyCenter.x = maxLeft + proxySize.width / 2; 571 | } 572 | else if(hasMinLeft && proxyCenter.x - proxySize.width / 2 < minLeft) 573 | { 574 | proxyCenter.x = minLeft + proxySize.width / 2; 575 | } 576 | 577 | if(hasMaxTop && proxyCenter.y - proxySize.height / 2 > maxTop) 578 | { 579 | proxyCenter.y = maxTop + proxySize.height / 2; 580 | } 581 | else if(hasMinTop && proxyCenter.y - proxySize.height / 2 < minTop) 582 | { 583 | proxyCenter.y = minTop + proxySize.height / 2; 584 | } 585 | } 586 | 587 | LayoutConstraint* layoutProperties = [proxy layoutProperties]; 588 | 589 | layoutProperties->top = TiDimensionDip(proxyCenter.y - proxySize.height / 2); 590 | layoutProperties->left = TiDimensionDip(proxyCenter.x - proxySize.width / 2); 591 | 592 | if (ensureBottom) 593 | { 594 | layoutProperties->bottom = TiDimensionDip(layoutProperties->top.value * -1); 595 | } 596 | 597 | if (ensureRight) 598 | { 599 | layoutProperties->right = TiDimensionDip(layoutProperties->left.value * -1); 600 | } 601 | 602 | [proxy respositionEx]; 603 | }]; 604 | } 605 | } 606 | 607 | @end --------------------------------------------------------------------------------