Skip to content

Instantly share code, notes, and snippets.

@branflake2267
Last active May 11, 2022 15:25
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save branflake2267/7461fd4cd83f53c8c4a3152104213ef6 to your computer and use it in GitHub Desktop.
GXT combo with multiple checkbox for GXT 4.0.3
package com.sencha.gxt.test.client.combo_multi_checkselect4;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.sencha.gxt.core.client.dom.XElement;
/**
* A simple native input checkbox.
* @since 4.0.3
*/
public class InputCheckboxCell extends AbstractCell<Boolean> {
public InputCheckboxCell() {
super("click", "touchstart");
}
/**
* Allow selection when clicked.
*/
@Override
public boolean handlesSelection() {
return true;
}
@Override
public void onBrowserEvent(Context context, Element parent, Boolean value, NativeEvent event,
ValueUpdater<Boolean> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
XElement targetEl = event.getEventTarget().cast();
if (targetEl != null && targetEl.getTagName().equalsIgnoreCase("input")) {
InputElement input = targetEl.cast();
if (input != null) {
valueUpdater.update(input.isChecked());
}
}
}
@Override
public void render(Context context, Boolean value, SafeHtmlBuilder sb) {
String checked = "";
if (value) {
checked = "checked";
}
String s = "<input type='checkbox' " + checked + " style='" + getStyle(context, value) + "' class='"
+ getStyleName(context, value) + "' />";
sb.append(SafeHtmlUtils.fromTrustedString(s));
}
/**
* Provide the input with a style attribute.
* @param value the checked value
* @param context the cell context
*
* @return styles
*/
protected String getStyle(Context context, Boolean value) {
return "";
}
/**
* Provide the input with a class attribute.
* @param value the checked value
* @param context the cell context
*
* @return the class attribute
*/
protected String getStyleName(Context context, Boolean value) {
return "";
}
}
import java.util.List;
import com.google.gwt.user.client.Event;
import com.sencha.gxt.data.shared.LabelProvider;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.widget.core.client.form.TriggerField;
public class MultiSelectComboBox<T> extends TriggerField<List<T>> {
/**
* Create an instance of the multiple select combo.
*
* @param listStore the possible values for selection.
* @param labelProvider the item label renderer.
*/
public MultiSelectComboBox(ListStore<T> listStore, LabelProvider<T> labelProvider) {
this(new MultiSelectComboBoxCell<T>(listStore, labelProvider));
}
/**
* Create an instance of the mutlti select combo with your own cell and property editor.
*
* @param cell a multi select combobox cell.
* @param propertyEditor a multi select combobox property editor.
*/
public MultiSelectComboBox(MultiSelectComboBoxCell<T> cell) {
super(cell, cell.getPropertyEditor());
}
@Override
protected void onResize(int width, int height) {
super.onResize(width, height);
getCell().syncListViewWidth(getElement());
}
@Override
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
// on key down
}
/**
* Sets the panel's expand state.
*
* @param expand true to expand
*/
public void setExpanded(boolean expand) {
if (expand) {
getCell().expand(getElement(), getValue());
} else {
getCell().collapse();
}
}
@Override
public MultiSelectComboBoxCell<T> getCell() {
return (MultiSelectComboBoxCell<T>) super.getCell();
}
}
package com.sencha.gxt.test.client.combo_multi_checkselect4;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.CompositeCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.HasCell;
import com.google.gwt.cell.client.TextCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.ui.RootPanel;
import com.sencha.gxt.cell.core.client.form.TriggerFieldCell;
import com.sencha.gxt.core.client.IdentityValueProvider;
import com.sencha.gxt.core.client.Style.Anchor;
import com.sencha.gxt.core.client.Style.AnchorAlignment;
import com.sencha.gxt.core.client.Style.HideMode;
import com.sencha.gxt.core.client.Style.SelectionMode;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.client.dom.XDOM;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.util.BaseEventPreview;
import com.sencha.gxt.data.shared.LabelProvider;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.widget.core.client.ListView;
import com.sencha.gxt.widget.core.client.ListViewSelectionModel;
import com.sencha.gxt.widget.core.client.event.XEvent;
import com.sencha.gxt.widget.core.client.form.PropertyEditor;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent;
import com.sencha.gxt.widget.core.client.selection.SelectionChangedEvent.SelectionChangedHandler;
public class MultiSelectComboBoxCell<T> extends TriggerFieldCell<List<T>> {
/**
* Render and parse values to and from the input.
*/
public static class MultiComboPropertyEditor<T> extends PropertyEditor<List<T>> {
protected MultiSelectComboBoxCell<T> cell;
protected LabelProvider<T> labelProvider;
public MultiComboPropertyEditor(MultiSelectComboBoxCell<T> cell, LabelProvider<T> labelProvider) {
this.cell = cell;
this.labelProvider = labelProvider;
}
/**
* Used to render the values into the input
*/
@Override
public String render(List<T> objects) {
if (objects == null) {
return "";
}
StringBuffer sb = new StringBuffer();
for (int i = 0; i < objects.size(); i++) {
T item = objects.get(i);
String label = labelProvider.getLabel(item);
sb.append(label);
if (i < objects.size() - 1) {
sb.append(getItemSeparator());
}
}
return sb.toString();
}
/**
* Used to parse the values from the inputs. Instead use the cells selected list values.
*/
@Override
public List<T> parse(CharSequence text) throws ParseException {
return cell.getValues();
}
protected String getItemSeparator() {
return ", ";
}
}
public class ItemCheckBoxCellHasCell<T> implements HasCell<T, Boolean> {
private ListViewSelectionModel<T> selectionModel;
public ItemCheckBoxCellHasCell(ListViewSelectionModel<T> selectionModel) {
this.selectionModel = selectionModel;
}
@Override
public Cell<Boolean> getCell() {
return new InputCheckboxCell();
}
@Override
public FieldUpdater<T, Boolean> getFieldUpdater() {
return new FieldUpdater<T, Boolean>() {
@Override
public void update(int index, T object, Boolean value) {
if (value) {
selectionModel.select(object, true);
} else {
selectionModel.deselect(object);
}
}
};
}
@Override
public Boolean getValue(T item) {
return selectionModel.isSelected(item);
}
}
public class ItemTextHasCell<T> implements HasCell<T, String> {
protected TextCell cell = new TextCell();
protected LabelProvider<T> labelProvider;
public ItemTextHasCell(LabelProvider<T> labelProvider) {
this.labelProvider = labelProvider;
}
@Override
public Cell<String> getCell() {
return cell;
}
@Override
public FieldUpdater<T, String> getFieldUpdater() {
return new FieldUpdater<T, String>() {
@Override
public void update(int index, T object, String value) {
}
};
}
@Override
public String getValue(T object) {
return labelProvider.getLabel(object);
}
}
public static class ListViewSelectionModelExt<T> extends ListViewSelectionModel<T> {
public ListViewSelectionModelExt() {
super();
setSelectionMode(SelectionMode.MULTI);
}
@Override
protected void doSelect(List<T> models, boolean keepExisting, boolean suppressEvent) {
// always keep existing
super.doSelect(models, true, suppressEvent);
}
@Override
protected void onMouseDown(NativeEvent event) {
XEvent e = event.<XEvent>cast();
XElement target = e.getEventTargetEl();
int selIndex = listView.findElementIndex(target);
if (selIndex == -1 || isLocked()) {
return;
}
T sel = listStore.get(selIndex);
if (sel == null) {
return;
}
boolean isSelected = isSelected(sel);
if (isSelected) {
doDeselect(Collections.singletonList(sel), false);
selected.remove(sel);
} else {
doSelect(Collections.singletonList(sel), true, false);
}
}
}
public static class ListViewExt<T, N> extends ListView<T, N> {
public ListViewExt(ListStore<T> store, ValueProvider<? super T, N> valueProvider, Cell<N> cell) {
super(store, valueProvider, cell);
}
@Override
public void onBrowserEvent(Event event) {
super.onBrowserEvent(event);
switch (event.getTypeInt()) {
case Event.ONCLICK:
onMouseDown(event);
break;
}
}
@Override
protected void onMouseOver(Event ce) {
// don't highlight on mouse over
// super.onMouseOver(ce);
}
}
protected ListViewExt<T, T> listView;
protected ListStore<T> store;
protected LabelProvider<T> labelProvider;
protected BaseEventPreview basePreviewEvent;
protected int listViewMaxHeight = 300;
protected ListViewSelectionModelExt<T> selectionModel;
protected List<T> showListSelectedValues;
protected boolean expanded;
public MultiSelectComboBoxCell(ListStore<T> store, LabelProvider<T> labelProvider) {
super(GWT.<TriggerFieldAppearance>create(TriggerFieldAppearance.class));
this.store = store;
this.labelProvider = labelProvider;
this.propertyEditor = initPropertyEditor(labelProvider);
}
protected PropertyEditor<List<T>> initPropertyEditor(LabelProvider<T> labelProvider) {
return new MultiComboPropertyEditor<T>(this, labelProvider);
}
@Override
protected void onTriggerClick(Context context, XElement parent, NativeEvent event, List<T> values,
ValueUpdater<List<T>> updater) {
super.onTriggerClick(context, parent, event, values, updater);
if (!expanded) {
saveContext(context, parent, event, updater, values);
expand(parent, values);
} else {
collapse();
}
}
/**
* Show the list view with the selected values.
*
* @param parent widget consuming this cell.
* @param values to be selected.
*/
public void expand(XElement parent, List<T> values) {
this.showListSelectedValues = values;
if (!expanded) {
configureListView();
showListView();
}
}
/**
* Set the selected values.
*
* @param values to be selected.
*/
public void setValues(List<T> values) {
selectionModel.setSelection(values);
}
/**
* Returns the selected values.
*
* @return the selected values.
*/
public List<T> getValues() {
return selectionModel.getSelection();
}
/**
* Sets the max height of the expanded list view.
*
* @param listViewMaxHeight sets the max height of the view.
*/
public void setListViewMaxHeight(int listViewMaxHeight) {
this.listViewMaxHeight = listViewMaxHeight;
}
/**
* Sync width to a parent widget.
*
* @param syncWidthToParentWidgetElement the widget to sync the width to.
*/
public void syncListViewWidth(XElement syncWidthToParentWidgetElement) {
if (!expanded) {
return;
}
listView.setWidth(getListViewWidth(syncWidthToParentWidgetElement));
}
/**
* Initialize the list view.
*/
protected void configureListView() {
if (listView != null) {
return;
}
selectionModel = new ListViewSelectionModelExt<>();
selectionModel.addSelectionChangedHandler(new SelectionChangedHandler<T>() {
@Override
public void onSelectionChanged(SelectionChangedEvent<T> event) {
listView.refresh();
List<T> selectedItems = event.getSelection();
updateSelectedItems(selectedItems);
}
});
listView = initListView();
basePreviewEvent = new BaseEventPreview() {
@Override
protected boolean onPreview(NativePreviewEvent event) {
Element target = event.getNativeEvent().getEventTarget().cast();
switch (event.getTypeInt()) {
case Event.ONSCROLL:
case Event.ONMOUSEWHEEL:
case Event.ONMOUSEDOWN:
// collapse if the mouse down is not on list or parent widget
if (listView != null && lastParent != null && !listView.getElement().isOrHasChild(target) && !lastParent.isOrHasChild(target)) {
collapse();
}
break;
}
return true;
}
};
basePreviewEvent.setAutoHide(false);
}
protected void updateSelectedItems(List<T> selectedItems) {
if (lastValueUpdater == null) {
return;
}
lastValueUpdater.update(selectedItems);
String renderedValues = getPropertyEditor().render(selectedItems);
setText(lastParent, renderedValues);
}
protected ListViewExt<T, T> initListView() {
List<HasCell<T, ?>> cells = new ArrayList<HasCell<T, ?>>();
cells.add(initCheckBoxCell(selectionModel));
cells.add(initTextCell(labelProvider));
// add two cells to layout a checkbox and label - [] label
CompositeCell<T> listViewCell = initCompositeCell(cells);
listView = new ListViewExt<T, T>(store, new IdentityValueProvider<T>(XDOM.getUniqueId()), listViewCell) {
@Override
protected void onAfterFirstAttach() {
super.onAfterFirstAttach();
// This can cause a lock up if called over and over
selectionModel.select(showListSelectedValues, false);
}
};
listView.setSelectionModel(selectionModel);
listView.setHideMode(HideMode.OFFSETS);
listView.setSelectOnOver(false);
listView.setVisible(false);
listView.setBorders(true);
return listView;
}
protected CompositeCell<T> initCompositeCell(List<HasCell<T, ?>> cells) {
return new CompositeCell<T>(cells) {
@Override
protected <X> void render(Context context, T value, SafeHtmlBuilder sb, HasCell<T, X> hasCell) {
// draw the cells in a line
Cell<X> cell = hasCell.getCell();
sb.appendHtmlConstant("<span style='display: inline-block; vertical-align:middle;'>");
cell.render(context, hasCell.getValue(value), sb);
sb.appendHtmlConstant("</span>&nbsp;");
}
};
}
protected HasCell<T, ?> initCheckBoxCell(ListViewSelectionModelExt<T> selectionModel) {
return new ItemCheckBoxCellHasCell<T>(selectionModel);
}
protected HasCell<T, ?> initTextCell(LabelProvider<T> labelProvider) {
return new ItemTextHasCell<T>(labelProvider);
}
protected void showListView() {
expanded = true;
RootPanel.get().add(listView);
listView.getElement().getStyle().setProperty("maxHeight", getListViewMaxHeight(lastParent));
listView.setWidth(getListViewWidth(lastParent));
listView.getElement().makePositionable(true);
listView.setVisible(true);
basePreviewEvent.add();
AnchorAlignment anchorAlignment = new AnchorAlignment(Anchor.TOP_LEFT, Anchor.BOTTOM_LEFT, true);
listView.getElement().alignTo(lastParent, anchorAlignment, 0, 0);
}
protected int getListViewWidth(XElement parent) {
return parent.getOffsetWidth();
}
protected String getListViewMaxHeight(XElement parent) {
return listViewMaxHeight + "px";
}
protected void collapse() {
doTriggerBlur();
basePreviewEvent.remove();
listView.setVisible(false);
clearContext();
RootPanel.get().remove(listView);
expanded = false;
}
protected void doTriggerBlur() {
doTriggerBlur(lastContext, lastParent, lastValue, lastValueUpdater);
}
protected void doTriggerBlur(Context context, XElement parent, List<T> value, ValueUpdater<List<T>> valueUpdater) {
triggerBlur(context, parent, value, valueUpdater);
onBlur(context, parent, value, null, valueUpdater);
}
@Override
protected void saveContext(Context context, Element parent, NativeEvent event, ValueUpdater<List<T>> valueUpdater,
List<T> value) {
super.saveContext(context, parent, event, valueUpdater, value);
}
@Override
protected void clearContext() {
if (expanded) {
return;
}
super.clearContext();
}
@Override
protected void onFocus(Context context, XElement parent, List<T> value, NativeEvent event,
ValueUpdater<List<T>> valueUpdater) {
super.onFocus(context, parent, value, event, valueUpdater);
saveContext(context, parent, event, valueUpdater, value);
}
}
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.RootPanel;
import com.sencha.gxt.core.client.util.Margins;
import com.sencha.gxt.data.shared.LabelProvider;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.test.client.combo_multi_checkselect2.Data;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.container.BorderLayoutContainer;
import com.sencha.gxt.widget.core.client.container.BorderLayoutContainer.BorderLayoutData;
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.info.Info;
public class MultiSelectComboTest4 implements EntryPoint {
@Override
public void onModuleLoad() {
List<Data> values = new ArrayList<Data>();
values.add(new Data("0", "1"));
//values.add(new Data("5", "102"));
//values.add(new Data("7", "234"));
List<Data> selectionValues = new ArrayList<Data>();
selectionValues.add(new Data("0", "1"));
selectionValues.add(new Data("1", "2"));
selectionValues.add(new Data("2", "3"));
selectionValues.add(new Data("3", "4"));
selectionValues.add(new Data("4", "101"));
selectionValues.add(new Data("5", "102"));
selectionValues.add(new Data("6", "103"));
selectionValues.add(new Data("7", "234"));
selectionValues.add(new Data("8", "235"));
selectionValues.add(new Data("9", "236"));
for (int i = 1000; i < 1500; i++) {
selectionValues.add(new Data(i + "", "Item: " + i));
}
ListStore<Data> listStore = new ListStore<Data>(new ModelKeyProvider<Data>() {
@Override
public String getKey(Data item) {
return item.getId().toString();
}
});
listStore.addAll(selectionValues);
LabelProvider<Data> labelProvider = new LabelProvider<Data>() {
@Override
public String getLabel(Data item) {
return item.getName();
}
};
final MultiSelectComboBox<Data> combo = new MultiSelectComboBox<Data>(listStore, labelProvider);
combo.setValue(values);
final TextButton button = new TextButton("Get Values");
button.addSelectHandler(new SelectHandler() {
@Override
public void onSelect(SelectEvent event) {
List<Data> values = combo.getValue();
Info.display("", "values=" + values.size() + " values=" + values);
}
});
VerticalLayoutContainer vlc = new VerticalLayoutContainer();
vlc.getElement().getStyle().setProperty("border", "1px solid green");
vlc.add(combo, new VerticalLayoutData(1, 50, new Margins(10)));
vlc.add(button, new VerticalLayoutData(1, -1, new Margins(10)));
BorderLayoutData layoutData = new BorderLayoutData(100);
BorderLayoutContainer blc = new BorderLayoutContainer();
blc.setNorthWidget(new SimpleContainer(), layoutData);
blc.setEastWidget(new SimpleContainer(), layoutData);
blc.setWestWidget(new SimpleContainer(), layoutData);
blc.setWidget(vlc);
Viewport vp = new Viewport();
vp.add(blc);
RootPanel.get().add(vp);
}
}
@branflake2267
Copy link
Author

screen shot 2017-08-10 at 12 15 59 pm

@StanislavPonomarev
Copy link

Hello, dear Brandon. Please, help me with your MultiSelectComboBox. How can I attach SelectionHandler, or order Handler to handle Selection Events to this MultiSelectComboBox ? I need to handle selection events, but all handlers i tryed with this component had no effect. Can you explain in some words possibilityes of selection handling in MultiSelectComboBox ?

@morettoni
Copy link

Looks like the list of the selected items is not always well refreshed.
If I do some select/clear/select cycles the text is correct, but when you open the combobox list nothing is elected, even if the getValue list returns the right values. 😔

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