Last active
November 16, 2018 21:05
-
-
Save branflake2267/5bd1150591ee56cc9c7b29f7d9b8e6fa to your computer and use it in GitHub Desktop.
GXT 4.0.3 workaround for touch not firing selection or click in the grid. Android, iOS, And windows edge, grid selection issue solution. Fix in 4.0.4.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.sencha.gxt.widget.core.client.grid; | |
| import java.util.Collections; | |
| import com.google.gwt.core.client.Scheduler; | |
| import com.google.gwt.core.client.Scheduler.ScheduledCommand; | |
| import com.google.gwt.dom.client.Element; | |
| import com.google.gwt.dom.client.NativeEvent; | |
| import com.google.gwt.event.dom.client.KeyCodes; | |
| import com.google.gwt.user.client.Event; | |
| import com.sencha.gxt.core.client.Style.SelectionMode; | |
| import com.sencha.gxt.core.client.util.KeyNav; | |
| import com.sencha.gxt.core.shared.event.GroupingHandlerRegistration; | |
| import com.sencha.gxt.data.shared.ListStore; | |
| import com.sencha.gxt.data.shared.Store; | |
| import com.sencha.gxt.data.shared.event.StoreRecordChangeEvent; | |
| import com.sencha.gxt.widget.core.client.event.RefreshEvent; | |
| import com.sencha.gxt.widget.core.client.event.RefreshEvent.RefreshHandler; | |
| import com.sencha.gxt.widget.core.client.event.RowClickEvent; | |
| import com.sencha.gxt.widget.core.client.event.RowClickEvent.RowClickHandler; | |
| import com.sencha.gxt.widget.core.client.event.RowMouseDownEvent; | |
| import com.sencha.gxt.widget.core.client.event.RowMouseDownEvent.RowMouseDownHandler; | |
| import com.sencha.gxt.widget.core.client.event.RowMouseUpEvent; | |
| import com.sencha.gxt.widget.core.client.event.RowMouseUpEvent.RowMouseUpHandler; | |
| import com.sencha.gxt.widget.core.client.event.RowTapEvent; | |
| import com.sencha.gxt.widget.core.client.event.RowTapEvent.RowTapHandler; | |
| import com.sencha.gxt.widget.core.client.event.ViewReadyEvent; | |
| import com.sencha.gxt.widget.core.client.event.ViewReadyEvent.ViewReadyHandler; | |
| import com.sencha.gxt.widget.core.client.event.XEvent; | |
| import com.sencha.gxt.widget.core.client.grid.Grid.Callback; | |
| import com.sencha.gxt.widget.core.client.grid.Grid.GridCell; | |
| import com.sencha.gxt.widget.core.client.selection.AbstractStoreSelectionModel; | |
| /** | |
| * Grid selection model. | |
| * | |
| * <dl> | |
| * <dt>Inherited Events:</dt> | |
| * <dd>AbstractStoreSelectionModel BeforeSelect</dd> | |
| * <dd>AbstractStoreSelectionModel SelectionChange</dd> | |
| * </dl> | |
| */ | |
| public class GridSelectionModel<M> extends AbstractStoreSelectionModel<M> { | |
| /** | |
| * Determines whether a given cell is selectable. | |
| */ | |
| public static class SelectionModelCallback implements Callback { | |
| protected GridSelectionModel<?> sm; | |
| /** | |
| * Creates a selection model callback that determines whether a given cell is selectable. | |
| * | |
| * @param sm the selection model | |
| */ | |
| public SelectionModelCallback(GridSelectionModel<?> sm) { | |
| this.sm = sm; | |
| } | |
| @Override | |
| public boolean isSelectable(GridCell cell) { | |
| return sm.isSelectable(cell.getRow(), cell.getCol()); | |
| } | |
| } | |
| protected class Handler implements RowMouseDownHandler, RowMouseUpHandler, RowClickHandler, | |
| ViewReadyHandler, RefreshHandler, RowTapHandler { | |
| @Override | |
| public void onRefresh(RefreshEvent event) { | |
| refresh(); | |
| if (getLastFocused() != null) { | |
| grid.getView().onHighlightRow(listStore.indexOf(getLastFocused()), true); | |
| } | |
| } | |
| @Override | |
| public void onRowClick(RowClickEvent event) { | |
| GridSelectionModel.this.onRowClick(event); | |
| } | |
| @Override | |
| public void onRowMouseDown(RowMouseDownEvent event) { | |
| GridSelectionModel.this.onRowMouseDown(event); | |
| } | |
| @Override | |
| public void onRowMouseUp(RowMouseUpEvent event) { | |
| GridSelectionModel.this.onRowMouseUp(event); | |
| } | |
| @Override | |
| public void onViewReady(ViewReadyEvent event) { | |
| refresh(); | |
| } | |
| @Override | |
| public void onRowTap(RowTapEvent event) { | |
| GridSelectionModel.this.onRowTap(event); | |
| } | |
| } | |
| /** | |
| * True if this grid selection model supports keyboard navigation (defaults to true). | |
| */ | |
| protected boolean enableNavKeys = true; | |
| /** | |
| * The grid associated with this selection model. | |
| */ | |
| protected Grid<M> grid; | |
| /** | |
| * The current keyboard navigator. | |
| */ | |
| protected KeyNav keyNav = new KeyNav() { | |
| @Override | |
| public void onDown(NativeEvent e) { | |
| GridSelectionModel.this.onKeyDown(e); | |
| } | |
| @Override | |
| public void onKeyPress(NativeEvent ce) { | |
| GridSelectionModel.this.onKeyPress(ce); | |
| } | |
| @Override | |
| public void onLeft(NativeEvent e) { | |
| GridSelectionModel.this.onKeyLeft(e); | |
| } | |
| @Override | |
| public void onRight(NativeEvent e) { | |
| GridSelectionModel.this.onKeyRight(e); | |
| } | |
| @Override | |
| public void onUp(NativeEvent e) { | |
| GridSelectionModel.this.onKeyUp(e); | |
| } | |
| }; | |
| /** | |
| * The list store for this selection model. | |
| */ | |
| protected ListStore<M> listStore; | |
| /** | |
| * A group of handler registrations for this selection model. | |
| */ | |
| protected GroupingHandlerRegistration handlerRegistration; | |
| /** | |
| * Track the selection index, when the shift combined with and so then this is the starting point of the selection. | |
| */ | |
| protected int indexOnSelectNoShift; | |
| /** | |
| * True to deselect a selected item on click (defaults to true). | |
| */ | |
| protected boolean deselectOnSimpleClick = true; | |
| protected boolean focusCellCalled; | |
| protected Handler handler = new Handler(); | |
| @Override | |
| public void bind(Store<M> store) { | |
| super.bind(store); | |
| if (store instanceof ListStore) { | |
| listStore = (ListStore<M>) store; | |
| } else { | |
| listStore = null; | |
| } | |
| } | |
| /** | |
| * Binds the given grid to this selection model. | |
| * | |
| * @param grid the grid to bind to this selection model | |
| */ | |
| public void bindGrid(Grid<M> grid) { | |
| if (this.grid != null) { | |
| if (handlerRegistration != null) { | |
| handlerRegistration.removeHandler(); | |
| handlerRegistration = null; | |
| } | |
| keyNav.bind(null); | |
| bind(null); | |
| } | |
| this.grid = grid; | |
| if (grid != null) { | |
| if (handlerRegistration == null) { | |
| handlerRegistration = new GroupingHandlerRegistration(); | |
| } | |
| handlerRegistration.add(grid.addRowMouseDownHandler(handler)); | |
| handlerRegistration.add(grid.addRowMouseUpHandler(handler)); | |
| handlerRegistration.add(grid.addRowTapHandler(handler)); | |
| handlerRegistration.add(grid.addRowClickHandler(handler)); | |
| handlerRegistration.add(grid.addViewReadyHandler(handler)); | |
| handlerRegistration.add(grid.addRefreshHandler(handler)); | |
| keyNav.bind(grid); | |
| bind(grid.getStore()); | |
| } | |
| } | |
| /** | |
| * Returns the currently bound grid. | |
| * | |
| * @return the grid | |
| */ | |
| public Grid<M> getGrid() { | |
| return grid; | |
| } | |
| /** | |
| * Selects the next row. | |
| * | |
| * @param keepexisting true to keep existing selections | |
| */ | |
| public void selectNext(boolean keepexisting) { | |
| if (hasNext()) { | |
| int idx = listStore.indexOf(lastSelected) + 1; | |
| select(idx, keepexisting); | |
| grid.getView().focusRow(idx); | |
| } | |
| } | |
| /** | |
| * Selects the previous row. | |
| * | |
| * @param keepexisting true to keep existing selections | |
| */ | |
| public void selectPrevious(boolean keepexisting) { | |
| if (hasPrevious()) { | |
| int idx = listStore.indexOf(lastSelected) - 1; | |
| select(idx, keepexisting); | |
| grid.getView().focusRow(idx); | |
| } | |
| } | |
| /** | |
| * Handles a row click event. The row click event is responsible for adding to a selection in multiple selection mode. | |
| * | |
| * @param event the row click event | |
| */ | |
| protected void onRowClick(RowClickEvent event) { | |
| if (fireSelectionChangeOnClick) { | |
| fireSelectionChange(); | |
| fireSelectionChangeOnClick = false; | |
| } | |
| } | |
| /** | |
| * Handles a row mouse up event. The row mouse down event is responsible for initiating a selection. | |
| * | |
| * @param event the row mouse down event | |
| * @since 4.0.3 | |
| */ | |
| protected void onRowMouseUp(RowMouseUpEvent event) { | |
| if (processSelectOnMouseUp) { | |
| onRowSelect(event.getEvent(), event.getRowIndex(), event.getColumnIndex()); | |
| } | |
| } | |
| /** | |
| * Handles a row mouse down event. The row mouse down event is responsible for initiating a selection. | |
| * | |
| * @param event the row mouse down event | |
| */ | |
| protected void onRowMouseDown(RowMouseDownEvent event) { | |
| if (!processSelectOnMouseUp) { | |
| onRowSelect(event.getEvent(), event.getRowIndex(), event.getColumnIndex()); | |
| } | |
| } | |
| /** | |
| * @param event The row tap event. | |
| * @since 4.0.3 | |
| */ | |
| public void onRowTap(RowTapEvent event) { | |
| if (fireSelectionChangeOnClick) { | |
| fireSelectionChange(); | |
| fireSelectionChangeOnClick = false; | |
| } | |
| onRowSelect(event.getEvent(), event.getRowIndex(), event.getColumnIndex()); | |
| } | |
| /** | |
| * Handles a row mouse down event. The row mouse down event is responsible for initiating a selection. | |
| * | |
| * @param event the row mouse down event | |
| * @since 4.0.3 | |
| */ | |
| protected void onRowSelect(Event event, int rowIndex, int colIndex) { | |
| if (Element.is(event.getEventTarget()) && !grid.getView().isSelectableTarget(Element.as(event.getEventTarget()))) { | |
| return; | |
| } | |
| if (isLocked()) { | |
| return; | |
| } | |
| if (rowIndex == -1) { | |
| deselectAll(); | |
| return; | |
| } | |
| focusCellCalled = false; | |
| mouseDown = true; | |
| XEvent e = event.<XEvent>cast(); | |
| // It is important the focusCell be called once, and only once in onRowMouseDown and onRowMouseClick | |
| // everything but multi select with the control key pressed is handled in mouse down | |
| if (event.getButton() == Event.BUTTON_RIGHT) { | |
| if (selectionMode != SelectionMode.SINGLE && isSelected(listStore.get(rowIndex))) { | |
| onRightClick(event, rowIndex, colIndex); | |
| return; | |
| } | |
| grid.getView().focusCell(rowIndex, colIndex, false); | |
| select(rowIndex, false); | |
| focusCellCalled = true; | |
| } else { | |
| M sel = listStore.get(rowIndex); | |
| if (sel == null) { | |
| return; | |
| } | |
| boolean isSelected = isSelected(sel); | |
| boolean isMeta = e.getCtrlOrMetaKey(); | |
| boolean isShift = event.getShiftKey(); | |
| switch (selectionMode) { | |
| case SIMPLE: | |
| grid.getView().focusCell(rowIndex, colIndex, false); | |
| focusCellCalled = true; | |
| if (!isSelected) { | |
| select(sel, true); | |
| } else if (isSelected && deselectOnSimpleClick) { | |
| deselect(sel); | |
| } | |
| break; | |
| case SINGLE: | |
| grid.getView().focusCell(rowIndex, colIndex, false); | |
| focusCellCalled = true; | |
| if (isSelected && isMeta) { | |
| deselect(sel); | |
| } else if (!isSelected) { | |
| select(sel, false); | |
| } | |
| break; | |
| case MULTI: | |
| if (isSelected && isMeta) { | |
| focusCellCalled = true; | |
| // reset the starting location of the click | |
| indexOnSelectNoShift = rowIndex; | |
| doDeselect(Collections.singletonList(sel), false); | |
| } else if (isMeta) { | |
| focusCellCalled = true; | |
| // reset the starting location of the click | |
| indexOnSelectNoShift = rowIndex; | |
| doSelect(Collections.singletonList(sel), true, false); | |
| } else if (isSelected && !isMeta && !isShift && selected.size() > 1) { | |
| // reset the starting location of the click | |
| indexOnSelectNoShift = rowIndex; | |
| doSelect(Collections.singletonList(sel), false, false); | |
| } else if (isShift && lastSelected != null) { | |
| int start; | |
| int end; | |
| // This deals with flipping directions | |
| if (indexOnSelectNoShift < rowIndex) { | |
| start = indexOnSelectNoShift; | |
| end = rowIndex; | |
| } else { | |
| start = rowIndex; | |
| end = indexOnSelectNoShift; | |
| } | |
| focusCellCalled = true; | |
| select(start, end, false); | |
| rowIndex = end; // focus on the last selected row | |
| } else if (!isSelected) { | |
| // reset the starting location of multi select | |
| indexOnSelectNoShift = rowIndex; | |
| focusCellCalled = true; | |
| doSelect(Collections.singletonList(sel), false, false); | |
| } | |
| if (!focusCellCalled) { | |
| grid.getView().focusCell(rowIndex, colIndex, false); | |
| } | |
| break; | |
| } | |
| } | |
| mouseDown = false; | |
| } | |
| /** | |
| * @since 4.0.3 | |
| */ | |
| protected void onRightClick(Event event, int rowIndex, int colIndex) { | |
| } | |
| /** | |
| * Returns true if there is an item in the list store following the most recently selected item (i.e. the selected | |
| * item is not the last item in the list store). | |
| * | |
| * @return true if there is an item following the most recently selected item | |
| */ | |
| protected boolean hasNext() { | |
| return lastSelected != null && listStore.indexOf(lastSelected) < (listStore.size() - 1); | |
| } | |
| /** | |
| * Returns true if there is an item in the list store preceding the most recently selected item (i.e. the selected | |
| * item is not the first item in the list store). | |
| * | |
| * @return true if there is an item preceding the most recently selected item | |
| */ | |
| protected boolean hasPrevious() { | |
| return lastSelected != null && listStore.indexOf(lastSelected) > 0; | |
| } | |
| /** | |
| * Returns true if the given cell is selectable. | |
| * | |
| * @param row the cell's row | |
| * @param cell the cell's column | |
| * @return true if the cell is selectable | |
| */ | |
| protected boolean isSelectable(int row, int cell) { | |
| return !grid.getColumnModel().isHidden(cell); | |
| } | |
| /** | |
| * Handles a key down event (e.g. as fired by the key navigator). | |
| * | |
| * @param nativeEvent the key down event | |
| */ | |
| protected void onKeyDown(NativeEvent nativeEvent) { | |
| XEvent e = nativeEvent.<XEvent>cast(); | |
| if (Element.is(nativeEvent.getEventTarget()) | |
| && !grid.getView().isSelectableTarget(Element.as(nativeEvent.getEventTarget()))) { | |
| return; | |
| } | |
| if (listStore.size() == 0) { | |
| return; | |
| } | |
| if (!e.getCtrlOrMetaKey() && selected.size() == 0 && getLastFocused() == null) { | |
| select(0, false); | |
| } else { | |
| int idx = listStore.indexOf(getLastFocused()); | |
| if (idx >= 0 && (idx + 1) < listStore.size()) { | |
| if (e.getCtrlOrMetaKey() || (e.getShiftKey() && isSelected(listStore.get(idx + 1)))) { | |
| if (!e.getCtrlOrMetaKey()) { | |
| deselect(idx); | |
| } | |
| M lF = listStore.get(idx + 1); | |
| if (lF != null) { | |
| setLastFocused(lF); | |
| grid.getView().focusCell(idx + 1, -1, false); | |
| } | |
| } else { | |
| if (e.getShiftKey() && lastSelected != getLastFocused()) { | |
| grid.getView().focusCell(idx + 1, -1, false); | |
| select(listStore.indexOf(lastSelected), idx + 1, true); | |
| } else { | |
| if (idx + 1 < listStore.size()) { | |
| grid.getView().focusCell(idx + 1, -1, false); | |
| selectNext(e.getShiftKey()); | |
| } | |
| } | |
| } | |
| } else { | |
| grid.getView().onNoNext(idx); | |
| } | |
| } | |
| e.preventDefault(); | |
| } | |
| /** | |
| * Handles a key left event (e.g. as fired by the key navigator). | |
| * | |
| * @param e the key left event | |
| */ | |
| protected void onKeyLeft(NativeEvent e) { | |
| } | |
| /** | |
| * Handles a key press event (e.g. as fired by the key navigator). | |
| * | |
| * @param nativeEvent the key press event | |
| */ | |
| protected void onKeyPress(NativeEvent nativeEvent) { | |
| if (Element.is(nativeEvent.getEventTarget()) | |
| && !grid.getView().isSelectableTarget(Element.as(nativeEvent.getEventTarget()))) { | |
| return; | |
| } | |
| int keyCode = nativeEvent.getKeyCode(); | |
| XEvent e = nativeEvent.<XEvent>cast(); | |
| if (lastSelected != null && enableNavKeys) { | |
| if (keyCode == KeyCodes.KEY_PAGEUP) { | |
| e.stopPropagation(); | |
| e.preventDefault(); | |
| select(0, false); | |
| grid.getView().focusRow(0); | |
| } else if (keyCode == KeyCodes.KEY_PAGEDOWN) { | |
| e.stopPropagation(); | |
| e.preventDefault(); | |
| int idx = listStore.indexOf(listStore.get(listStore.size() - 1)); | |
| select(idx, false); | |
| grid.getView().focusRow(idx); | |
| } | |
| } | |
| // if space bar is pressed | |
| if (e.getKeyCode() == 32) { | |
| if (getLastFocused() != null) { | |
| if (e.getShiftKey() && lastSelected != null) { | |
| int last = listStore.indexOf(lastSelected); | |
| int i = listStore.indexOf(getLastFocused()); | |
| grid.getView().focusCell(i, -1, false); | |
| select(last, i, e.getCtrlOrMetaKey()); | |
| } else { | |
| if (isSelected(getLastFocused())) { | |
| deselect(getLastFocused()); | |
| } else { | |
| grid.getView().focusCell(listStore.indexOf(getLastFocused()), -1, false); | |
| select(getLastFocused(), true); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Handles a key right event (e.g. as fired by the key navigator). | |
| * | |
| * @param e the key right event | |
| */ | |
| protected void onKeyRight(NativeEvent e) { | |
| } | |
| /** | |
| * Handles a key up event (e.g. as fired by the key navigator). | |
| * | |
| * @param nativeEvent the key up event | |
| */ | |
| protected void onKeyUp(NativeEvent nativeEvent) { | |
| XEvent xe = nativeEvent.<XEvent>cast(); | |
| if (Element.is(nativeEvent.getEventTarget()) | |
| && !grid.getView().isSelectableTarget(Element.as(nativeEvent.getEventTarget()))) { | |
| return; | |
| } | |
| int idx = listStore.indexOf(getLastFocused()); | |
| if (idx >= 1) { | |
| if (xe.getCtrlOrMetaKey() || (nativeEvent.getShiftKey() && isSelected(listStore.get(idx - 1)))) { | |
| if (!xe.getCtrlOrMetaKey()) { | |
| deselect(idx); | |
| } | |
| M lF = listStore.get(idx - 1); | |
| if (lF != null) { | |
| setLastFocused(lF); | |
| grid.getView().focusCell(idx - 1, -1, false); | |
| } | |
| } else { | |
| if (nativeEvent.getShiftKey() && lastSelected != getLastFocused()) { | |
| grid.getView().focusCell(idx - 1, -1, false); | |
| select(listStore.indexOf(lastSelected), idx - 1, true); | |
| } else { | |
| if (idx > 0) { | |
| grid.getView().focusCell(idx - 1, -1, false); | |
| selectPrevious(nativeEvent.getShiftKey()); | |
| } | |
| } | |
| } | |
| } else { | |
| grid.getView().onNoPrev(); | |
| } | |
| nativeEvent.preventDefault(); | |
| } | |
| @Override | |
| protected void onLastFocusChanged(M oldFocused, M newFocused) { | |
| int i; | |
| if (oldFocused != null) { | |
| i = listStore.indexOf(oldFocused); | |
| if (i >= 0) { | |
| grid.getView().onHighlightRow(i, false); | |
| } | |
| } | |
| if (newFocused != null) { | |
| i = listStore.indexOf(newFocused); | |
| if (i >= 0) { | |
| grid.getView().onHighlightRow(i, true); | |
| } | |
| } | |
| } | |
| @Override | |
| protected void onRecordChange(StoreRecordChangeEvent<M> event) { | |
| super.onRecordChange(event); | |
| refreshRowSelection(event.getRecord().getModel()); | |
| } | |
| @Override | |
| protected void onSelectChange(M model, boolean select) { | |
| int idx = listStore.indexOf(model); | |
| if (idx != -1) { | |
| if (select) { | |
| grid.getView().onRowSelect(idx); | |
| } else { | |
| grid.getView().onRowDeselect(idx); | |
| } | |
| } | |
| } | |
| @Override | |
| protected void onUpdate(final M model) { | |
| super.onUpdate(model); | |
| refreshRowSelection(model); | |
| } | |
| protected void refreshRowSelection(final M model) { | |
| Scheduler.get().scheduleFinally(new ScheduledCommand() { | |
| @Override | |
| public void execute() { | |
| if (isSelected(model)) { | |
| onSelectChange(model, true); | |
| } | |
| if (getLastFocused() == model) { | |
| setLastFocused(getLastFocused()); | |
| } | |
| } | |
| }); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment