-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathKeyboardNavigationManager.js
More file actions
151 lines (128 loc) · 4.2 KB
/
KeyboardNavigationManager.js
File metadata and controls
151 lines (128 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import SortedList from '../algorithms/SortedList';
import getMinimumCrossingRows from '../algorithms/getMinimumCrossingRows';
import TreeLevels from '../algorithms/TreeLevels';
import Rect from '../graphics/structs/Rect';
export default function KeyboardNavigationManager() {
var /*
Rectangles of the layout. Every rectangle has context property set to itemid.
*/
_placements = [],
/*
This is sorted list of horizontal lines user may navigate along them and across with arrow keys between rectangles of the layout
Every rectangle may belong to multiple rows, so rows selection is optimized to minimize their number
*/
_rows,
/*
Tree levels structure is collection of colelctions. Its level contains sorted list of rectangles cross by individual row
*/
_treeLevels,
/*
Current itemid and row. Every rectangle may belong to multiple rows, so this structure helps to stay within row during navigation.
*/
_cursor = null;
function Cursor(itemid, row) {
this.itemid = itemid;
this.row = row;
}
function addRect(rect, itemid) {
var newRect = new Rect(rect);
newRect.context = itemid;
_placements.push(newRect);
}
function prepair() {
if (_treeLevels == null) {
var levelIndex = 0;
_rows = SortedList();
getMinimumCrossingRows(this, _placements, function (row) {
_rows.add(row, levelIndex);
levelIndex += 1;
});
_treeLevels = TreeLevels();
_placements.sort(function (a, b) {
return a.horizontalCenter() - b.horizontalCenter();
});
for (var index = 0, len = _placements.length; index < len; index += 1) {
var placement = _placements[index];
_rows.loopForward(this, placement.y, function (row, levelIndex) {
if (row > placement.bottom()) {
return true;
}
_treeLevels.addItem(levelIndex, placement.context, placement);
});
}
}
}
function getCursor(itemid) {
prepair();
if (_cursor == null || _cursor.itemid != itemid) {
_cursor = new Cursor(itemid, _treeLevels.getLevelIndex(itemid));
}
return _cursor;
}
function getItemAbove(itemid) {
_cursor = getCursor(itemid);
moveCursorNextRow(false);
return _cursor.itemid;
}
function getItemBelow(itemid) {
_cursor = getCursor(itemid);
moveCursorNextRow(true);
return _cursor.itemid;
}
function moveCursorNextRow(isBelow) {
var cursorItemRect = _treeLevels.getItemContext(_cursor.itemid);
var cursorCenter = cursorItemRect.horizontalCenter();
var previousCursorItem = _cursor.itemid;
_treeLevels.loopLevelsFromItem(this, _cursor.itemid, isBelow, function (levelIndex) {
_cursor.row = levelIndex;
_cursor.itemid = _treeLevels.binarySearch(this, levelIndex, function (itemid, placement) {
return cursorCenter - placement.horizontalCenter();
});
return true;
});
if (previousCursorItem == _cursor.itemid) {
if (isBelow) {
_cursor.row = _treeLevels.getEndLevelIndex(_cursor.itemid);
} else {
_cursor.row = _treeLevels.getLevelIndex(_cursor.itemid);
}
}
}
function getItemOnLeft(itemid) {
_cursor = getCursor(itemid);
var nextItem = _treeLevels.getPrevItem(_cursor.itemid, _cursor.row);
if (nextItem != null) {
_cursor.itemid = nextItem;
}
return _cursor.itemid;
}
function getItemOnRight(itemid) {
_cursor = getCursor(itemid);
var nextItem = _treeLevels.getNextItem(_cursor.itemid, _cursor.row);
if (nextItem != null) {
_cursor.itemid = nextItem;
}
return _cursor.itemid;
}
function getNavigationLevels() {
prepair();
var result = [];
_treeLevels.loopLevels(this, function (levelIndex, level) {
var levelItems = [];
_treeLevels.loopLevelItems(this, levelIndex, function (itemid, item, position) {
levelItems.push(itemid);
});
result.push(levelItems);
});
return result;
}
return {
addRect: addRect,
prepair: prepair,
getItemAbove: getItemAbove,
getItemBelow: getItemBelow,
getItemOnLeft: getItemOnLeft,
getItemOnRight: getItemOnRight,
getNavigationLevels: getNavigationLevels
};
};