keynav.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. define(function(require, exports, module) {
  2. var kity = require('../core/kity');
  3. var utils = require('../core/utils');
  4. var keymap = require('../core/keymap');
  5. var Minder = require('../core/minder');
  6. var MinderNode = require('../core/node');
  7. var Command = require('../core/command');
  8. var Module = require('../core/module');
  9. var Renderer = require('../core/render');
  10. Module.register('KeyboardModule', function() {
  11. var min = Math.min,
  12. max = Math.max,
  13. abs = Math.abs,
  14. sqrt = Math.sqrt,
  15. exp = Math.exp;
  16. function buildPositionNetwork(root) {
  17. var pointIndexes = [],
  18. p;
  19. root.traverse(function(node) {
  20. p = node.getLayoutBox();
  21. // bugfix: 不应导航到收起的节点(判断其尺寸是否存在)
  22. if (p.width && p.height) {
  23. pointIndexes.push({
  24. left: p.x,
  25. top: p.y,
  26. right: p.x + p.width,
  27. bottom: p.y + p.height,
  28. width: p.width,
  29. height: p.height,
  30. node: node
  31. });
  32. }
  33. });
  34. for (var i = 0; i < pointIndexes.length; i++) {
  35. findClosestPointsFor(pointIndexes, i);
  36. }
  37. }
  38. // 这是金泉的点子,赞!
  39. // 求两个不相交矩形的最近距离
  40. function getCoefedDistance(box1, box2) {
  41. var xMin, xMax, yMin, yMax, xDist, yDist, dist, cx, cy;
  42. xMin = min(box1.left, box2.left);
  43. xMax = max(box1.right, box2.right);
  44. yMin = min(box1.top, box2.top);
  45. yMax = max(box1.bottom, box2.bottom);
  46. xDist = xMax - xMin - box1.width - box2.width;
  47. yDist = yMax - yMin - box1.height - box2.height;
  48. if (xDist < 0) dist = yDist;
  49. else if (yDist < 0) dist = xDist;
  50. else dist = sqrt(xDist * xDist + yDist * yDist);
  51. var node1 = box1.node;
  52. var node2 = box2.node;
  53. // sibling
  54. if (node1.parent == node2.parent) {
  55. dist /= 10;
  56. }
  57. // parent
  58. if (node2.parent == node1) {
  59. dist /= 5;
  60. }
  61. return dist;
  62. }
  63. function findClosestPointsFor(pointIndexes, iFind) {
  64. var find = pointIndexes[iFind];
  65. var most = {},
  66. quad;
  67. var current, dist;
  68. for (var i = 0; i < pointIndexes.length; i++) {
  69. if (i == iFind) continue;
  70. current = pointIndexes[i];
  71. dist = getCoefedDistance(current, find);
  72. // left check
  73. if (current.right < find.left) {
  74. if (!most.left || dist < most.left.dist) {
  75. most.left = {
  76. dist: dist,
  77. node: current.node
  78. };
  79. }
  80. }
  81. // right check
  82. if (current.left > find.right) {
  83. if (!most.right || dist < most.right.dist) {
  84. most.right = {
  85. dist: dist,
  86. node: current.node
  87. };
  88. }
  89. }
  90. // top check
  91. if (current.bottom < find.top) {
  92. if (!most.top || dist < most.top.dist) {
  93. most.top = {
  94. dist: dist,
  95. node: current.node
  96. };
  97. }
  98. }
  99. // bottom check
  100. if (current.top > find.bottom) {
  101. if (!most.down || dist < most.down.dist) {
  102. most.down = {
  103. dist: dist,
  104. node: current.node
  105. };
  106. }
  107. }
  108. }
  109. find.node._nearestNodes = {
  110. right: most.right && most.right.node || null,
  111. top: most.top && most.top.node || null,
  112. left: most.left && most.left.node || null,
  113. down: most.down && most.down.node || null
  114. };
  115. }
  116. function navigateTo(km, direction) {
  117. var referNode = km.getSelectedNode();
  118. if (!referNode) {
  119. km.select(km.getRoot());
  120. buildPositionNetwork(km.getRoot());
  121. return;
  122. }
  123. if (!referNode._nearestNodes) {
  124. buildPositionNetwork(km.getRoot());
  125. }
  126. var nextNode = referNode._nearestNodes[direction];
  127. if (nextNode) {
  128. km.select(nextNode, true);
  129. }
  130. }
  131. // 稀释用
  132. var lastFrame;
  133. return {
  134. 'events': {
  135. 'layoutallfinish': function() {
  136. var root = this.getRoot();
  137. buildPositionNetwork(root);
  138. },
  139. 'normal.keydown readonly.keydown': function(e) {
  140. var minder = this;
  141. ['left', 'right', 'up', 'down'].forEach(function(key) {
  142. if (e.isShortcutKey(key)) {
  143. navigateTo(minder, key == 'up' ? 'top' : key);
  144. e.preventDefault();
  145. }
  146. });
  147. }
  148. }
  149. };
  150. });
  151. });