Created
April 2, 2019 21:08
-
-
Save branflake2267/97c92fe470aa7b03c6667c0263ac97ee to your computer and use it in GitHub Desktop.
GXT 4.0.3 - ListViewSelectionModel selection fix, when you click on the scroll bar and use shift to persist the selection.
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; | |
import java.util.Collections; | |
import com.google.gwt.dom.client.Element; | |
import com.google.gwt.dom.client.NativeEvent; | |
import com.google.gwt.event.dom.client.ClickEvent; | |
import com.google.gwt.event.dom.client.ClickHandler; | |
import com.google.gwt.event.dom.client.KeyCodes; | |
import com.google.gwt.event.dom.client.MouseDownEvent; | |
import com.google.gwt.event.dom.client.MouseDownHandler; | |
import com.google.gwt.event.dom.client.MouseUpEvent; | |
import com.google.gwt.event.dom.client.MouseUpHandler; | |
import com.sencha.gxt.core.client.Style.SelectionMode; | |
import com.sencha.gxt.core.client.dom.XElement; | |
import com.sencha.gxt.core.client.gestures.PointerEventsSupport; | |
import com.sencha.gxt.core.client.gestures.TapGestureRecognizer; | |
import com.sencha.gxt.core.client.gestures.TouchData; | |
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.widget.core.client.event.RefreshEvent; | |
import com.sencha.gxt.widget.core.client.event.RefreshEvent.RefreshHandler; | |
import com.sencha.gxt.widget.core.client.event.XEvent; | |
import com.sencha.gxt.widget.core.client.selection.AbstractStoreSelectionModel; | |
/** | |
* ListView selection model. | |
* | |
* @param <M> the model type | |
*/ | |
public class ListViewSelectionModel<M> extends AbstractStoreSelectionModel<M> { | |
protected class Handler implements MouseDownHandler, MouseUpHandler, ClickHandler, RefreshHandler { | |
@Override | |
public void onClick(ClickEvent event) { | |
onMouseClick(event); | |
} | |
@Override | |
public void onMouseDown(MouseDownEvent event) { | |
ListViewSelectionModel.this.onMouseDown(event); | |
} | |
@Override | |
public void onMouseUp(MouseUpEvent event) { | |
ListViewSelectionModel.this.onMouseUp(event); | |
} | |
@Override | |
public void onRefresh(RefreshEvent event) { | |
ListViewSelectionModel.this.onRefresh(event); | |
} | |
} | |
protected boolean enableNavKeys = true; | |
protected KeyNav keyNav = new KeyNav() { | |
@Override | |
public void onDown(NativeEvent e) { | |
if (isVertical) { | |
ListViewSelectionModel.this.onKeyDown(e); | |
} | |
} | |
@Override | |
public void onKeyPress(NativeEvent ce) { | |
ListViewSelectionModel.this.onKeyPress(ce); | |
} | |
@Override | |
public void onLeft(NativeEvent e) { | |
if (!isVertical) { | |
ListViewSelectionModel.this.onKeyUp(e); | |
} | |
} | |
@Override | |
public void onRight(NativeEvent e) { | |
if (!isVertical) { | |
ListViewSelectionModel.this.onKeyDown(e); | |
} | |
} | |
@Override | |
public void onUp(NativeEvent e) { | |
if (isVertical) { | |
ListViewSelectionModel.this.onKeyUp(e); | |
} | |
} | |
}; | |
protected ListStore<M> listStore; | |
protected ListView<M, ?> listView; | |
/** | |
* True to deselect a selected item on click (defaults to {@code true). | |
*/ | |
protected boolean deselectOnSimpleClick = true; | |
protected boolean isVertical = true; | |
protected Handler handler = new Handler(); | |
protected GroupingHandlerRegistration handlerRegistration = new GroupingHandlerRegistration(); | |
/** | |
* Track the selection index, when the shift combined with and so then this is the starting point of the selection. | |
*/ | |
protected int indexOnSelectNoShift; | |
/** | |
* Binds the list view to the selection model. | |
* | |
* @param listView the list view | |
*/ | |
public void bindList(ListView<M, ?> listView) { | |
if (this.listView != null) { | |
handlerRegistration.removeHandler(); | |
keyNav.bind(null); | |
this.listStore = null; | |
bind(null); | |
} | |
this.listView = listView; | |
if (listView != null) { | |
if (handlerRegistration == null) { | |
handlerRegistration = new GroupingHandlerRegistration(); | |
} | |
handlerRegistration.add(listView.addDomHandler(handler, MouseDownEvent.getType())); | |
handlerRegistration.add(listView.addDomHandler(handler, MouseUpEvent.getType())); | |
handlerRegistration.add(listView.addDomHandler(handler, ClickEvent.getType())); | |
handlerRegistration.add(listView.addRefreshHandler(handler)); | |
keyNav.bind(listView); | |
bind(listView.getStore()); | |
this.listStore = listView.getStore(); | |
listView.addGestureRecognizer(new TapGestureRecognizer() { | |
@Override | |
protected void onTap(TouchData touchData) { | |
super.onTap(touchData); | |
ListViewSelectionModel.this.onTap(touchData); | |
} | |
}); | |
} | |
} | |
/** | |
* Returns the currently bound list view. | |
* | |
* @return the list view | |
*/ | |
public ListView<M, ?> getListView() { | |
return listView; | |
} | |
/** | |
* Returns {@code true} if up and down arrow keys are used for navigation. Else left and right arrow keys are used. | |
* | |
* @return the isVertical | |
*/ | |
public boolean isVertical() { | |
return isVertical; | |
} | |
/** | |
* Sets if up and down arrow keys or left and right arrow keys should be used (defaults to {@code true}). | |
* | |
* @param isVertical the isVertical to set | |
*/ | |
public void setVertical(boolean isVertical) { | |
this.isVertical = isVertical; | |
} | |
/** | |
* since 4.0.3 | |
*/ | |
protected void onRefresh(RefreshEvent event) { | |
refresh(); | |
if (getLastFocused() != null) { | |
listView.onHighlightRow(listStore.indexOf(getLastFocused()), true); | |
} | |
} | |
protected void onMouseClick(ClickEvent ce) { | |
onMouseClick(ce.getNativeEvent()); | |
} | |
protected void onMouseClick(NativeEvent event) { | |
if (fireSelectionChangeOnClick) { | |
fireSelectionChange(); | |
fireSelectionChangeOnClick = false; | |
} | |
} | |
protected void onMouseDown(MouseDownEvent event) { | |
onMouseDown(event.getNativeEvent()); | |
} | |
protected void onMouseDown(NativeEvent event) { | |
if (PointerEventsSupport.impl.isSupported()) { | |
return; | |
} | |
if (!processSelectOnMouseUp) { | |
onSelect(event); | |
} | |
} | |
/** | |
* Returns if the click is on the scroll bar. | |
* | |
* @param event - native event | |
* @return true or false | |
* @since 4.0.4 | |
*/ | |
protected boolean isScrollBarEvent(NativeEvent event) { | |
Element target = event.getEventTarget().cast(); | |
if (target == null) { | |
return false; | |
} | |
int selIndex = listView.findElementIndex(target); | |
// Figure if the event is on the scroll bar | |
if (selIndex == -1) { | |
return true; | |
} | |
return false; | |
} | |
/** | |
* @since 4.0.3 | |
*/ | |
protected void onMouseUp(MouseUpEvent event) { | |
onMouseUp(event.getNativeEvent()); | |
} | |
/** | |
* @since 4.0.3 | |
*/ | |
protected void onMouseUp(NativeEvent event) { | |
if (processSelectOnMouseUp) { | |
onSelect(event); | |
} | |
} | |
/** | |
* @since 4.0.3 | |
*/ | |
protected void onTap(TouchData touchData) { | |
onSelect(touchData.getLastNativeEvent()); | |
} | |
/** | |
* @since 4.0.3 | |
*/ | |
protected void onSelect(NativeEvent nativeEvent) { | |
// Ignore scroll bar events | |
if (isScrollBarEvent(nativeEvent)) { | |
return; | |
} | |
XEvent event = nativeEvent.<XEvent>cast(); | |
XElement target = event.getEventTargetEl(); | |
int selIndex = listView.findElementIndex(target); | |
if (isLocked() || isInput(target)) { | |
return; | |
} | |
if (selIndex == -1) { | |
deselectAll(); | |
return; | |
} | |
mouseDown = true; | |
if (event.isRightClick()) { | |
if (selectionMode != SelectionMode.SINGLE && isSelected(listStore.get(selIndex))) { | |
onRightClick(nativeEvent, selIndex); | |
return; | |
} | |
select(selIndex, false); | |
listView.focusItem(selIndex); | |
} else { | |
M selectedModel = listStore.get(selIndex); | |
if (selectedModel == null) { | |
return; | |
} | |
boolean isSelected = isSelected(selectedModel); | |
boolean isMeta = event.getCtrlOrMetaKey(); | |
boolean isShift = event.getShiftKey(); | |
switch (selectionMode) { | |
case SIMPLE: | |
listView.focusItem(selIndex); | |
if (!isSelected) { | |
select(selectedModel, true); | |
} else if (isSelected && deselectOnSimpleClick) { | |
deselect(selectedModel); | |
} | |
break; | |
case SINGLE: | |
if (isMeta && isSelected) { | |
deselect(selectedModel); | |
} else if (!isSelected) { | |
listView.focusItem(selIndex); | |
select(selectedModel, false); | |
} | |
break; | |
case MULTI: | |
if (isMeta && isSelected) { | |
// reset the starting location of the click | |
indexOnSelectNoShift = selIndex; | |
doDeselect(Collections.singletonList(selectedModel), false); | |
} else if (isMeta) { | |
// reset the starting location of the click | |
indexOnSelectNoShift = selIndex; | |
doSelect(Collections.singletonList(selectedModel), true, false); | |
listView.focusItem(selIndex); | |
} else if (isSelected && !isShift && !isMeta && selected.size() > 1) { | |
// reset the starting location of the click | |
indexOnSelectNoShift = selIndex; | |
doSelect(Collections.singletonList(selectedModel), false, false); | |
listView.focusItem(selIndex); | |
} else if (isShift && lastSelected != null) { | |
int start; | |
int end; | |
// This deals with flipping directions | |
if (indexOnSelectNoShift < selIndex) { | |
start = indexOnSelectNoShift; | |
end = selIndex; | |
} else { | |
start = selIndex; | |
end = indexOnSelectNoShift; | |
} | |
select(start, end, false); | |
listView.focusItem(end); | |
} else if (!isSelected) { | |
// reset the starting location of multi select | |
indexOnSelectNoShift = selIndex; | |
listView.focusItem(selIndex); | |
doSelect(Collections.singletonList(selectedModel), false, false); | |
} | |
break; | |
} | |
} | |
mouseDown = false; | |
} | |
/** | |
* @since 4.0.3 | |
*/ | |
protected void onRightClick(NativeEvent event, int selectedIndex) { | |
} | |
protected boolean isInput(Element target) { | |
String tag = target.getTagName(); | |
return "INPUT".equals(tag) || "TEXTAREA".equals(tag); | |
} | |
protected void onKeyDown(NativeEvent event) { | |
XEvent e = event.<XEvent>cast(); | |
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); | |
listView.focusItem(idx + 1); | |
} | |
} else { | |
if (e.getShiftKey() && lastSelected != getLastFocused()) { | |
select(listStore.indexOf(lastSelected), idx + 1, true); | |
listView.focusItem(idx + 1); | |
} else { | |
if (idx + 1 < listStore.size()) { | |
select(idx + 1, e.getShiftKey()); | |
listView.focusItem(idx + 1); | |
} | |
} | |
} | |
} | |
} | |
e.preventDefault(); | |
} | |
protected void onKeyPress(NativeEvent event) { | |
XEvent e = event.<XEvent>cast(); | |
if (lastSelected != null && enableNavKeys) { | |
int kc = e.getKeyCode(); | |
if (kc == KeyCodes.KEY_PAGEUP || kc == KeyCodes.KEY_HOME) { | |
e.stopEvent(); | |
select(0, false); | |
listView.focusItem(0); | |
} else if (kc == KeyCodes.KEY_PAGEDOWN || kc == KeyCodes.KEY_END) { | |
e.stopEvent(); | |
int idx = listStore.indexOf(listStore.get(listStore.size() - 1)); | |
select(idx, false); | |
listView.focusItem(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()); | |
select(last, i, e.getCtrlOrMetaKey()); | |
listView.focusItem(i); | |
} else { | |
if (isSelected(getLastFocused())) { | |
deselect(getLastFocused()); | |
} else { | |
select(getLastFocused(), true); | |
listView.focusItem(listStore.indexOf(getLastFocused())); | |
} | |
} | |
} | |
} | |
} | |
protected void onKeyUp(NativeEvent event) { | |
XEvent e = event.<XEvent>cast(); | |
int idx = listStore.indexOf(getLastFocused()); | |
if (idx >= 1) { | |
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); | |
listView.focusItem(idx - 1); | |
} | |
} else { | |
if (e.getShiftKey() && lastSelected != getLastFocused()) { | |
select(listStore.indexOf(lastSelected), idx - 1, true); | |
listView.focusItem(idx - 1); | |
} else { | |
if (idx > 0) { | |
select(idx - 1, e.getShiftKey()); | |
listView.focusItem(idx - 1); | |
} | |
} | |
} | |
} | |
e.preventDefault(); | |
} | |
@Override | |
protected void onLastFocusChanged(M oldFocused, M newFocused) { | |
int i; | |
if (oldFocused != null) { | |
i = listStore.indexOf(oldFocused); | |
if (i >= 0) { | |
listView.onHighlightRow(i, false); | |
} | |
} | |
if (newFocused != null) { | |
i = listStore.indexOf(newFocused); | |
if (i >= 0) { | |
listView.onHighlightRow(i, true); | |
} | |
} | |
} | |
@Override | |
protected void onSelectChange(M model, boolean select) { | |
listView.onSelectChange(model, select); | |
} | |
void onRowUpdated(M model) { | |
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