mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 14:52:41 +01:00
Add support for device swipe events
Summary: Ref T2700. Allow JS to listen for swipes on devices. There are a bunch of tricky cases here and I probably didn't get them all totally right, but this interaction broadly looks like this: - We implement gesture recognition for the mouse in device modes (narrow browser), and for touch events from an actual device. - The sigil `touchable` indicates that a node wants to react to touch events. - When the user touches a `touchable` node, we start listening for moves. They might be tapping/clicking (in which case we don't care), but they might also be gesturing. - Once the user moves their finger/pointer far enough away from the tap origin, we recognize it as a gesture. I hardcoded this at 20px; I wasn't able to find any "official" Apple value, but 20px seems like a common default. - At this point, we look at where their finger has moved. - If they moved it mostly up/down, we interpret the gesture as "scroll" and just stop listening. The device does its own thing. - However, if they moved it mostly left/right, we interpret it as a "swipe". We start killing the moves so the device doesn't scroll. - Once we've recognized that a gesture is underway, we send a "gesture.swipe.start" event and then "gesture.swipe.move" events for every move. - When the user ends the gesture, we send "gesture.swipe.end". - If the user cancels the gesture (currently, only by tapping with a second finger), we send "gesture.swipe.cancel". - Gesture events have raw position data and some convenience fields. Test Plan: Wrote UI example and used it from the Desktop, iPhone simulator, and a real iphone. - The code always seems to get "scroll" vs "swipe" correct (i.e., consistent with my intentions). - The threshold feels pretty good to me. - Tapping with a second finger cancels the action. Reviewers: chad, btrahan Reviewed By: chad CC: aran Maniphest Tasks: T2700 Differential Revision: https://secure.phabricator.com/D5308
This commit is contained in:
parent
4c914a5c49
commit
6b5204dca9
7 changed files with 382 additions and 101 deletions
|
@ -51,6 +51,7 @@ $package_spec = array(
|
|||
'javelin-behavior-konami',
|
||||
'javelin-behavior-aphlict-dropdown',
|
||||
'javelin-behavior-history-install',
|
||||
'javelin-behavior-phabricator-gesture',
|
||||
|
||||
'javelin-behavior-phabricator-active-nav',
|
||||
'javelin-behavior-phabricator-nav',
|
||||
|
|
|
@ -805,7 +805,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'conpherence-widget-pane-css' =>
|
||||
array(
|
||||
'uri' => '/res/6e5755bb/rsrc/css/application/conpherence/widget-pane.css',
|
||||
'uri' => '/res/e67ad581/rsrc/css/application/conpherence/widget-pane.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -1167,28 +1167,30 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'javelin-behavior-conpherence-menu' =>
|
||||
array(
|
||||
'uri' => '/res/0ad6ab54/rsrc/js/application/conpherence/behavior-menu.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-workflow',
|
||||
3 => 'javelin-util',
|
||||
4 => 'javelin-stratcom',
|
||||
5 => 'javelin-uri',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/conpherence/behavior-menu.js',
|
||||
),
|
||||
'javelin-behavior-conpherence-pontificate' =>
|
||||
array(
|
||||
'uri' => '/res/06214a06/rsrc/js/application/conpherence/behavior-pontificate.js',
|
||||
'uri' => '/res/cb1a5cf0/rsrc/js/application/conpherence/behavior-menu.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-request',
|
||||
3 => 'javelin-stratcom',
|
||||
4 => 'javelin-uri',
|
||||
5 => 'javelin-util',
|
||||
6 => 'javelin-workflow',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/conpherence/behavior-menu.js',
|
||||
),
|
||||
'javelin-behavior-conpherence-pontificate' =>
|
||||
array(
|
||||
'uri' => '/res/15263692/rsrc/js/application/conpherence/behavior-pontificate.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-util',
|
||||
3 => 'javelin-workflow',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/conpherence/behavior-pontificate.js',
|
||||
),
|
||||
|
@ -1746,6 +1748,34 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-file-tree.js',
|
||||
),
|
||||
'javelin-behavior-phabricator-gesture' =>
|
||||
array(
|
||||
'uri' => '/res/1222d486/rsrc/js/application/core/behavior-gesture.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-behavior-device',
|
||||
2 => 'javelin-stratcom',
|
||||
3 => 'javelin-vector',
|
||||
4 => 'javelin-dom',
|
||||
5 => 'javelin-magical-init',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/core/behavior-gesture.js',
|
||||
),
|
||||
'javelin-behavior-phabricator-gesture-example' =>
|
||||
array(
|
||||
'uri' => '/res/da636e19/rsrc/js/application/uiexample/gesture-example.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-stratcom',
|
||||
1 => 'javelin-behavior',
|
||||
2 => 'javelin-vector',
|
||||
3 => 'javelin-dom',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/uiexample/gesture-example.js',
|
||||
),
|
||||
'javelin-behavior-phabricator-keyboard-pager' =>
|
||||
array(
|
||||
'uri' => '/res/56d64eff/rsrc/js/application/core/behavior-keyboard-pager.js',
|
||||
|
@ -2664,7 +2694,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'phabricator-core-css' =>
|
||||
array(
|
||||
'uri' => '/res/b34e5c75/rsrc/css/core/core.css',
|
||||
'uri' => '/res/1e7afaa9/rsrc/css/core/core.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -3055,7 +3085,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'phabricator-standard-page-view' =>
|
||||
array(
|
||||
'uri' => '/res/252faaf4/rsrc/css/application/base/standard-page-view.css',
|
||||
'uri' => '/res/70fa2da4/rsrc/css/application/base/standard-page-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -3500,7 +3530,7 @@ celerity_register_resource_map(array(
|
|||
), array(
|
||||
'packages' =>
|
||||
array(
|
||||
'8e2735e2' =>
|
||||
'fdd3bb5f' =>
|
||||
array(
|
||||
'name' => 'core.pkg.css',
|
||||
'symbols' =>
|
||||
|
@ -3543,10 +3573,10 @@ celerity_register_resource_map(array(
|
|||
35 => 'phabricator-object-item-list-view-css',
|
||||
36 => 'global-drag-and-drop-css',
|
||||
),
|
||||
'uri' => '/res/pkg/8e2735e2/core.pkg.css',
|
||||
'uri' => '/res/pkg/fdd3bb5f/core.pkg.css',
|
||||
'type' => 'css',
|
||||
),
|
||||
'0ed3458f' =>
|
||||
'9abfe389' =>
|
||||
array(
|
||||
'name' => 'core.pkg.js',
|
||||
'symbols' =>
|
||||
|
@ -3578,15 +3608,16 @@ celerity_register_resource_map(array(
|
|||
24 => 'javelin-behavior-konami',
|
||||
25 => 'javelin-behavior-aphlict-dropdown',
|
||||
26 => 'javelin-behavior-history-install',
|
||||
27 => 'javelin-behavior-phabricator-active-nav',
|
||||
28 => 'javelin-behavior-phabricator-nav',
|
||||
29 => 'javelin-behavior-phabricator-remarkup-assist',
|
||||
30 => 'phabricator-textareautils',
|
||||
31 => 'phabricator-file-upload',
|
||||
32 => 'javelin-behavior-global-drag-and-drop',
|
||||
33 => 'javelin-behavior-phabricator-reveal-content',
|
||||
27 => 'javelin-behavior-phabricator-gesture',
|
||||
28 => 'javelin-behavior-phabricator-active-nav',
|
||||
29 => 'javelin-behavior-phabricator-nav',
|
||||
30 => 'javelin-behavior-phabricator-remarkup-assist',
|
||||
31 => 'phabricator-textareautils',
|
||||
32 => 'phabricator-file-upload',
|
||||
33 => 'javelin-behavior-global-drag-and-drop',
|
||||
34 => 'javelin-behavior-phabricator-reveal-content',
|
||||
),
|
||||
'uri' => '/res/pkg/0ed3458f/core.pkg.js',
|
||||
'uri' => '/res/pkg/9abfe389/core.pkg.js',
|
||||
'type' => 'js',
|
||||
),
|
||||
'dca4a03d' =>
|
||||
|
@ -3733,17 +3764,17 @@ celerity_register_resource_map(array(
|
|||
'reverse' =>
|
||||
array(
|
||||
'aphront-attached-file-view-css' => 'eb35a026',
|
||||
'aphront-crumbs-view-css' => '8e2735e2',
|
||||
'aphront-dialog-view-css' => '8e2735e2',
|
||||
'aphront-error-view-css' => '8e2735e2',
|
||||
'aphront-form-view-css' => '8e2735e2',
|
||||
'aphront-list-filter-view-css' => '8e2735e2',
|
||||
'aphront-pager-view-css' => '8e2735e2',
|
||||
'aphront-panel-view-css' => '8e2735e2',
|
||||
'aphront-table-view-css' => '8e2735e2',
|
||||
'aphront-tokenizer-control-css' => '8e2735e2',
|
||||
'aphront-tooltip-css' => '8e2735e2',
|
||||
'aphront-typeahead-control-css' => '8e2735e2',
|
||||
'aphront-crumbs-view-css' => 'fdd3bb5f',
|
||||
'aphront-dialog-view-css' => 'fdd3bb5f',
|
||||
'aphront-error-view-css' => 'fdd3bb5f',
|
||||
'aphront-form-view-css' => 'fdd3bb5f',
|
||||
'aphront-list-filter-view-css' => 'fdd3bb5f',
|
||||
'aphront-pager-view-css' => 'fdd3bb5f',
|
||||
'aphront-panel-view-css' => 'fdd3bb5f',
|
||||
'aphront-table-view-css' => 'fdd3bb5f',
|
||||
'aphront-tokenizer-control-css' => 'fdd3bb5f',
|
||||
'aphront-tooltip-css' => 'fdd3bb5f',
|
||||
'aphront-typeahead-control-css' => 'fdd3bb5f',
|
||||
'differential-changeset-view-css' => '8aaacd1b',
|
||||
'differential-core-view-css' => '8aaacd1b',
|
||||
'differential-inline-comment-editor' => '322728f3',
|
||||
|
@ -3757,19 +3788,19 @@ celerity_register_resource_map(array(
|
|||
'differential-table-of-contents-css' => '8aaacd1b',
|
||||
'diffusion-commit-view-css' => 'c8ce2d88',
|
||||
'diffusion-icons-css' => 'c8ce2d88',
|
||||
'global-drag-and-drop-css' => '8e2735e2',
|
||||
'global-drag-and-drop-css' => 'fdd3bb5f',
|
||||
'inline-comment-summary-css' => '8aaacd1b',
|
||||
'javelin-aphlict' => '0ed3458f',
|
||||
'javelin-aphlict' => '9abfe389',
|
||||
'javelin-behavior' => 'cd1d650a',
|
||||
'javelin-behavior-aphlict-dropdown' => '0ed3458f',
|
||||
'javelin-behavior-aphlict-listen' => '0ed3458f',
|
||||
'javelin-behavior-aphront-basic-tokenizer' => '0ed3458f',
|
||||
'javelin-behavior-aphlict-dropdown' => '9abfe389',
|
||||
'javelin-behavior-aphlict-listen' => '9abfe389',
|
||||
'javelin-behavior-aphront-basic-tokenizer' => '9abfe389',
|
||||
'javelin-behavior-aphront-drag-and-drop' => '322728f3',
|
||||
'javelin-behavior-aphront-drag-and-drop-textarea' => '322728f3',
|
||||
'javelin-behavior-aphront-form-disable-on-submit' => '0ed3458f',
|
||||
'javelin-behavior-aphront-form-disable-on-submit' => '9abfe389',
|
||||
'javelin-behavior-audit-preview' => 'f96657b8',
|
||||
'javelin-behavior-dark-console' => 'dca4a03d',
|
||||
'javelin-behavior-device' => '0ed3458f',
|
||||
'javelin-behavior-device' => '9abfe389',
|
||||
'javelin-behavior-differential-accept-with-errors' => '322728f3',
|
||||
'javelin-behavior-differential-add-reviewers-and-ccs' => '322728f3',
|
||||
'javelin-behavior-differential-comment-jump' => '322728f3',
|
||||
|
@ -3785,31 +3816,32 @@ celerity_register_resource_map(array(
|
|||
'javelin-behavior-diffusion-commit-graph' => 'f96657b8',
|
||||
'javelin-behavior-diffusion-pull-lastmodified' => 'f96657b8',
|
||||
'javelin-behavior-error-log' => 'dca4a03d',
|
||||
'javelin-behavior-global-drag-and-drop' => '0ed3458f',
|
||||
'javelin-behavior-history-install' => '0ed3458f',
|
||||
'javelin-behavior-konami' => '0ed3458f',
|
||||
'javelin-behavior-lightbox-attachments' => '0ed3458f',
|
||||
'javelin-behavior-global-drag-and-drop' => '9abfe389',
|
||||
'javelin-behavior-history-install' => '9abfe389',
|
||||
'javelin-behavior-konami' => '9abfe389',
|
||||
'javelin-behavior-lightbox-attachments' => '9abfe389',
|
||||
'javelin-behavior-load-blame' => '322728f3',
|
||||
'javelin-behavior-maniphest-batch-selector' => '7707de41',
|
||||
'javelin-behavior-maniphest-subpriority-editor' => '7707de41',
|
||||
'javelin-behavior-maniphest-transaction-controls' => '7707de41',
|
||||
'javelin-behavior-maniphest-transaction-expand' => '7707de41',
|
||||
'javelin-behavior-maniphest-transaction-preview' => '7707de41',
|
||||
'javelin-behavior-phabricator-active-nav' => '0ed3458f',
|
||||
'javelin-behavior-phabricator-autofocus' => '0ed3458f',
|
||||
'javelin-behavior-phabricator-keyboard-shortcuts' => '0ed3458f',
|
||||
'javelin-behavior-phabricator-nav' => '0ed3458f',
|
||||
'javelin-behavior-phabricator-active-nav' => '9abfe389',
|
||||
'javelin-behavior-phabricator-autofocus' => '9abfe389',
|
||||
'javelin-behavior-phabricator-gesture' => '9abfe389',
|
||||
'javelin-behavior-phabricator-keyboard-shortcuts' => '9abfe389',
|
||||
'javelin-behavior-phabricator-nav' => '9abfe389',
|
||||
'javelin-behavior-phabricator-object-selector' => '322728f3',
|
||||
'javelin-behavior-phabricator-oncopy' => '0ed3458f',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => '0ed3458f',
|
||||
'javelin-behavior-phabricator-reveal-content' => '0ed3458f',
|
||||
'javelin-behavior-phabricator-search-typeahead' => '0ed3458f',
|
||||
'javelin-behavior-phabricator-tooltips' => '0ed3458f',
|
||||
'javelin-behavior-phabricator-watch-anchor' => '0ed3458f',
|
||||
'javelin-behavior-refresh-csrf' => '0ed3458f',
|
||||
'javelin-behavior-phabricator-oncopy' => '9abfe389',
|
||||
'javelin-behavior-phabricator-remarkup-assist' => '9abfe389',
|
||||
'javelin-behavior-phabricator-reveal-content' => '9abfe389',
|
||||
'javelin-behavior-phabricator-search-typeahead' => '9abfe389',
|
||||
'javelin-behavior-phabricator-tooltips' => '9abfe389',
|
||||
'javelin-behavior-phabricator-watch-anchor' => '9abfe389',
|
||||
'javelin-behavior-refresh-csrf' => '9abfe389',
|
||||
'javelin-behavior-repository-crossreference' => '322728f3',
|
||||
'javelin-behavior-toggle-class' => '0ed3458f',
|
||||
'javelin-behavior-workflow' => '0ed3458f',
|
||||
'javelin-behavior-toggle-class' => '9abfe389',
|
||||
'javelin-behavior-workflow' => '9abfe389',
|
||||
'javelin-dom' => 'cd1d650a',
|
||||
'javelin-event' => 'cd1d650a',
|
||||
'javelin-install' => 'cd1d650a',
|
||||
|
@ -3828,48 +3860,48 @@ celerity_register_resource_map(array(
|
|||
'javelin-util' => 'cd1d650a',
|
||||
'javelin-vector' => 'cd1d650a',
|
||||
'javelin-workflow' => 'cd1d650a',
|
||||
'lightbox-attachment-css' => '8e2735e2',
|
||||
'lightbox-attachment-css' => 'fdd3bb5f',
|
||||
'maniphest-task-summary-css' => 'eb35a026',
|
||||
'maniphest-transaction-detail-css' => 'eb35a026',
|
||||
'phabricator-busy' => '0ed3458f',
|
||||
'phabricator-busy' => '9abfe389',
|
||||
'phabricator-content-source-view-css' => '8aaacd1b',
|
||||
'phabricator-core-buttons-css' => '8e2735e2',
|
||||
'phabricator-core-css' => '8e2735e2',
|
||||
'phabricator-crumbs-view-css' => '8e2735e2',
|
||||
'phabricator-directory-css' => '8e2735e2',
|
||||
'phabricator-core-buttons-css' => 'fdd3bb5f',
|
||||
'phabricator-core-css' => 'fdd3bb5f',
|
||||
'phabricator-crumbs-view-css' => 'fdd3bb5f',
|
||||
'phabricator-directory-css' => 'fdd3bb5f',
|
||||
'phabricator-drag-and-drop-file-upload' => '322728f3',
|
||||
'phabricator-dropdown-menu' => '0ed3458f',
|
||||
'phabricator-file-upload' => '0ed3458f',
|
||||
'phabricator-filetree-view-css' => '8e2735e2',
|
||||
'phabricator-flag-css' => '8e2735e2',
|
||||
'phabricator-form-view-css' => '8e2735e2',
|
||||
'phabricator-header-view-css' => '8e2735e2',
|
||||
'phabricator-jump-nav' => '8e2735e2',
|
||||
'phabricator-keyboard-shortcut' => '0ed3458f',
|
||||
'phabricator-keyboard-shortcut-manager' => '0ed3458f',
|
||||
'phabricator-main-menu-view' => '8e2735e2',
|
||||
'phabricator-menu-item' => '0ed3458f',
|
||||
'phabricator-nav-view-css' => '8e2735e2',
|
||||
'phabricator-notification' => '0ed3458f',
|
||||
'phabricator-notification-css' => '8e2735e2',
|
||||
'phabricator-notification-menu-css' => '8e2735e2',
|
||||
'phabricator-object-item-list-view-css' => '8e2735e2',
|
||||
'phabricator-dropdown-menu' => '9abfe389',
|
||||
'phabricator-file-upload' => '9abfe389',
|
||||
'phabricator-filetree-view-css' => 'fdd3bb5f',
|
||||
'phabricator-flag-css' => 'fdd3bb5f',
|
||||
'phabricator-form-view-css' => 'fdd3bb5f',
|
||||
'phabricator-header-view-css' => 'fdd3bb5f',
|
||||
'phabricator-jump-nav' => 'fdd3bb5f',
|
||||
'phabricator-keyboard-shortcut' => '9abfe389',
|
||||
'phabricator-keyboard-shortcut-manager' => '9abfe389',
|
||||
'phabricator-main-menu-view' => 'fdd3bb5f',
|
||||
'phabricator-menu-item' => '9abfe389',
|
||||
'phabricator-nav-view-css' => 'fdd3bb5f',
|
||||
'phabricator-notification' => '9abfe389',
|
||||
'phabricator-notification-css' => 'fdd3bb5f',
|
||||
'phabricator-notification-menu-css' => 'fdd3bb5f',
|
||||
'phabricator-object-item-list-view-css' => 'fdd3bb5f',
|
||||
'phabricator-object-selector-css' => '8aaacd1b',
|
||||
'phabricator-paste-file-upload' => '0ed3458f',
|
||||
'phabricator-prefab' => '0ed3458f',
|
||||
'phabricator-paste-file-upload' => '9abfe389',
|
||||
'phabricator-prefab' => '9abfe389',
|
||||
'phabricator-project-tag-css' => 'eb35a026',
|
||||
'phabricator-remarkup-css' => '8e2735e2',
|
||||
'phabricator-remarkup-css' => 'fdd3bb5f',
|
||||
'phabricator-shaped-request' => '322728f3',
|
||||
'phabricator-side-menu-view-css' => '8e2735e2',
|
||||
'phabricator-standard-page-view' => '8e2735e2',
|
||||
'phabricator-textareautils' => '0ed3458f',
|
||||
'phabricator-tooltip' => '0ed3458f',
|
||||
'phabricator-transaction-view-css' => '8e2735e2',
|
||||
'phabricator-zindex-css' => '8e2735e2',
|
||||
'sprite-apps-large-css' => '8e2735e2',
|
||||
'sprite-gradient-css' => '8e2735e2',
|
||||
'sprite-icon-css' => '8e2735e2',
|
||||
'sprite-menu-css' => '8e2735e2',
|
||||
'syntax-highlighting-css' => '8e2735e2',
|
||||
'phabricator-side-menu-view-css' => 'fdd3bb5f',
|
||||
'phabricator-standard-page-view' => 'fdd3bb5f',
|
||||
'phabricator-textareautils' => '9abfe389',
|
||||
'phabricator-tooltip' => '9abfe389',
|
||||
'phabricator-transaction-view-css' => 'fdd3bb5f',
|
||||
'phabricator-zindex-css' => 'fdd3bb5f',
|
||||
'sprite-apps-large-css' => 'fdd3bb5f',
|
||||
'sprite-gradient-css' => 'fdd3bb5f',
|
||||
'sprite-icon-css' => 'fdd3bb5f',
|
||||
'sprite-menu-css' => 'fdd3bb5f',
|
||||
'syntax-highlighting-css' => 'fdd3bb5f',
|
||||
),
|
||||
));
|
||||
|
|
|
@ -960,6 +960,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFormExample' => 'applications/uiexample/examples/PhabricatorFormExample.php',
|
||||
'PhabricatorGarbageCollectorConfigOptions' => 'applications/config/option/PhabricatorGarbageCollectorConfigOptions.php',
|
||||
'PhabricatorGarbageCollectorDaemon' => 'infrastructure/daemon/PhabricatorGarbageCollectorDaemon.php',
|
||||
'PhabricatorGestureExample' => 'applications/uiexample/examples/PhabricatorGestureExample.php',
|
||||
'PhabricatorGitGraphStream' => 'applications/repository/daemon/PhabricatorGitGraphStream.php',
|
||||
'PhabricatorGitHubConfigOptions' => 'applications/config/option/PhabricatorGitHubConfigOptions.php',
|
||||
'PhabricatorGlobalLock' => 'infrastructure/util/PhabricatorGlobalLock.php',
|
||||
|
@ -2481,6 +2482,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFormExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorGarbageCollectorConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
|
||||
'PhabricatorGestureExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorGitHubConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorGlobalLock' => 'PhutilLock',
|
||||
'PhabricatorGlobalUploadTargetView' => 'AphrontView',
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorGestureExample extends PhabricatorUIExample {
|
||||
|
||||
public function getName() {
|
||||
return 'Gestures';
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return hsprintf(
|
||||
'Use <tt>touchable</tt> to listen for gesture events. Note that you '.
|
||||
'must be in device mode for this to work (you can narrow your browser '.
|
||||
'window if you are on a desktop).');
|
||||
}
|
||||
|
||||
public function renderExample() {
|
||||
|
||||
$id = celerity_generate_unique_node_id();
|
||||
|
||||
Javelin::initBehavior(
|
||||
'phabricator-gesture-example',
|
||||
array(
|
||||
'rootID' => $id,
|
||||
));
|
||||
|
||||
return javelin_tag(
|
||||
'div',
|
||||
array(
|
||||
'sigil' => 'touchable',
|
||||
'id' => $id,
|
||||
'style' => 'width: 320px; height: 240px; margin: auto;',
|
||||
),
|
||||
'');
|
||||
}
|
||||
}
|
|
@ -154,6 +154,7 @@ final class PhabricatorStandardPageView extends PhabricatorBarePageView {
|
|||
Javelin::initBehavior('toggle-class', array());
|
||||
Javelin::initBehavior('konami', array());
|
||||
Javelin::initBehavior('history-install');
|
||||
Javelin::initBehavior('phabricator-gesture');
|
||||
|
||||
$current_token = null;
|
||||
if ($user) {
|
||||
|
|
125
webroot/rsrc/js/application/core/behavior-gesture.js
Normal file
125
webroot/rsrc/js/application/core/behavior-gesture.js
Normal file
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* @provides javelin-behavior-phabricator-gesture
|
||||
* @requires javelin-behavior
|
||||
* javelin-behavior-device
|
||||
* javelin-stratcom
|
||||
* javelin-vector
|
||||
* javelin-dom
|
||||
* javelin-magical-init
|
||||
* @javelin
|
||||
*/
|
||||
|
||||
/**
|
||||
* Basic gesture recognition. Unstable. Only supports swipes.
|
||||
*/
|
||||
JX.behavior('phabricator-gesture', function(config) {
|
||||
|
||||
var target = null;
|
||||
var swiping = false;
|
||||
var p0;
|
||||
var p1;
|
||||
|
||||
JX.Stratcom.listen(
|
||||
['touchstart', 'mousedown'],
|
||||
'touchable',
|
||||
function(e) {
|
||||
if (JX.Device.getDevice() == 'desktop') {
|
||||
return;
|
||||
}
|
||||
if (JX.Stratcom.pass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (target && e.getType() == 'touchstart') {
|
||||
// This corresponds to a second finger touching while the first finger
|
||||
// is held: stop the swipe.
|
||||
var event_data = get_swipe_data();
|
||||
var event_target = target;
|
||||
stop_swipe();
|
||||
JX.DOM.invoke(event_target, 'gesture.swipe.cancel', event_data);
|
||||
return;
|
||||
}
|
||||
|
||||
target = e.getNode('touchable');
|
||||
p0 = JX.Vector.getPos(e);
|
||||
p1 = JX.Vector.getPos(e);
|
||||
});
|
||||
|
||||
JX.enableDispatch(document.body, 'mousemove');
|
||||
JX.Stratcom.listen(
|
||||
['touchmove', 'mousemove'],
|
||||
null,
|
||||
function(e) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
p1 = JX.Vector.getPos(e);
|
||||
if (!swiping) {
|
||||
|
||||
// Here, we haven't started the swipe yet. We're waiting for you to
|
||||
// move your finger far enough to make it clear that you intend to
|
||||
// swipe or drag, not just tap.
|
||||
|
||||
var dx = (p1.x - p0.x);
|
||||
var dy = (p1.y - p0.y);
|
||||
var swipe_radius = 20;
|
||||
if ((dx * dx) + (dy * dy) >= (swipe_radius * swipe_radius)) {
|
||||
|
||||
// You've moved your finger far enough away from the origin that
|
||||
// we're sure you mean to swipe, scroll, or drag, not just tap.
|
||||
// Decide if you're trying to scroll or swipe. If your finger's
|
||||
// motion has been primarily vertical, assume this is a scroll. If
|
||||
// your finger's motion has been primarily horizontal, assume this
|
||||
// is a swipe.
|
||||
if (dy * dy >= dx * dx) {
|
||||
stop_swipe();
|
||||
return;
|
||||
}
|
||||
swiping = true;
|
||||
JX.DOM.invoke(target, 'gesture.swipe.start', get_swipe_data());
|
||||
}
|
||||
}
|
||||
if (swiping) {
|
||||
if (!e.getNode('touchable')) {
|
||||
p1 = JX.$V(p0);
|
||||
}
|
||||
JX.DOM.invoke(target, 'gesture.swipe.move', get_swipe_data());
|
||||
e.prevent();
|
||||
}
|
||||
});
|
||||
|
||||
JX.Stratcom.listen(
|
||||
['touchend', 'touchcancel', 'mouseup'],
|
||||
null,
|
||||
function(e) {
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: Clear the event state first so we don't keep swiping if a
|
||||
// handler throws.
|
||||
var event_target = target;
|
||||
var event_data = get_swipe_data();
|
||||
stop_swipe();
|
||||
|
||||
JX.DOM.invoke(event_target, 'gesture.swipe.end', event_data);
|
||||
});
|
||||
|
||||
function get_swipe_data() {
|
||||
var dir = (p1.x > p0.x) ? 'left' : 'right';
|
||||
var length = Math.abs(p1.x - p0.x);
|
||||
|
||||
return {
|
||||
p0: p0,
|
||||
p1: p1,
|
||||
dir: dir,
|
||||
length: length
|
||||
};
|
||||
}
|
||||
|
||||
function stop_swipe() {
|
||||
target = null;
|
||||
swiping = false;
|
||||
}
|
||||
|
||||
});
|
85
webroot/rsrc/js/application/uiexample/gesture-example.js
Normal file
85
webroot/rsrc/js/application/uiexample/gesture-example.js
Normal file
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @requires javelin-stratcom
|
||||
* javelin-behavior
|
||||
* javelin-vector
|
||||
* javelin-dom
|
||||
* @provides javelin-behavior-phabricator-gesture-example
|
||||
*/
|
||||
|
||||
JX.behavior('phabricator-gesture-example', function(config) {
|
||||
|
||||
var strokes = [];
|
||||
var current = [];
|
||||
|
||||
var root = JX.$(config.rootID);
|
||||
|
||||
var canvas = JX.$N('canvas');
|
||||
var d = JX.Vector.getDim(root);
|
||||
canvas.width = d.x;
|
||||
canvas.height = d.y;
|
||||
root.appendChild(canvas);
|
||||
|
||||
var cxt = canvas.getContext('2d');
|
||||
JX.Stratcom.listen(
|
||||
'gesture.swipe.end',
|
||||
null,
|
||||
function(e) {
|
||||
var stroke = get_stroke(e);
|
||||
strokes.push(stroke);
|
||||
current = [];
|
||||
redraw();
|
||||
});
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'gesture.swipe.move',
|
||||
null,
|
||||
function(e) {
|
||||
var stroke = get_stroke(e);
|
||||
current = [stroke];
|
||||
redraw();
|
||||
});
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'gesture.swipe.cancel',
|
||||
null,
|
||||
function(e) {
|
||||
current = [];
|
||||
redraw();
|
||||
});
|
||||
|
||||
function get_stroke(e) {
|
||||
var data = e.getData();
|
||||
var p = JX.$V(root);
|
||||
return [
|
||||
data.p0.x - p.x,
|
||||
data.p0.y - p.y,
|
||||
data.p1.x - p.x,
|
||||
data.p1.y - p.y
|
||||
];
|
||||
}
|
||||
|
||||
function redraw() {
|
||||
cxt.fillStyle = '#dfdfdf';
|
||||
cxt.fillRect(0, 0, d.x, d.y);
|
||||
|
||||
for (var ii = 0; ii < strokes.length; ii++) {
|
||||
var s = strokes[ii];
|
||||
cxt.strokeStyle = 'rgba(0, 0, 0, 0.50)';
|
||||
cxt.beginPath();
|
||||
cxt.moveTo(s[0], s[1]);
|
||||
cxt.lineTo(s[2], s[3]);
|
||||
cxt.stroke();
|
||||
}
|
||||
|
||||
for (var ii = 0; ii < current.length; ii++) {
|
||||
var s = current[ii];
|
||||
cxt.strokeStyle = 'rgba(255, 0, 0, 1)';
|
||||
cxt.beginPath();
|
||||
cxt.moveTo(s[0], s[1]);
|
||||
cxt.lineTo(s[2], s[3]);
|
||||
cxt.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
redraw();
|
||||
});
|
Loading…
Reference in a new issue