PDA

View Full Version : [CLOSED] Keyboard Navigation in GridPanel with CheckboxselectionModel,



matt
Dec 25, 2014, 1:32 PM
Hi,

Quick questions related to "KeyNav = true" and "Multi" selection mode.

1. By default when using Keyboard navigation mode, the selected/focused row is checked.
- I would like the selected row not to be checked by default.

2. Keyboard navigation always clear current checked items.
- I would like the keyboard navigation keep current checked rows.


You can emulate my desired behavior (point 2). by holding control key and navigating. (shift is still working, space is still checking/unchecking rows and current selection stays if we move from row to row by keyboard)

Thank you

Daniil
Dec 25, 2014, 2:32 PM
Hi Matt,

As far as I can see there are no such settings to get the behavior that you need.

The logic is in the Ext.selection.Model's afterKeyNavigate method. You can investigate and could try to override.
http://docs-origin.sencha.com/extjs/4.2.1/source/Model2.html#Ext-selection-Model-method-afterKeyNavigate

matt
Dec 25, 2014, 7:04 PM
Daniil, Thank you for the hint.

So simple to implement it once you really get into sencha source code. :)
Implemented my behavior. If anyone needs help with that, please ask, I will post the changes.

matrixwebtech
Dec 26, 2014, 2:41 AM
hi @matt
please post the sample.may be this is helpful for others.

matt
Jan 06, 2015, 10:43 AM
Hi,

Sorry for late answer... but ExtJs 5 gave me a bit of headache :)

So my solution is based on the idea of emulating Ctrl key pressed all the time. I took two methods from ExtJs4 source code of the selection model:

- afterKeyNavigate
- selectWithEvent

I have added bool member which I called "freeSelection" that allow to turn off/on that feature. It is not proper override of the selection but a bit of hack (I needed that feature only for one grid panel in my app so did not override whole selection model class)


Mainly I have changed e.ctrlKey to e.ctrlKey || me.freeSelection.



Ext.define("MyApp.MyGrid.MySelection", {

// Private
// Called after a new record has been navigated to by a keystroke.
// Event is passed so that shift and ctrl can be handled.
afterKeyNavigate: function (e, record) {
var me = this,
recIdx,
fromIdx,
isSelected = me.isSelected(record),
from = (me.selectionStart && me.isSelected(me.lastFocused)) ? me.selectionStart : (me.selectionStart = me.lastFocused),
key = e.getCharCode(),
isSpace = key === e.SPACE,
direction = key === e.UP || key === e.PAGE_UP ? 'up' : (key === e.DOWN || key === e.DOWN ? 'down' : null);

switch (me.selectionMode) {
case 'MULTI':

if (isSpace) {
// SHIFT+SPACE, select range
if (e.shiftKey) {
me.selectRange(from, record, e.ctrlKey || me.freeSelection);
} else {
// SPACE pessed on a selected item: deselect but leave it focused.
// e.ctrlKey means "keep existing"
if (isSelected) {
me.doDeselect(record, e.ctrlKey || me.freeSelection);

// This record is already focused. To get the focus effect put on it (as opposed to selected)
// we have to focus null first.
me.setLastFocused(null);
me.setLastFocused(record);
}
// SPACE on an unselected item: select it
else {
me.doSelect(record, e.ctrlKey || me.freeSelection);
}
}
}

// SHIFT-navigate selects intervening rows from the last selected (or last focused) item and target item
else if (e.shiftKey && from) {

// If we are going back *into* the selected range, we deselect.
fromIdx = me.store.indexOf(from);
recIdx = me.store.indexOf(record);

// If we are heading back TOWARDS the start rec - deselect skipped range...
if (direction === 'up' && fromIdx <= recIdx) {
me.deselectRange(me.lastFocused, recIdx + 1);
}
else if (direction === 'down' && fromIdx >= recIdx) {
me.deselectRange(me.lastFocused, recIdx - 1);
}

// If we are heading AWAY from start point, or no CTRL key, so just select the range and let the CTRL control "keepExisting"...
else if (from !== record) {
me.selectRange(from, record, e.ctrlKey || me.freeSelection);
}
me.lastSelected = record;
me.setLastFocused(record);
}

// CTRL-navigate onto a selected item just focuses it
else if ((e.ctrlKey || me.freeSelection) && isSelected) {
me.setLastFocused(record);
}

// CTRL-navigate, just move focus
else if (e.ctrlKey || me.freeSelection) {
me.setLastFocused(record);
}

// Just navigation - select the target
else {
me.doSelect(record, false);
}
break;
case 'SIMPLE':
if (isSelected) {
me.doDeselect(record);
} else {
me.doSelect(record, true);
}
break;
case 'SINGLE':
// Space hit
if (isSpace) {
if (isSelected) {
me.doDeselect(record);
me.setLastFocused(record);
} else {
me.doSelect(record);
}
}

// CTRL-navigation: just move focus
else if (e.ctrlKey || me.freeSelection) {
me.setLastFocused(record);
}

// if allowDeselect is on and this record isSelected, deselect it
else if (me.allowDeselect && isSelected) {
me.doDeselect(record);
}

// select the record and do NOT maintain existing selections
else {
me.doSelect(record, false);
}
break;
}

// selectionStart is a start point for shift/mousedown to create a range from.
// If the mousedowned record was not already selected, then it becomes the
// start of any range created from now on.
// If we drop to no records selected, then there is no range start any more.
if (!e.shiftKey) {
if (me.isSelected(record)) {
me.selectionStart = record;
}
}
},



// Provides differentiation of logic between MULTI, SIMPLE and SINGLE
// selection modes. Requires that an event be passed so that we can know
// if user held ctrl or shift.
selectWithEvent: function (record, e) {
var me = this,
isSelected = me.isSelected(record),
shift = e.shiftKey,
ctrl = e.ctrlKey,
start = me.selectionStart,
selected = me.getSelection(),
len = selected.length,
allowDeselect = me.allowDeselect,
toDeselect, i, item;

switch (me.selectionMode) {
case 'MULTI':
if (shift && start) {
me.selectRange(start, record, ctrl);
} else if (ctrl && isSelected) {
me.doDeselect(record, false);
} else if (ctrl) {
me.doSelect(record, true, false);
} else if (isSelected && !shift && !ctrl && len > 1) {

if (!me.freeSelection) {

toDeselect = [];

for (i = 0; i < len; ++i) {
item = selected[i];
if (item !== record) {
toDeselect.push(item);
}
}

me.doDeselect(toDeselect);
}
} else if (!isSelected) {

if (!me.freeSelection) {
me.doSelect(record, false);
}
else {
me.setLastFocused(record);
}
}
break;
case 'SIMPLE':
if (isSelected) {
me.doDeselect(record);
} else {
me.doSelect(record, true);
}
break;
case 'SINGLE':
if (allowDeselect && !ctrl) {
allowDeselect = me.toggleOnClick;
}
if (allowDeselect && isSelected) {
me.doDeselect(record);
} else {
me.doSelect(record, false);
}
break;
}

// selectionStart is a start point for shift/mousedown to create a range from.
// If the mousedowned record was not already selected, then it becomes the
// start of any range created from now on.
// If we drop to no records selected, then there is no range start any more.
if (!shift) {
if (me.isSelected(record)) {
me.selectionStart = record;
} else {
me.selectionStart = null;
}
}
}
});



Once you have the code in place I replace these methods in selection model:



var selection = myGrid.getSelectionModel();

selection.freeSelection = true;
selection.afterKeyNavigate = me.afterKeyNavigate;
selection.selectWithEvent = me.selectWithEvent;


"me" is instance of MyApp.MyGrid.MySelection


The solution is kind of workaround... and I have not tested it thoroughly yet as I have decided to move on to ExtJS5. Once I have got this done (maybe in a better way) in ExtJS 5 I will create thread in Ext.Net 3.0.

Hope that you give you some head start how to archive the desired effect.