Skip to content

Instantly share code, notes, and snippets.

@branflake2267
Created August 29, 2017 20:40
Show Gist options
  • Save branflake2267/6b375ebd7e70e335f45a6fbab9e18d77 to your computer and use it in GitHub Desktop.
Save branflake2267/6b375ebd7e70e335f45a6fbab9e18d77 to your computer and use it in GitHub Desktop.
GXT 4 with filter icon.
/* FilterStyles.gss file with a filter.png file in the same package*/
.filter {
gwt-sprite: "filter";
width: 10px;
height: 10px;
padding-left: 20px;
}
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.google.gwt.core.client.EntryPoint;
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.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.editor.client.Editor.Path;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.resources.client.ImageResource.ImageOptions;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.datepicker.client.CalendarUtil;
import com.sencha.gxt.core.client.GXT;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.client.dom.XElement;
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.PropertyAccess;
import com.sencha.gxt.data.shared.loader.FilterConfig;
import com.sencha.gxt.data.shared.loader.FilterPagingLoadConfig;
import com.sencha.gxt.data.shared.loader.FilterPagingLoadConfigBean;
import com.sencha.gxt.data.shared.loader.LoadResultListStoreBinding;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoader;
import com.sencha.gxt.widget.core.client.ContentPanel;
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.ColumnWidthChangeEvent;
import com.sencha.gxt.widget.core.client.event.ColumnWidthChangeEvent.ColumnWidthChangeHandler;
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.ColumnHeader.Head;
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.grid.filters.Filter;
import com.sencha.gxt.widget.core.client.grid.filters.GridFilters;
import com.sencha.gxt.widget.core.client.grid.filters.StringFilter;
import com.sencha.gxt.widget.core.client.toolbar.PagingToolBar;
public class GridWithFilteringIcon implements EntryPoint {
// provide the icon and css to put the icon to the left of the column header label
public interface MyGridResources extends ClientBundle {
// access to all resources
public static MyGridResources INSTANCE = GWT.create(MyGridResources.class);
// css selectors
interface FilterStyles extends CssResource {
// css style filter selector with sprite
String filter();
}
// access to css selectors
@Source("FilterStyles.gss")
FilterStyles styles();
@Source("Filter.png")
@ImageOptions(width = 16, height = 16)
ImageResource filter();
}
public class GridFiltersExt<M> extends GridFilters<M> {
public GridFiltersExt(PagingLoader<FilterPagingLoadConfig, PagingLoadResult<Stock>> pagingLoader) {
super(pagingLoader);
}
@Override
public void updateColumnHeadings() {
// sets the default style
super.updateColumnHeadings();
GWT.log("updateColumnHeadings()");
// ~~~ Find the element to add the class name
// copied super.updateColumnHeadings()
ColumnModel<M> model = grid.getColumnModel();
int cols = model.getColumnCount();
for (int i = 0; i < cols; i++) {
ColumnConfig<M, ?> config = model.getColumn(i);
if (!config.isHidden()) {
ColumnHeader<M> header = grid.getView().getHeader();
if (header != null) {
Head h = header.getHead(i);
if (h != null && h.isRendered()) {
Filter<M, ?> f = getFilter(config.getValueProvider().getPath());
if (f != null) {
// ~~~ - add a custom class with icon
NodeList<Element> nodes = h.getElement().select("span");
XElement spanElement = nodes.getItem(0).cast();
spanElement.setClassName(MyGridResources.INSTANCE.styles().filter(), f.isActive());
}
}
}
}
}
}
}
public int COUNTER = 0;
private ContentPanel panel;
private PagingLoader<FilterPagingLoadConfig, PagingLoadResult<Stock>> pagingLoader;
private Grid<Stock> grid;
private GridFiltersExt<Stock> gridFilters;
/**
* This is the entry point method.
*/
public void onModuleLoad() {
Viewport viewport = new Viewport();
viewport.setWidget(asWidget());
RootLayoutPanel.get().add(viewport);
}
public Widget asWidget() {
if (panel == null) {
// Inject resources
MyGridResources.INSTANCE.styles().ensureInjected();
StockProperties properties = GWT.create(StockProperties.class);
SafeHtml label;
label = wrapHeaderString("col1");
final ColumnConfig<Stock, String> name = new ColumnConfig<Stock, String>(properties.name(), 60, label);
label = wrapHeaderString("col2");
ColumnConfig<Stock, Double> change = new ColumnConfig<Stock, Double>(properties.change(), 80, label);
label = wrapHeaderString("col3");
ColumnConfig<Stock, String> industry = new ColumnConfig<Stock, String>(properties.industry(), 80, label);
label = wrapHeaderString("col4");
ColumnConfig<Stock, Date> lastUpdated = new ColumnConfig<Stock, Date>(properties.lastTrans(), 80, label);
List<ColumnConfig<Stock, ?>> columns = new ArrayList<ColumnConfig<Stock, ?>>();
columns.add(name);
columns.add(change);
columns.add(industry);
columns.add(lastUpdated);
ColumnModel<Stock> cm = new ColumnModel<Stock>(columns);
cm.addColumnWidthChangeHandler(new ColumnWidthChangeHandler() {
@Override
public void onColumnWidthChange(ColumnWidthChangeEvent event) {
// ~~~ workaround
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
gridFilters.updateColumnHeadings();
}
});
}
});
}
});
ListStore<Stock> store = new ListStore<Stock>(properties.key());
final GridView<Stock> gridview = new GridView<Stock>() {
@Override
public void refresh(boolean headerToo) {
super.refresh(headerToo);
// ~~~ Workaround
gridFilters.updateColumnHeadings();
}
};
gridview.setAutoExpandColumn(name);
grid = new Grid<Stock>(store, cm, gridview);
grid.addAttachHandler(new Handler() {
@Override
public void onAttachOrDetach(AttachEvent event) {
pagingLoader.load();
}
});
grid.getView().setColumnHeader(new WordWrapColumnHeader<Stock>(grid, cm));
pagingLoader = getLoader(store);
grid.setLoader(pagingLoader);
final StringFilter<Stock> nameFilter = new StringFilter<Stock>(properties.name());
gridFilters = new GridFiltersExt<Stock>(pagingLoader);
gridFilters.initPlugin(grid);
gridFilters.addFilter(nameFilter);
VerticalLayoutContainer vlc = new VerticalLayoutContainer();
vlc.add(grid, new VerticalLayoutData(1, 1));
vlc.add(createPagingToolBar(), new VerticalLayoutData(1, -1));
panel = new ContentPanel();
panel.setHeading("Add Icon On Filter Example");
panel.add(vlc);
}
return panel;
}
private PagingLoader<FilterPagingLoadConfig, PagingLoadResult<Stock>> getLoader(ListStore<Stock> store) {
RpcProxy<FilterPagingLoadConfig, PagingLoadResult<Stock>> dataProxy = new RpcProxy<FilterPagingLoadConfig, PagingLoadResult<Stock>>() {
@Override
public void load(FilterPagingLoadConfig loadConfig, AsyncCallback<PagingLoadResult<Stock>> callback) {
getRpcStocks(loadConfig, callback);
}
};
final PagingLoader<FilterPagingLoadConfig, PagingLoadResult<Stock>> loader = new PagingLoader<FilterPagingLoadConfig, PagingLoadResult<Stock>>(
dataProxy);
loader.useLoadConfig(new FilterPagingLoadConfigBean());
loader
.addLoadHandler(new LoadResultListStoreBinding<FilterPagingLoadConfig, Stock, PagingLoadResult<Stock>>(store));
loader.setRemoteSort(true);
return loader;
}
public static final String START_HEADER_WRAPPER = "<span style='white-space: normal;'><b>";
public static final String END_HEADER_WRAPPER = "</b></span>";
public static String getHeaderContents(String hdr) {
String retString = null;
if (hdr != null) {
retString = hdr.replace(START_HEADER_WRAPPER, "");
retString = retString.replace(END_HEADER_WRAPPER, "");
}
return retString;
}
protected SafeHtml wrapHeaderString(String untrustedString) {
if (untrustedString == null)
return null;
SafeHtml escapedString = SafeHtmlUtils.fromString(untrustedString);
SafeHtml safeHtml = SafeHtmlUtils
.fromTrustedString(START_HEADER_WRAPPER + escapedString.asString() + END_HEADER_WRAPPER);
return safeHtml;
}
// ------------------------------------------------------------------------------------------------
// Below are Utility Classes to create Data and a copy of WordWrapColumnHeader from Sencha's website
// ------------------------------------------------------------------------------------------------
public class Stock implements Serializable, Comparable<Stock> {
private Integer id;
private Double change;
private Date date = new Date();
private String industry = getType();
private Double last;
private String name;
private Double open;
private String symbol;
private boolean split = Boolean.valueOf(Math.random() > .5);
public Stock() {
this.id = Integer.valueOf(COUNTER++);
}
public Stock(String name, String symbol, double open, double last, Date date) {
this();
this.name = name;
this.symbol = symbol;
this.change = last - open;
this.open = open;
this.last = last;
this.date = date;
}
public Double getChange() {
return change;
}
public Integer getId() {
return id;
}
public String getIndustry() {
return industry;
}
public Double getLast() {
return last;
}
public Date getLastTrans() {
return date;
}
public String getName() {
return name;
}
public Double getOpen() {
return open;
}
public double getPercentChange() {
return getChange() / getOpen();
}
public String getSymbol() {
return symbol;
}
public boolean isSplit() {
return split;
}
public void setChange(Double change) {
this.change = change;
}
public void setId(Integer id) {
this.id = id;
}
public void setIndustry(String industry) {
this.industry = industry;
}
public void setLast(Double last) {
this.last = last;
}
public void setLastTrans(Date date) {
this.date = date;
}
public void setName(String name) {
this.name = name;
}
public void setOpen(Double open) {
this.open = open;
}
public void setSplit(boolean split) {
this.split = split;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
public String toString() {
return getName();
}
private String getType() {
double r = Math.random();
if (r <= .25) {
return "Auto";
} else if (r > .25 && r <= .50) {
return "Media";
} else if (r > .5 && r <= .75) {
return "Medical";
} else {
return "Tech";
}
}
@Override
public int compareTo(Stock o) {
return (this.getLastTrans().compareTo(o.getLastTrans()));
}
}
public interface StockProperties extends PropertyAccess<Stock> {
@Path("symbol")
ModelKeyProvider<Stock> key();
ValueProvider<Stock, String> name();
ValueProvider<Stock, String> symbol();
ValueProvider<Stock, Double> last();
ValueProvider<Stock, Double> change();
ValueProvider<Stock, Date> lastTrans();
ValueProvider<Stock, String> industry();
}
private void getRpcStocks(FilterPagingLoadConfig loadConfig, AsyncCallback<PagingLoadResult<Stock>> callback) {
final int offset = loadConfig.getOffset();
final int limit = loadConfig.getLimit();
final List<Stock> stocks = getStocks(loadConfig, offset, limit);
PagingLoadResult<Stock> result = new PagingLoadResult<Stock>() {
@Override
public List<Stock> getData() {
return stocks;
}
@Override
public void setTotalLength(int totalLength) {
}
@Override
public void setOffset(int offset) {
}
@Override
public int getTotalLength() {
return stocks.size();
}
@Override
public int getOffset() {
return offset;
}
};
callback.onSuccess(result);
}
public List<Stock> getStocks(FilterPagingLoadConfig loadConfig, int offset, int limit) {
List<Stock> stocks = getStocks(loadConfig);
if (limit > stocks.size()) {
limit = stocks.size() - 1;
}
List<Stock> subList;
try {
subList = stocks.subList(offset, offset + limit);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
// Oops
subList = new ArrayList<Stock>();
}
return subList;
}
private List<Stock> getStocks(FilterPagingLoadConfig loadConfig) {
Date d1 = new Date();
Date d2 = new Date();
CalendarUtil.addDaysToDate(d2, 2);
String filter = getFilter(loadConfig);
List<Stock> stocks = new ArrayList<Stock>();
for (int i = 0; i < 1000; i++) {
Stock stock = new Stock("i=" + i, "ai=" + i, 125.64, 123.43, d1);
if (filter != null && stock.getName().contains(filter)) {
stocks.add(stock);
} else if (filter == null) {
stocks.add(stock);
}
}
return stocks;
}
private String getFilter(FilterPagingLoadConfig loadConfig) {
List<FilterConfig> filters = loadConfig.getFilters();
if (filters == null || filters.isEmpty()) {
return null;
}
FilterConfig fc = filters.get(0);
String filter = fc.getValue();
return filter;
}
private PagingToolBar createPagingToolBar() {
PagingToolBar toolBar = new PagingToolBar(50);
toolBar.bind(pagingLoader);
return toolBar;
}
/**
* A {@link Grid} {@link ColumnHeader} that supports word wrap, enabling long column headings to be displayed in
* multiple rows. Provides options for letting the height of the header default to the height of the tallest heading,
* or to be manually set to a specific pixel value.
* <p />
* In order for a heading to word wrap, the heading must be set to HTML that enables word wrap, e.g.
* <code><span style='white-space: normal;'>A very long heading</span></code>.
*/
public class WordWrapColumnHeader<M> extends ColumnHeader<M> {
protected class WordWrapHead extends ColumnHeader<M>.Head {
@SuppressWarnings({ "rawtypes" })
public WordWrapHead(ColumnConfig column) {
super(column);
getElement().getStyle().setFloat(Style.Float.NONE);
}
}
private int fixedHeight = -1;
/**
* Constructs a new word wrap column header for the specified grid and column model. To configure the grid to use
* the new word wrap column header, invoke {@link GridView#setColumnHeader(ColumnHeader)}, e.g.
* <code>grid.getView().setColumnHeader(new WordWrapColumnHeader<T>(g, cm)</code>.
*
* @param container
* - the grid that contains this word wrap column header
* @param cm
* - the column model that provides the configuration for this column header
*/
public WordWrapColumnHeader(Grid<M> container, ColumnModel<M> cm) {
super(container, cm);
}
/**
* Sets the height of the word wrap column header to a specific value. Generally this is not required as the column
* header sizes itself to the height of the tallest heading. However, there are some use cases where the column
* header height must be set (e.g. linked grids displayed side-by-side, where the column header height of one grid
* must match that of the other).
*
* @param newHeight
* the height of the column header, or -1 to resize the header to the height of it's tallest heading (i.e.
* it's "natural" height)
*/
@Override
public void setHeight(int newHeight) {
if (GXT.isIE()) {
getElement().getOffsetParent().setScrollTop(0);
}
// Do not forward to base class... the parent element must remain unsized
fixedHeight = newHeight;
if (newHeight == -1) {
setNaturalHeight();
} else {
setFixedHeight(newHeight);
}
super.checkHeaderSizeChange();
}
@Override
protected void checkHeaderSizeChange() {
onHeaderSizeChange();
super.checkHeaderSizeChange();
}
@Override
@SuppressWarnings("rawtypes")
protected Head createNewHead(ColumnConfig config) {
return new WordWrapHead(config);
}
private int getContentHeight() {
int height = 0;
int columnCount = cm.getColumnCount();
for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) {
if (!cm.isHidden(columnIndex)) {
Head head = getHead(columnIndex);
XElement inner = head.getElement();
height = Math.max(height, inner.getOffsetHeight());
}
}
// adjust to allow space for sort icon
height += 5;
return height;
}
private void onHeaderSizeChange() {
if (fixedHeight == -1) {
setNaturalHeight();
} else {
setFixedHeight(fixedHeight);
}
}
private void setFixedHeight(int newHeight) {
int offsetHeight = getOffsetHeight();
int contentHeight = getContentHeight();
int deltaHeight = offsetHeight - contentHeight;
newHeight -= deltaHeight;
overrideHeaderHeight = newHeight;
refresh();
}
private void setNaturalHeight() {
overrideHeaderHeight = -1;
refresh();
}
}
}
@branflake2267
Copy link
Author

Icon used
filter

@branflake2267
Copy link
Author

screen shot 2017-08-29 at 1 41 08 pm

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