Implemented first version of key-board control over action-objects. For now arrow/page up/down and CTRL-A works.

Known Issues:
- Keyboard navigation in egw_grids (like felamimail) may not work correctly when navigating to not-yet loaded items (current implementation for that is more a hack)
- short-cuts not yet implemented
- not tested in IE, Safari, FF 3.6
- felamimail sometimes loads all/many of the emails you were navigating over
This commit is contained in:
Andreas Stöckel 2011-06-12 16:41:40 +00:00
parent 49ece38c6c
commit e03d416fda
3 changed files with 197 additions and 6 deletions

View File

@ -1027,7 +1027,8 @@ egwActionObject.prototype.getIndex = function()
*/
egwActionObject.prototype.getFocusedObject = function()
{
var cr = this.getContainerRoot();
/*var cr = this.getContainerRoot();*/
var cr = this.getRootObject();
return cr ? cr.focusedChild : null;
}
@ -1113,6 +1114,115 @@ egwActionObject.prototype._ifaceCallback = function(_newState, _changedBit, _shi
return _newState;
}
/**
* Handler for key presses
*/
egwActionObject.prototype.handleKeyPress = function(_keyCode, _shift, _ctrl, _alt) {
switch (_keyCode) {
case EGW_KEY_ARROW_UP:
case EGW_KEY_ARROW_DOWN:
case EGW_KEY_PAGE_UP:
case EGW_KEY_PAGE_DOWN:
var intval =
(_keyCode == EGW_KEY_ARROW_UP || _keyCode == EGW_KEY_ARROW_DOWN) ?
1 : 10;
if (this.children.length > 0)
{
// Get the focused object
var focused = this.getFocusedObject();
// Determine the object which should get selected
var selObj = null;
if (!focused)
{
selObj = this.children[0];
}
else
{
selObj = (_keyCode == EGW_KEY_ARROW_UP || _keyCode == EGW_KEY_PAGE_UP) ?
focused.getPrevious(intval) : focused.getNext(intval);
}
if (selObj != null)
{
if (!_shift)
{
this.setAllSelected(false);
}
else
{
var objs = focused.traversePath(selObj);
for (var i = 0; i < objs.length; i++)
{
objs[i].setSelected(true);
}
}
selObj.setSelected(true);
selObj.setFocused(true);
// Tell the aoi of the object to make it visible
selObj.makeVisible();
}
return true;
}
break;
// Handle CTRL-A to select all elements in the current container
case EGW_KEY_A:
if (_ctrl)
{
this.setAllSelected(true);
return true;
}
break;
}
return false;
}
egwActionObject.prototype.getPrevious = function(_intval)
{
if (this.parent != null)
{
var idx = this.parent.children.indexOf(this);
if (idx > 0)
{
idx = Math.max(0, idx - _intval);
return this.parent.children[idx];
}
else
{
// TODO: Implement traversal of trees
}
}
return this;
}
egwActionObject.prototype.getNext = function(_intval)
{
if (this.parent != null)
{
var idx = this.parent.children.indexOf(this);
if (idx < this.parent.children.length - 1)
{
idx = Math.min(this.parent.children.length - 1, idx + _intval);
return this.parent.children[idx];
}
else
{
// TODO: Implement traversal of trees
}
}
return this;
}
/**
* Returns whether the object is currently selected.
*/
@ -1175,6 +1285,11 @@ egwActionObject.prototype.setFocused = function(_focused)
this.parent.updateFocusedChild(this, _focused);
}
}
if (this.focusedChild != null && _focused == false)
{
this.focusedChild.setFocused(false);
}
}
/**
@ -1289,7 +1404,7 @@ egwActionObject.prototype.updateFocusedChild = function(_child, _focused)
}
}
if (this.parent && !egwBitIsSet(this.flags, EGW_AO_FLAG_IS_CONTAINER))
if (this.parent /*&& !egwBitIsSet(this.flags, EGW_AO_FLAG_IS_CONTAINER)*/)
{
this.parent.updateFocusedChild(_child, _focused);
}
@ -1411,6 +1526,15 @@ egwActionObject.prototype.triggerCallback = function()
return true;
}
/**
* Calls the corresponding function of the AOI which tries to make the object
* visible.
*/
egwActionObject.prototype.makeVisible = function()
{
this.iface.makeVisible();
}
var EGW_AO_EXEC_SELECTED = 0;
var EGW_AO_EXEC_THIS = 1;
@ -1660,6 +1784,8 @@ function egwActionObjectInterface()
// or EGW_AI_DRAG_OUT
this.doTriggerEvent = function(_event, _data) {return false;}
this.doMakeVisible = function() {};
this._state = EGW_AO_STATE_NORMAL || EGW_AO_STATE_VISIBLE;
this.stateChangeCallback = null;
@ -1778,8 +1904,13 @@ egwActionObjectInterface.prototype.triggerEvent = function(_event, _data)
return this.doTriggerEvent(_event, _data);
}
/**
* Scrolls the element into a visble area if it is currently hidden
*/
egwActionObjectInterface.prototype.makeVisible = function()
{
return this.doMakeVisible();
}
/** -- egwActionObjectDummyInterface Class -- **/

View File

@ -445,8 +445,10 @@ egwGridDataElement.prototype.insertElement = function(_index, _id)
null);
element.index = _index;
// Create the action object
var object = this.actionObject.insertObject(_index, _id, null, 0);
// Create the action object with a temporary AOI which can be used to make
// the object visible
var object = this.actionObject.insertObject(_index, _id,
new egwGridTmpAOI(this.getRootElement().gridObject, _index), 0);
object.data = element;
// Link the two together

View File

@ -127,6 +127,8 @@ function egwGridViewOuter(_parentNode, _dataRoot, _selectColsCallback, _toggleAl
// Insert the base grid container into the DOM-Tree
this.grid = new egwGridViewGrid(null, null, true, this); // (No parent grid, no height change callback, scrollable)
this.grid.insertIntoDOM(this.outer_tr, []);
this.dataRoot.gridObject = this.grid;
}
/**
@ -1435,6 +1437,7 @@ function egwGridViewRow_aoiSetup()
this.aoi.row = this;
this.aoi.doSetState = egwGridViewRow_aoiSetState;
this.aoi.doTriggerEvent = egwGridViewRow_aoiTriggerEvent;
this.aoi.doMakeVisible = egwGridViewRow_aoiMakeVisible;
this.aoi.getDOMNode = egwGridViewRow_aoiGetDOMNode;
}
@ -1473,6 +1476,11 @@ function egwGridViewRow_aoiTriggerEvent(_event, _data)
}
}
function egwGridViewRow_aoiMakeVisible()
{
egwGridView_scrollToArea(this.row.grid.scrollarea, this.row.getArea());
}
/**
* Returns the actionObjectInterface object of this grid item.
*/
@ -2041,4 +2049,54 @@ function egwGridViewFullRow_doSetViewArea()
//
}
/**
* Temporary AOI which has to be assigned to invisible grid objects in order
* to give them the possiblity to make them visible when using e.g. keyboard navigation
*/
function egwGridTmpAOI(_grid, _index)
{
var aoi = new egwActionObjectDummyInterface();
// Assign the make visible function
aoi.grid = _grid;
aoi.index = _index;
aoi.doMakeVisible = egwGridTmpAOI_makeVisible;
return aoi;
}
function egwGridTmpAOI_makeVisible()
{
// Assume an area for the element (this code is not optimal, but it should
// work in most cases - problem is that the elements in the grid may have equal
// sizes and the grid is scrolled to some area where the element is not)
// TODO: Support for trees
var avgHeight = this.grid.getOuter().avgRowHeight;
var area = egwArea(this.index * avgHeight, avgHeight);
egwGridView_scrollToArea(this.grid.scrollarea, area);
}
function egwGridView_scrollToArea(_scrollarea, _visarea)
{
// Get the current view area
var va = egwArea(_scrollarea.scrollTop(), _scrollarea.height());
// Calculate the assumed position of this element
var pos = _visarea;
// Check whether it is currently (completely) visible, if not scroll the
// scroll area to that position
if (!(pos.top >= va.top && pos.bottom <= va.bottom))
{
if (pos.top < va.top)
{
_scrollarea.scrollTop(pos.top);
}
else
{
_scrollarea.scrollTop(va.top + pos.bottom - va.bottom);
}
}
}