Skip to content

Instantly share code, notes, and snippets.

@branflake2267
Last active September 20, 2019 10:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save branflake2267/d5f7b546f7c0dbe83df07a2d4d687695 to your computer and use it in GitHub Desktop.
Save branflake2267/d5f7b546f7c0dbe83df07a2d4d687695 to your computer and use it in GitHub Desktop.
GXT 4 split grid with left side that locks.
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.sencha.gxt.test.client.grid_splitgrid.cust.GridRightClickEvent.GridRightHandler;
/**
* Fires after the reset selection event
*/
public class GridRightClickEvent extends GwtEvent<GridRightHandler> {
public interface HasGridRightClickHandlers {
HandlerRegistration addGridRightClickHandler(GridRightHandler handler);
}
public interface GridRightHandler extends EventHandler {
void onRightClick(GridRightClickEvent event);
}
/**
* Handler type.
*/
private static Type<GridRightHandler> TYPE;
/**
* Gets the type associated with this event.
*
* @return returns the handler type
*/
public static Type<GridRightHandler> getType() {
if (TYPE == null) {
TYPE = new Type<GridRightHandler>();
}
return TYPE;
}
private int rowIndex;
private int colIndex;
private Event event;
/**
* Create a new right click event on row/cell
*
* @param event
* is the event target
* @param rowIndex
* @param colIndex
*/
public GridRightClickEvent(Event event, int rowIndex, int colIndex) {
this.event = event;
this.rowIndex = rowIndex;
this.colIndex = colIndex;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Type<GridRightHandler> getAssociatedType() {
return (Type) TYPE;
}
@Override
protected void dispatch(GridRightHandler handler) {
handler.onRightClick(this);
}
public int getRowIndex() {
return rowIndex;
}
public int getColIndex() {
return colIndex;
}
public Event getEvent() {
return event;
}
}
package com.sencha.gxt.test.client.grid_splitgrid.cust;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.core.client.GWT;
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.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.GXT;
import com.sencha.gxt.core.client.dom.XDOM;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.gestures.PointerEventsSupport;
import com.sencha.gxt.core.client.util.Margins;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoader;
import com.sencha.gxt.test.client.grid_livegrid.checkbox.CountingCheckBoxSelectionModel_LGV;
import com.sencha.gxt.test.client.grid_splitgrid.cust.GridRightClickEvent.GridRightHandler;
import com.sencha.gxt.test.client.grid_splitgrid.cust.SplitGridView.GridSide;
import com.sencha.gxt.widget.core.client.Composite;
import com.sencha.gxt.widget.core.client.container.HorizontalLayoutContainer;
import com.sencha.gxt.widget.core.client.container.HorizontalLayoutContainer.HorizontalLayoutData;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
import com.sencha.gxt.widget.core.client.event.ReconfigureEvent;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.GridView;
public class SplitGrid<M> extends Composite {
/**
* Use for left and right grids.
*/
public class GridExt<M> extends Grid<M> {
private HorizontalLayoutContainer maskOnWidget;
public GridExt(ListStore<M> store, ColumnModel<M> cm, GridView<M> view) {
super(store, cm, view);
// right click
sinkEvents(Event.ONCONTEXTMENU);
}
@Override
protected void onRightClick(Event event) {
// ignore the native context menu
event.preventDefault();
event.stopPropagation();
Element target = Element.as(event.getEventTarget());
int rowIndex = view.findRowIndex(target);
if (rowIndex != -1) {
int colIndex = view.findCellIndex(target, null);
fireEvent(new GridRightClickEvent(event, rowIndex, colIndex));
}
}
public HandlerRegistration addRightClickHandler(GridRightHandler handler) {
return addHandler(handler, GridRightClickEvent.getType());
}
@Override
public void mask() {
// causing issues
}
@Override
public void unmask() {
// causing issues
}
@Override
protected void onLoaderBeforeLoad() {
maskGrids();
}
@Override
protected void onLoaderLoadException() {
unMaskGrids();
}
@Override
protected void onLoadLoader() {
unMaskGrids();
}
public void maskGrids() {
// TODO would you like this? or just keep a loading icon in paging toolbar
// if (maskOnWidget == null) {
// return;
// }
// maskOnWidget.mask(DefaultMessages.getMessages().loadMask_msg());
}
public void unMaskGrids() {
// // TODO would you like this? or just keep a loading icon in paging toolbar
// if (maskOnWidget == null) {
// return;
// }
// maskOnWidget.unmask();
}
public void setMaskOnWidget(HorizontalLayoutContainer maskOnWidget) {
this.maskOnWidget = maskOnWidget;
}
@Override
public void reconfigure(ListStore<M> store, ColumnModel<M> cm) {
if (!viewReady) {
this.store = store;
this.cm = cm;
setSelectionModel(sm);
return;
}
// if (isLoadMask()) {
// mask(DefaultMessages.getMessages().loadMask_msg());
// }
// view.initData(store, cm);
initDataJsni(view, store, cm);
this.store = store;
this.cm = cm;
sinkCellEvents();
// rebind the sm
setSelectionModel(sm);
if (isViewReady()) {
view.refresh(true);
}
// if (isLoadMask()) {
// unmask();
// }
fireEvent(new ReconfigureEvent());
}
public native void initDataJsni(GridView<M> view, ListStore<M> store, ColumnModel<M> cm) /*-{
view.@com.sencha.gxt.widget.core.client.grid.GridView::initData(*)(store, cm);
}-*/;
/**
* Reconfigure will try to set the selection model again. the selection model only needs to be set in beginning.
*/
public void setSelectionModel(CountingCheckBoxSelectionModel_LGV<M> sm) {
if (this.sm != null) {
this.sm.bindGrid(null);
}
this.sm = sm;
if (sm != null) {
sm.bindGrid(this);
}
}
@Override
protected void onTouch(Event event) {
// GWT.log("onTouch(e) ----- ");
// super.onTouch(event); // ~~~ workaround for edge
}
@Override
protected void onMouseDown(Event e) {
GWT.log("onMouseDown(e) ********* ");
super.onMouseDown(e);
}
@Override
public void onBrowserEvent(Event ce) {
super.onBrowserEvent(ce);
switch (ce.getTypeInt()) {
case Event.ONCLICK:
// ~~~ workaround for edge - correlates with onTouch being disabled
if (PointerEventsSupport.impl.isSupported()) {
onClick(ce);
}
break;
}
}
}
// views
private SplitGridView<M> leftGridView;
private SplitGridView<M> rightGridView;
// grids
private GridExt<M> leftGrid;
private GridExt<M> rightGrid;
private HorizontalLayoutContainer grids;
private HorizontalLayoutData leftGridScrollSpacer;
// selection
private ListStore<M> listStore;
// grid configs
private PagingLoader<PagingLoadConfig, PagingLoadResult<M>> pagingLoader;
private List<ColumnConfig<M, ?>> leftColumns;
private List<ColumnConfig<M, ?>> rightColumns;
private SplitPagingToolBar<M> pagingToolbar;
/**
* This is used to eliminate recursive calls when setting scroll top
*/
private int scrollLeftRigthTrack = 0;
public SplitGrid(ListStore<M> listStore, PagingLoader<PagingLoadConfig, PagingLoadResult<M>> pagingLoader,
List<ColumnConfig<M, ?>> leftColumns, List<ColumnConfig<M, ?>> rightColumns) {
this.listStore = listStore;
this.pagingLoader = pagingLoader;
this.leftColumns = leftColumns;
this.rightColumns = rightColumns;
initWidget(initSplitGrid());
}
private Widget initSplitGrid() {
leftGridView = new SplitGridView<M>(GridSide.LEFT);
rightGridView = new SplitGridView<M>(GridSide.RIGHT);
leftGridView.setRightGridView(rightGridView);
rightGridView.setLeftGridView(leftGridView);
leftGrid = initLeftGrid(listStore, pagingLoader);
rightGrid = initRightGrid(listStore, pagingLoader);
String borderColorClassName = "#cccccc"; // GENERAL_CSS.borderColorPrimaryAsBackground();
// grid center splitter line
FlowPanel centerSplitter = new FlowPanel();
centerSplitter.setWidth("2px");
centerSplitter.setHeight("100%");
centerSplitter.getElement().addClassName(borderColorClassName);
// grid bottom border
FlowPanel bottomBorder = new FlowPanel();
bottomBorder.setHeight("2px");
bottomBorder.setWidth("100%");
bottomBorder.getElement().addClassName(borderColorClassName);
// left spacer for windows
leftGridScrollSpacer = new HorizontalLayoutData(300, 1);
leftGridScrollSpacer.setMargins(new Margins(0, 0, getLeftGridMargin(), 0));
// horz - grids
grids = new HorizontalLayoutContainer();
grids.add(leftGrid, leftGridScrollSpacer);
grids.add(centerSplitter, new HorizontalLayoutData(-1, 1));
grids.add(rightGrid, new HorizontalLayoutData(1, 1));
grids.addResizeHandler(new ResizeHandler() {
@Override
public void onResize(ResizeEvent event) {
resizeLeftGridMargin();
}
});
// blank space on the left for windows
grids.getElement().getStyle().setBackgroundColor("#b9b9b9");
leftGrid.setMaskOnWidget(grids);
rightGrid.setMaskOnWidget(grids);
// vert - toolbar/grid
VerticalLayoutContainer widget = new VerticalLayoutContainer();
widget.add(grids, new VerticalLayoutData(1, 1));
widget.add(bottomBorder, new VerticalLayoutData(1, -1));
widget.add(getPagingToolbar(), new VerticalLayoutData(-1, -1));
// Make sure both column headers are the same height
leftGridView.getHeader().setHeight(28);
rightGridView.getHeader().setHeight(28);
return widget;
}
/**
* Determine if the left grid should have a margin based on the right grid having a scroll bar or not.
*/
private void resizeLeftGridMargin() {
if (!GXT.isWindows()) {
return;
}
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
leftGridScrollSpacer.setMargins(new Margins(0, 0, getLeftGridMargin(), 0));
grids.forceLayout();
}
});
}
private int getLeftGridMargin() {
if (!GXT.isWindows()) {
return 0;
}
int margin = 0;
XElement dataTable = getDataTable(rightGrid.getView());
// does the right side have a scroll bar
if (dataTable != null && rightGrid.getView().getScroller().getScrollWidth() <= dataTable.getOffsetWidth()) {
margin = XDOM.getScrollBarWidth() - 1; // 1px fix, like a border causing it to be higher
}
return margin;
}
private native XElement getDataTable(GridView<?> view) /*-{
return view.@com.sencha.gxt.widget.core.client.grid.GridView::dataTable;
}-*/;
private SplitPagingToolBar<M> getPagingToolbar() {
// The page size is set with the paging limit.
pagingToolbar = new SplitPagingToolBar<M>(200);
pagingToolbar.bind(pagingLoader);
return pagingToolbar;
}
/**
* Left Grid
*/
private GridExt<M> initLeftGrid(ListStore<M> listStore,
final PagingLoader<PagingLoadConfig, PagingLoadResult<M>> pagingLoader) {
ColumnModel<M> leftCM = new ColumnModel<M>(getLeftColumns());
final GridExt<M> leftGrid = new GridExt<M>(listStore, leftCM, leftGridView) {
@Override
public void onBrowserEvent(Event ce) {
if (ce.getTypeInt() == Event.ONSCROLL && scrollLeftRigthTrack == 0) {
scrollLeftRigthTrack++;
rightGrid.getView().getScroller().setScrollTop(getView().getScroller().getScrollTop());
} else if (ce.getTypeInt() == Event.ONSCROLL) {
scrollLeftRigthTrack = 0;
}
super.onBrowserEvent(ce);
}
};
//leftGrid.setSelectionModel(selectionModel);
leftGrid.setLoader(pagingLoader);
// styles
leftGrid.getView().setForceFit(true);
leftGrid.setBorders(false);
leftGrid.setColumnReordering(true);
leftGrid.getView().setStripeRows(true);
leftGrid.getView().setColumnLines(false);
leftGrid.setLoadMask(true);
return leftGrid;
}
protected void clearSelection() {
//selectionModel.deselectAll();
}
/**
* Right Grid
*/
private GridExt<M> initRightGrid(ListStore<M> listStore,
final PagingLoader<PagingLoadConfig, PagingLoadResult<M>> pagingLoader) {
ColumnModel<M> rightCM = new ColumnModel<M>(getRightColumns());
GridExt<M> rightGrid = new GridExt<M>(listStore, rightCM, rightGridView) {
@Override
public void onBrowserEvent(Event ce) {
if (ce.getTypeInt() == Event.ONSCROLL && scrollLeftRigthTrack == 0) {
scrollLeftRigthTrack++;
leftGrid.getView().getScroller().setScrollTop(getView().getScroller().getScrollTop());
} else if (ce.getTypeInt() == Event.ONSCROLL) {
scrollLeftRigthTrack = 0;
}
super.onBrowserEvent(ce);
}
};
//rightGrid.setSelectionModelRight(selectionModel);
rightGrid.setLoader(pagingLoader);
// grid styles
rightGrid.setBorders(false);
rightGrid.setColumnReordering(true);
rightGrid.getView().setStripeRows(true);
rightGrid.getView().setColumnLines(false);
rightGrid.setLoadMask(true);
return rightGrid;
}
/**
* Get left columns but include the checkbox column
*/
private List<ColumnConfig<M, ?>> getLeftColumns() {
List<ColumnConfig<M, ?>> columns = new ArrayList<>();
// dev columns
columns.addAll(leftColumns);
return columns;
}
private List<ColumnConfig<M, ?>> getRightColumns() {
return rightColumns;
}
public ListStore<M> getStore() {
return listStore;
}
public SplitGridView<M> getLeftView() {
return leftGridView;
}
public SplitGridView<M> getRightView() {
return rightGridView;
}
public void setEmptyText(String emptyText) {
leftGridView.setEmptyText("");
rightGridView.setEmptyText(emptyText);
}
public void refresh(boolean headerToo) {
leftGridView.refresh(headerToo);
rightGridView.refresh(headerToo);
clearSelection();
}
public HandlerRegistration addLeftGridRightClickhandler(GridRightHandler handler) {
return leftGrid.addRightClickHandler(handler);
}
public HandlerRegistration addRightGridRightClickhandler(GridRightHandler handler) {
return rightGrid.addRightClickHandler(handler);
}
public void scrollToTop() {
pagingToolbar.first();
}
public Grid<M> getLeftGrid() {
return leftGrid;
}
public Grid<M> getRightGrid() {
return rightGrid;
}
public void reconfigure(List<ColumnConfig<M, ?>> columnsLeft, List<ColumnConfig<M, ?>> columnsRight) {
this.leftColumns = columnsLeft;
this.rightColumns = columnsRight;
ColumnModel<M> leftCM = new ColumnModel<M>(getLeftColumns());
ColumnModel<M> rightCM = new ColumnModel<M>(getRightColumns());
leftGrid.reconfigure(listStore, leftCM);
rightGrid.reconfigure(listStore, rightCM);
resizeLeftGridMargin();
}
public SplitPagingToolBar<M> getPagingToolBar() {
return pagingToolbar;
}
}
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.StyleInjector;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.data.client.loader.RpcProxy;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.loader.DataProxy;
import com.sencha.gxt.data.shared.loader.LoadResultListStoreBinding;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadConfigBean;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoader;
import com.sencha.gxt.test.client.grid_splitgrid.cust.SplitGridView.GridSide;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.container.SimpleContainer;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
import com.sencha.gxt.widget.core.client.container.Viewport;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent.SelectHandler;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.filters.StringFilter;
public class SplitGridExample implements EntryPoint {
private static final int COLUMNS_SIZE = 20;
private static final int TOTAL_LENGTH = 5000;
public class CustomPagingLoader<C extends PagingLoadConfig, D extends PagingLoadResult<?>>
extends PagingLoader<C, D> {
public CustomPagingLoader(DataProxy<C, D> proxy) {
super(proxy);
}
@Override
public boolean load() {
return super.load();
}
}
@Override
public void onModuleLoad() {
ListStore<Data> listStore = getStore();
final CustomPagingLoader<PagingLoadConfig, PagingLoadResult<Data>> pagingLoader = getLoader(listStore);
pagingLoader.setLimit(200);
List<ColumnConfig<Data, ?>> leftColumns = getColumns(GridSide.LEFT, 0, 3);
List<ColumnConfig<Data, ?>> rightColumns = getColumns(GridSide.RIGHT, 3, COLUMNS_SIZE);
SplitGrid<Data> grid = new SplitGrid<Data>(listStore, pagingLoader, leftColumns, rightColumns);
TextButton loadbutton = new TextButton("Load");
loadbutton.addSelectHandler(new SelectHandler() {
@Override
public void onSelect(SelectEvent event) {
pagingLoader.load();
}
});
HorizontalPanel hp = new HorizontalPanel();
hp.add(loadbutton);
SimpleContainer top = new SimpleContainer();
top.add(hp);
VerticalLayoutContainer vlc = new VerticalLayoutContainer();
vlc.add(top, new VerticalLayoutData(1, 100));
vlc.add(grid, new VerticalLayoutData(1, 1));
Viewport vp = new Viewport();
vp.add(vlc);
RootPanel.get().add(vp);
}
/**
* Columns
*/
private List<ColumnConfig<Data, ?>> getColumns(GridSide gridSide, int start, int limit) {
List<ColumnConfig<Data, ?>> columns = new ArrayList<ColumnConfig<Data, ?>>();
// TODO move to ClientBundle
// TODO account for older browser transparency/opacity
StyleInjector.inject(".headBlack { background-color: black; } .colGray { background-color: rgba(0, 0, 0, 0.1); }");
for (int c = start; c < limit; c++) {
String header = "COL" + c;
SafeHtml safeHtml = SafeHtmlUtils.fromSafeConstant(header);
ColumnConfig<Data, String> col = new ColumnConfig<Data, String>(new ValueProviderExt(c), 70);
col.setHeader(safeHtml);
// Align Column Content
col.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
col.setHorizontalHeaderAlignment(HasHorizontalAlignment.ALIGN_LEFT);
if (c == 3) {
col.setColumnHeaderClassName("headBlack");
col.setColumnTextClassName("colGray");
}
columns.add(col);
}
return columns;
}
/**
* Loader
*
* TODO dont' load on left side
*
* @param listStore
*/
private CustomPagingLoader<PagingLoadConfig, PagingLoadResult<Data>> getLoader(ListStore<Data> listStore) {
RpcProxy<PagingLoadConfig, PagingLoadResult<Data>> proxy = new RpcProxy<PagingLoadConfig, PagingLoadResult<Data>>() {
@Override
public void load(final PagingLoadConfig loadConfig, final AsyncCallback<PagingLoadResult<Data>> callback) {
GWT.log("pause loading");
Timer t = new Timer() {
@Override
public void run() {
GWT.log("load() " + loadConfig.getOffset() + " " + loadConfig.getLimit() + " Loading...");
getDatas(loadConfig, callback);
}
};
t.schedule(500);
}
};
CustomPagingLoader<PagingLoadConfig, PagingLoadResult<Data>> loader = new CustomPagingLoader<PagingLoadConfig, PagingLoadResult<Data>>(
proxy);
loader.useLoadConfig(new PagingLoadConfigBean());
loader.addLoadHandler(new LoadResultListStoreBinding<PagingLoadConfig, Data, PagingLoadResult<Data>>(listStore));
loader.setRemoteSort(true);
return loader;
}
private ListStore<Data> getStore() {
ListStore<Data> store = new ListStore<Data>(new ModelKeyProvider<Data>() {
@Override
public String getKey(Data item) {
return item.getKey();
}
});
return store;
}
private void getDatas(PagingLoadConfig loadConfig, AsyncCallback<PagingLoadResult<Data>> callback) {
final int offset = loadConfig.getOffset();
int limit = loadConfig.getLimit();
GWT.log("getDatas: offset=" + offset + " limit=" + limit);
final List<Data> datas = new ArrayList<Data>();
for (int i = offset; i < offset + limit; i++) {
datas.add(getData(i));
}
PagingLoadResult<Data> result = new PagingLoadResult<Data>() {
@Override
public List<Data> getData() {
return datas;
}
@Override
public void setTotalLength(int totalLength) {
}
@Override
public void setOffset(int offset) {
}
@Override
public int getTotalLength() {
return TOTAL_LENGTH;
}
@Override
public int getOffset() {
return offset;
}
};
callback.onSuccess(result);
}
private Data getData(int row) {
String key = "key" + row;
String[] values = new String[COLUMNS_SIZE];
for (int col = 0; col < COLUMNS_SIZE; col++) {
values[col] = "" + col + "," + row;
}
Data data = new Data(key, values);
return data;
}
public class ValueProviderExt implements ValueProvider<Data, String> {
private int index;
public ValueProviderExt(int index) {
this.index = index;
}
@Override
public String getValue(Data data) {
return data.getValue(index);
}
@Override
public void setValue(Data object, String value) {
}
@Override
public String getPath() {
return "path" + index;
}
}
public class StringFilterExt extends StringFilter<Data> {
public StringFilterExt(int index) {
super(new ValueProviderExt(index));
}
}
public class Data {
private String key;
private String[] values;
public Data(String key, String[] values) {
this.key = key;
this.values = values;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getValue(int index) {
return values[index];
}
public void setValue(int index, String value) {
this.values[index] = value;
}
@Override
public String toString() {
String s = "Data(";
s += "key=" + key;
s += ")";
return s;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Data other = (Data) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
return true;
}
}
}
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.Style.Cursor;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.util.Region;
import com.sencha.gxt.data.shared.SortDir;
import com.sencha.gxt.data.shared.SortInfoBean;
import com.sencha.gxt.fx.client.DragEndEvent;
import com.sencha.gxt.fx.client.DragStartEvent;
import com.sencha.gxt.widget.core.client.ComponentHelper;
import com.sencha.gxt.widget.core.client.event.XEvent;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnHeader;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.GridView;
import com.sencha.gxt.widget.core.client.menu.Menu;
/**
* LiveGridView overrides
*/
public class SplitGridView<M> extends GridView<M> {
public class ColumnHeaderExt<M> extends ColumnHeader<M> {
public ColumnHeaderExt(Grid<M> container, ColumnModel<M> cm) {
super(container, cm);
}
public void setEnableColumnResizing(boolean enable) {
if (bar == null && enable) {
bar = new GridSplitBar() {
protected void onDragStart(DragStartEvent e) {
drag(true, "1px solid black", 1, 1);
getElement().getStyle().setCursor(Cursor.DEFAULT);
startX = e.getX();
int cols = cm.getColumnCount();
for (int i = 0, len = cols; i < len; i++) {
if (cm.isHidden(i) || !cm.isResizable(i)) continue;
Element hd = getHead(i).getElement();
if (hd != null) {
Element hdParent = hd.getParentElement(); // Workaround for bad width when menu is not present
Region rr = XElement.as(hdParent).getRegion();
//GWT.log("rr=" + rr + " startX=" + startX + " right=" + (rr.getRight() - 5));
if (startX > rr.getRight() - 5 && startX < rr.getRight() + 5) {
colIndex = heads.indexOf(getHead(i));
//GWT.log("~~~~~~DRAG colIndex=" + colIndex);
if (colIndex != -1) break;
}
}
}
//GWT.log("~~~~~~DRAG colIndex=" + colIndex);
if (colIndex > -1) {
Element c = getHead(colIndex).getElement();
int x = startX;
int minx = x - XElement.as(c).getX() - minColumnWidth;
int maxx = (XElement.as(container.getElement()).getX() + XElement.as(container.getElement()).getWidth(false))
- e.getNativeEvent().<XEvent>cast().getXY().getX();
d.setXConstraint(minx, maxx);
}
}
protected void onDragEnd(DragEndEvent e) {
drag(false, "none", 0, splitterWidth);
int endX = e.getX();
int diff = endX - startX;
int width = Math.max(getMinColumnWidth(), cm.getColumnWidth(colIndex) + diff);
cm.setUserResized(true);
cm.setColumnWidth(colIndex, width);
}
};
container.getElement().appendChild(bar.getElement());
if (isAttached()) {
ComponentHelper.doAttach(bar);
}
bar.show();
} else if (bar != null && !enable) {
ComponentHelper.doDetach(bar);
bar.getElement().removeFromParent();
bar = null;
}
}
}
/**
* Designates left or right grid
*/
public enum GridSide {
LEFT, RIGHT
}
//private HashSet<M> selected;
private GridSide gridSide;
private SplitGridView<M> leftGridView;
private SplitGridView<M> rightGridView;
public SplitGridView(GridSide gridSide) {
this.gridSide = gridSide;
vbar = false;
if (gridSide == GridSide.LEFT) {
// fix white space by decrementing 1
scrollOffset = -1;
}
}
public void setLeftGridView(SplitGridView<M> leftGridView) {
this.leftGridView = leftGridView;
this.rightGridView = this;
}
public void setRightGridView(SplitGridView<M> rightGridView) {
this.rightGridView = rightGridView;
this.leftGridView = this;
}
@Override
public void onRowSelect(final int rowIndex) {
super.onRowSelect(rowIndex);
if (gridSide == GridSide.LEFT) {
// ~~~ workaround, there is something that is causing the issue before this frame.
// TODO This causes a small flicker, but brings it into a working state - do to changing views and reselect
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
rightGridView.onRowSelectSync(rowIndex);
}
});
} else if (gridSide == GridSide.RIGHT) {
leftGridView.onRowSelectSync(rowIndex);
}
}
@Override
public void onRowDeselect(int rowIndex) {
super.onRowDeselect(rowIndex);
if (gridSide == GridSide.LEFT) {
rightGridView.onRowDeselectSync(rowIndex);
} else if (gridSide == GridSide.RIGHT) {
leftGridView.onRowDeselectSync(rowIndex);
}
}
@Override
public void onRowOver(Element row) {
super.onRowOver(row);
int rowIndex = findRowIndex(row);
if (rowIndex < 0) {
return;
}
if (gridSide == GridSide.LEFT) {
Element rightRow = rightGridView.getRow(rowIndex);
rightGridView.onRowOverSync(rightRow);
} else if (gridSide == GridSide.RIGHT) {
Element leftRow = leftGridView.getRow(rowIndex);
leftGridView.onRowOverSync(leftRow);
}
}
@Override
public void onRowOut(Element row) {
super.onRowOut(row);
int rowIndex = findRowIndex(row);
if (rowIndex < 0) {
return;
}
if (gridSide == GridSide.LEFT) {
Element rightRow = rightGridView.getRow(rowIndex);
rightGridView.onRowOutSync(rightRow);
} else if (gridSide == GridSide.RIGHT) {
Element leftRow = leftGridView.getRow(rowIndex);
leftGridView.onRowOutSync(leftRow);
}
}
private void onRowSelectSync(int rowIndex) {
super.onRowSelect(rowIndex);
}
private void onRowOverSync(Element row) {
if (row == null) {
return;
}
super.onRowOver(row);
}
private void onRowOutSync(Element row) {
if (row == null) {
return;
}
super.onRowOut(row);
}
private void onRowDeselectSync(int rowIndex) {
super.onRowDeselect(rowIndex);
}
@Override
protected void doSort(int colIndex, SortDir sortDir) {
// Left delegate to the right Grid loader
if (gridSide == GridSide.LEFT) {
ColumnConfig<M, ?> column = cm.getColumn(colIndex);
ValueProvider<? super M, ?> vp = column.getValueProvider();
SortInfoBean bean = new SortInfoBean(vp, sortDir);
if (sortDir == null && sortState != null && vp.getPath().equals(sortState.getSortField())) {
bean.setSortDir(sortState.getSortDir() == SortDir.ASC ? SortDir.DESC : SortDir.ASC);
} else if (sortDir == null) {
bean.setSortDir(SortDir.ASC);
}
grid.getLoader().clearSortInfo();
grid.getLoader().addSortInfo(bean);
//maskView();
} else if (gridSide == GridSide.RIGHT) {
super.doSort(colIndex, sortDir);
}
}
@Override
protected void initHeader() {
if (header == null) {
header = new ColumnHeaderExt<M>(grid, cm) {
@Override
protected Menu getContextMenu(int column) {
return super.getContextMenu(column);
}
};
}
// ~~~ workaround for setting context menu upstream
// Use the defined factory in forensics grid
// header.setMenuFactory(new HeaderContextMenuFactory() {
// @Override
// public Menu getMenuForColumn(int columnIndex) {
// return createContextMenu(columnIndex);
// }
// });
header.setSplitterWidth(splitterWidth);
}
@Override
protected void initElements() {
super.initElements();
// hide scroll bar
if (gridSide == GridSide.LEFT) {
//scroller.addClassName(GENERAL_CSS.hideScrollBar());
}
}
/**
* changed visibility
*/
@Override
public void onNoNext(int index) {
super.onNoNext(index);
}
/**
* changed visibility
*/
@Override
protected void onNoPrev() {
super.onNoPrev();
}
/**
* changed visbility
*/
@Override
protected void onHighlightRow(int rowIndex, boolean highlight) {
super.onHighlightRow(rowIndex, highlight);
}
}
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.uibinder.client.UiConstructor;
import com.google.gwt.user.client.ui.TextBox;
import com.sencha.gxt.core.client.gestures.TapGestureRecognizer;
import com.sencha.gxt.core.client.gestures.TouchData;
import com.sencha.gxt.core.client.gestures.TouchEventToGestureAdapter;
import com.sencha.gxt.core.client.resources.CommonStyles;
import com.sencha.gxt.core.client.util.Util;
import com.sencha.gxt.data.shared.loader.BeforeLoadEvent;
import com.sencha.gxt.data.shared.loader.LoadEvent;
import com.sencha.gxt.data.shared.loader.LoadExceptionEvent;
import com.sencha.gxt.data.shared.loader.LoaderHandler;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoadResultBean;
import com.sencha.gxt.data.shared.loader.PagingLoader;
import com.sencha.gxt.messages.client.DefaultMessages;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent.SelectHandler;
import com.sencha.gxt.widget.core.client.toolbar.FillToolItem;
import com.sencha.gxt.widget.core.client.toolbar.LabelToolItem;
import com.sencha.gxt.widget.core.client.toolbar.PagingToolBar.PagingToolBarAppearance;
import com.sencha.gxt.widget.core.client.toolbar.PagingToolBar.PagingToolBarMessages;
import com.sencha.gxt.widget.core.client.toolbar.SeparatorToolItem;
import com.sencha.gxt.widget.core.client.toolbar.ToolBar;
/**
* Copied from PagingToolbar
*/
public class SplitPagingToolBar<M> extends ToolBar {
protected static class DefaultPagingToolBarMessages implements PagingToolBarMessages {
@Override
public String afterPageText(int page) {
return DefaultMessages.getMessages().pagingToolBar_afterPageText(page);
}
@Override
public String beforePageText() {
return DefaultMessages.getMessages().pagingToolBar_beforePageText();
}
@Override
public String displayMessage(int start, int end, int total) {
return DefaultMessages.getMessages().pagingToolBar_displayMsg(start, end, total);
}
@Override
public String emptyMessage() {
return DefaultMessages.getMessages().pagingToolBar_emptyMsg();
}
@Override
public String firstText() {
return DefaultMessages.getMessages().pagingToolBar_firstText();
}
@Override
public String lastText() {
return DefaultMessages.getMessages().pagingToolBar_lastText();
}
@Override
public String nextText() {
return DefaultMessages.getMessages().pagingToolBar_nextText();
}
@Override
public String prevText() {
return DefaultMessages.getMessages().pagingToolBar_prevText();
}
@Override
public String refreshText() {
return DefaultMessages.getMessages().pagingToolBar_refreshText();
}
}
protected int activePage = -1, pages;
protected LabelToolItem beforePage, afterText, displayText;
protected PagingLoadConfig config;
protected TextButton first, prev, next, last, refresh;
protected PagingLoader<PagingLoadConfig, ?> loader;
protected TextBox pageText;
protected boolean showToolTips = true;
protected int start, pageSize, totalLength;
private final PagingToolBarAppearance appearance;
private boolean loading;
private boolean buttonsEnabled;
// flag to track if refresh was clicked since setIcon will steal focus. If it was focused, we must refocus after icon change
private boolean activeRefresh = false;
private LoaderHandler<PagingLoadConfig, ?> handler = new LoaderHandler<PagingLoadConfig, PagingLoadResult<?>>() {
@Override
public void onBeforeLoad(final BeforeLoadEvent<PagingLoadConfig> event) {
loading = true;
doEnableButtons(false);
refresh.setIcon(appearance.loading());
if (activeRefresh) {
refresh.focus();
}
Scheduler.get().scheduleFinally(new ScheduledCommand() {
@Override
public void execute() {
if (event.isCancelled()) {
refresh.setIcon(appearance.refresh());
if (activeRefresh) {
refresh.focus();
}
doEnableButtons(true);
SplitPagingToolBar.this.onLoad(new LoadEvent<PagingLoadConfig, PagingLoadResult<?>>(config,
new PagingLoadResultBean<Object>(null, totalLength, start)));
}
}
});
}
@Override
public void onLoad(LoadEvent<PagingLoadConfig, PagingLoadResult<?>> event) {
refresh.setIcon(appearance.refresh());
if (activeRefresh) {
refresh.focus();
activeRefresh = false;
}
doEnableButtons(true);
SplitPagingToolBar.this.onLoad(event);
}
@Override
public void onLoadException(LoadExceptionEvent<PagingLoadConfig> event) {
refresh.setIcon(appearance.refresh());
if (activeRefresh) {
refresh.focus();
activeRefresh = false;
}
doEnableButtons(true);
//setting this here since we never get into onLoad
loading = false;
}
};
private HandlerRegistration handlerRegistration;
private PagingToolBarMessages messages;
private boolean reuseConfig = true;
/**
* Creates a new paging tool bar.
*
* @param pageSize the page size
*/
@UiConstructor
public SplitPagingToolBar(int pageSize) {
this(GWT.<ToolBarAppearance>create(ToolBarAppearance.class),
GWT.<PagingToolBarAppearance>create(PagingToolBarAppearance.class), pageSize);
}
/**
* Creates a new tool bar.
*
* @param toolBarAppearance the tool bar appearance
* @param appearance the paging tool bar appearance
* @param pageSize the page size
*/
public SplitPagingToolBar(ToolBarAppearance toolBarAppearance, PagingToolBarAppearance appearance, int pageSize) {
super(toolBarAppearance);
this.appearance = appearance;
this.pageSize = pageSize;
//addStyleName("x-paging-toolbar-mark");
first = new TextButton();
first.setIcon(appearance.first());
first.addSelectHandler(new SelectHandler() {
@Override
public void onSelect(SelectEvent event) {
first();
}
});
prev = new TextButton();
prev.setIcon(appearance.prev());
prev.addSelectHandler(new SelectHandler() {
@Override
public void onSelect(SelectEvent event) {
previous();
}
});
next = new TextButton();
next.setIcon(appearance.next());
next.addSelectHandler(new SelectHandler() {
@Override
public void onSelect(SelectEvent event) {
next();
}
});
last = new TextButton();
last.setIcon(appearance.last());
last.addSelectHandler(new SelectHandler() {
@Override
public void onSelect(SelectEvent event) {
last();
}
});
refresh = new TextButton();
refresh.setIcon(appearance.refresh());
refresh.addSelectHandler(new SelectHandler() {
@Override
public void onSelect(SelectEvent event) {
refresh();
}
});
beforePage = new LabelToolItem();
beforePage.setLabel(getMessages().beforePageText());
afterText = new LabelToolItem();
pageText = new TextBox();
pageText.setWidth("30px");
pageText.addKeyDownHandler(new KeyDownHandler() {
public void onKeyDown(KeyDownEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
onPageChange();
}
}
});
new TouchEventToGestureAdapter(pageText, new TapGestureRecognizer() {
@Override
protected void onTap(TouchData touchData) {
super.onTap(touchData);
pageText.setFocus(true);
}
});
displayText = new LabelToolItem();
displayText.addStyleName(CommonStyles.get().nowrap());
addToolTips();
add(first);
add(prev);
add(new SeparatorToolItem());
add(beforePage);
add(pageText);
add(afterText);
add(new SeparatorToolItem());
add(next);
add(last);
add(new SeparatorToolItem());
add(refresh);
add(new FillToolItem());
add(displayText);
}
/**
* Binds the toolbar to the loader.
*
* @param loader the loader
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public void bind(PagingLoader<? extends PagingLoadConfig, ?> loader) {
if (this.loader != null) {
handlerRegistration.removeHandler();
}
this.loader = (PagingLoader) loader;
if (loader != null) {
loader.setLimit(pageSize);
// the loader and the handler have the same generics, the cast is required
// because neither one cares about the data in the load result. Unsure of
// a better way to express this.
handlerRegistration = loader.addLoaderHandler((LoaderHandler) handler);
}
}
/**
* Clears the current toolbar text.
*/
public void clear() {
pageText.setText("");
afterText.setLabel("");
displayText.setLabel("");
}
/**
* Called when a load request is initialed and called after completion of the load request. Subclasses may override as
* needed.
*
* @param enabled the enabled state
*/
protected void doEnableButtons(boolean enabled) {
buttonsEnabled = enabled;
first.setEnabled(enabled);
prev.setEnabled(enabled);
beforePage.setEnabled(enabled);
pageText.setEnabled(enabled);
afterText.setEnabled(enabled);
next.setEnabled(enabled);
last.setEnabled(enabled);
displayText.setEnabled(enabled);
}
/**
* Moves to the first page.
*/
public void first() {
if (!loading) {
doLoadRequest(0, pageSize);
}
}
/**
* Returns the active page.
*
* @return the active page
*/
public int getActivePage() {
return activePage;
}
/**
* Returns the toolbar appearance.
*
* @return the appearance
*/
public PagingToolBarAppearance getPagingToolbarAppearance() {
return appearance;
}
/**
* Returns the toolbar messages.
*
* @return the messages
*/
public PagingToolBarMessages getMessages() {
if (messages == null) {
messages = new DefaultPagingToolBarMessages();
}
return messages;
}
/**
* Returns the current page size.
*
* @return the page size
*/
public int getPageSize() {
return pageSize;
}
/**
* Returns the total number of pages.
*
* @return the total pages
*/
public int getTotalPages() {
return pages;
}
/**
* Returns true if the paging toolbar buttons are enabled.
*
* @return the buttons enabled.
*/
public boolean isButtonsEnabled() {
return buttonsEnabled;
}
/**
* Returns true if the previous load config is reused.
*
* @return the reuse config state
*/
public boolean isReuseConfig() {
return reuseConfig;
}
/**
* Returns true if tooltip are enabled.
*
* @return the show tooltip state
*/
public boolean isShowToolTips() {
return showToolTips;
}
/**
* Moves to the last page.
*/
public void last() {
if (!loading) {
if (totalLength > 0) {
int extra = totalLength % pageSize;
int lastStart = extra > 0 ? (totalLength - extra) : totalLength - pageSize;
doLoadRequest(lastStart, pageSize);
}
}
}
/**
* Moves to the last page.
*/
public void next() {
if (!loading) {
doLoadRequest(start + pageSize, pageSize);
}
}
/**
* Moves the the previous page.
*/
public void previous() {
if (!loading) {
doLoadRequest(Math.max(0, start - pageSize), pageSize);
}
}
/**
* Refreshes the data using the current configuration.
*/
public void refresh() {
if (!loading) {
activeRefresh = true;
doLoadRequest(start, pageSize);
}
}
/**
* Sets the active page (1 to page count inclusive).
*
* @param page the page
*/
public void setActivePage(int page) {
if (page > pages) {
last();
return;
}
if (page != activePage && page > 0 && page <= pages) {
doLoadRequest(--page * pageSize, pageSize);
} else {
pageText.setText(String.valueOf((int) activePage));
}
}
/**
* Sets the toolbar messages.
*
* @param messages the messages
*/
public void setMessages(PagingToolBarMessages messages) {
this.messages = messages;
}
/**
* Sets the current page size. This method does not effect the data currently being displayed. The new page size will
* not be used until the next load request.
*
* @param pageSize the new page size
*/
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
/**
* True to reuse the previous load config (defaults to true).
*
* @param reuseConfig true to reuse the load config
*/
public void setReuseConfig(boolean reuseConfig) {
this.reuseConfig = reuseConfig;
}
/**
* Sets if the button tool tips should be displayed (defaults to true, pre-render).
*
* @param showToolTips true to show tool tips
*/
public void setShowToolTips(boolean showToolTips) {
this.showToolTips = showToolTips;
if (showToolTips) {
addToolTips();
} else {
removeToolTips();
}
}
protected void doLoadRequest(int offset, int limit) {
if (reuseConfig && config != null) {
config.setOffset(offset);
config.setLimit(pageSize);
loader.load(config);
} else {
loader.setLimit(pageSize);
loader.load(offset, limit);
}
}
protected void onLoad(LoadEvent<PagingLoadConfig, PagingLoadResult<?>> event) {
loading = false;
config = event.getLoadConfig();
PagingLoadResult<?> result = event.getLoadResult();
start = result.getOffset();
totalLength = result.getTotalLength();
activePage = (int) Math.ceil((double) (start + pageSize) / pageSize);
pages = totalLength < pageSize ? 1 : (int) Math.ceil((double) totalLength / pageSize);
if (activePage > pages && totalLength > 0) {
last();
return;
} else if (activePage > pages) {
start = 0;
activePage = 1;
}
pageText.setText(String.valueOf((int) activePage));
String after = null, display = null;
after = getMessages().afterPageText(pages);
afterText.setLabel(after);
first.setEnabled(activePage != 1);
prev.setEnabled(activePage != 1);
next.setEnabled(activePage != pages);
last.setEnabled(activePage != pages);
int temp = activePage == pages ? totalLength : start + pageSize;
display = getMessages().displayMessage(start + 1, (int) temp, totalLength);
String msg = display;
if (totalLength == 0) {
msg = getMessages().emptyMessage();
}
displayText.setLabel(msg);
forceLayout();
}
protected void onPageChange() {
String value = pageText.getText();
if (value.equals("") || !Util.isInteger(value)) {
pageText.setText(String.valueOf((int) activePage));
return;
}
int p = Integer.parseInt(value);
setActivePage(p);
}
/**
* Helper method to apply the tool tip messages to built-in toolbar buttons. Additional tooltips can be set by
* overriding {@link #setShowToolTips(boolean)}.
*/
private void addToolTips() {
PagingToolBarMessages m = getMessages();
first.setToolTip(m.firstText());
prev.setToolTip(m.prevText());
next.setToolTip(m.nextText());
last.setToolTip(m.lastText());
refresh.setToolTip(m.refreshText());
}
/**
* Helper method to remove the tool tip messages from built-in toolbar buttons. Additional tooltips can be set by
* overriding {@link #setShowToolTips(boolean)}.
*/
private void removeToolTips() {
first.removeToolTip();
prev.removeToolTip();
next.removeToolTip();
last.removeToolTip();
refresh.removeToolTip();
}
}
@branflake2267
Copy link
Author

@javac9
Copy link

javac9 commented Sep 20, 2019

Hello and thanks for providing this example. One question though: How complete is this example (haven't tried to run it yet) - can it be used in production?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment