Search code examples
javagwtgxt

GXT: ComboBoxCell not editable


I want a simple select cell (part of a grid). I want to be able to select one of the available options, but not be able to input an arbitrary value. I used setForceSelection(true); but I could still edit the field. Although this removes the value, if it's not one of the predefined ones, it's not the behavior I am after. I need it to behave exactly like a select element. Do I need to use another component? Which one?

The column light is the one I'm referring to. http://www.sencha.com/examples/#ExamplePlace:cellgrid

Thank you in advance.

Here is the code of the demo. First comes the part that I'm interested in and then follows the full example for future reference. Please provide an edit on this sample so I can adjust it for my code.

  ColumnConfig<Plant, String> lightColumn = new ColumnConfig<Plant, String>(properties.light(), 130, "Light");
  lightColumn.setColumnTextStyle(fieldPaddingStyle);

  ComboBoxCell<String> lightCombo = new ComboBoxCell<String>(lights, new LabelProvider<String>() {
    @Override
    public String getLabel(String item) {
      return item;
    }
  });
  lightCombo.addSelectionHandler(new SelectionHandler<String>() {

    @Override
    public void onSelection(SelectionEvent<String> event) {
      CellSelectionEvent<String> sel = (CellSelectionEvent<String>) event;
      Plant p = store.get(sel.getContext().getIndex());
      Info.display("Lightness Selected", p.getName() + " selected " + event.getSelectedItem());
    }
  });
  lightCombo.setTriggerAction(TriggerAction.ALL);
  lightCombo.setForceSelection(true);

  lightColumn.setCell(lightCombo);
  lightCombo.setWidth(110);

/**
 * Sencha GXT 3.1.2 - Sencha for GWT
 * Copyright(c) 2007-2014, Sencha, Inc.
 * licensing@sencha.com
 *
 * http://www.sencha.com/products/gxt/license/
 */
package com.sencha.gxt.explorer.client.grid;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.google.gwt.cell.client.Cell.Context;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.editor.client.Editor.Path;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.i18n.shared.DateTimeFormat;
import com.google.gwt.i18n.shared.DateTimeFormat.PredefinedFormat;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safecss.shared.SafeStylesUtils;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.Random;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.cell.core.client.ProgressBarCell;
import com.sencha.gxt.cell.core.client.ResizeCell;
import com.sencha.gxt.cell.core.client.SliderCell;
import com.sencha.gxt.cell.core.client.TextButtonCell;
import com.sencha.gxt.cell.core.client.form.ComboBoxCell;
import com.sencha.gxt.cell.core.client.form.ComboBoxCell.TriggerAction;
import com.sencha.gxt.cell.core.client.form.DateCell;
import com.sencha.gxt.core.client.Style.Side;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.client.resources.CommonStyles;
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.data.shared.PropertyAccess;
import com.sencha.gxt.examples.resources.client.TestData;
import com.sencha.gxt.examples.resources.client.model.Plant;
import com.sencha.gxt.explorer.client.model.Example.Detail;
import com.sencha.gxt.theme.base.client.colorpalette.ColorPaletteBaseAppearance;
import com.sencha.gxt.widget.core.client.ColorPaletteCell;
import com.sencha.gxt.widget.core.client.ColorPaletteCell.ColorPaletteAppearance;
import com.sencha.gxt.widget.core.client.FramedPanel;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.container.BoxLayoutContainer.BoxLayoutPack;
import com.sencha.gxt.widget.core.client.event.CellSelectionEvent;
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.event.SelectEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent.SelectHandler;
import com.sencha.gxt.widget.core.client.form.DateTimePropertyEditor;
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.info.Info;
import com.sencha.gxt.widget.core.client.tips.ToolTipConfig;

public class CellGridExample implements IsWidget, EntryPoint {

  private static final String[] COLORS = new String[]{"161616", "002241", "006874", "82a700", "bbc039", "f3f1cd"};

  interface PlaceProperties extends PropertyAccess<Plant> {
    ValueProvider<Plant, Date> available();

    @Path("name")
    ModelKeyProvider<Plant> key();

    ValueProvider<Plant, String> name();

    ValueProvider<Plant, Integer> difficulty();

    ValueProvider<Plant, Double> progress();

    ValueProvider<Plant, String> color();

    ValueProvider<Plant, String> light();
  }

  private static final PlaceProperties properties = GWT.create(PlaceProperties.class);
  private ListStore<Plant> store;
  private FramedPanel panel;

  public class CellColumnResizer<M, T> implements ColumnWidthChangeHandler {

    private Grid<M> grid;
    private ColumnConfig<M, T> column;
    private ResizeCell<T> cell;

    public CellColumnResizer(Grid<M> grid, ColumnConfig<M, T> column, ResizeCell<T> cell) {
      this.grid = grid;
      this.column = column;
      this.cell = cell;
    }

    @Override
    public void onColumnWidthChange(ColumnWidthChangeEvent event) {
      if (column == event.getColumnConfig()) {
        int w = event.getColumnConfig().getWidth();
        int rows = store.size();

        int col = grid.getColumnModel().indexOf(column);

        cell.setWidth(w - 20);

        ListStore<M> store = grid.getStore();

        for (int i = 0; i < rows; i++) {
          M p = grid.getStore().get(i);

          // option 1 could be better for force fit where all columns are
          // resized
          // would need to run deferred using DelayedTask to ensure only run
          // once
          // grid.getStore().update(p);

          // option 2
          Element parent = grid.getView().getCell(i, col);
          if (parent != null) {
            parent = parent.getFirstChildElement();
            SafeHtmlBuilder sb = new SafeHtmlBuilder();
            cell.render(new Context(i, col, store.getKeyProvider().getKey(p)), column.getValueProvider().getValue(p),
                sb);
            parent.setInnerHTML(sb.toSafeHtml().asString());
          }

        }
      }
    }

  }

  @Override
  public Widget asWidget() {
    if (panel == null) {
      // reduce the padding on text element as we have widgets in the cells
      SafeStyles btnPaddingStyle = SafeStylesUtils.fromTrustedString("padding: 1px 3px 0;");
      SafeStyles fieldPaddingStyle = SafeStylesUtils.fromTrustedString("padding: 2px 3px;");

      ColumnConfig<Plant, String> nameColumn = new ColumnConfig<Plant, String>(properties.name(), 100, "Name");
      // IMPORTANT we want the text element (cell parent) to only be as wide as
      // the cell and not fill the cell
      nameColumn.setColumnTextClassName(CommonStyles.get().inlineBlock());
      nameColumn.setColumnTextStyle(btnPaddingStyle);

      TextButtonCell button = new TextButtonCell();
      button.addSelectHandler(new SelectHandler() {

        @Override
        public void onSelect(SelectEvent event) {
          Context c = event.getContext();
          int row = c.getIndex();
          Plant p = store.get(row);
          Info.display("Event", "The " + p.getName() + " was clicked.");
        }
      });
      nameColumn.setCell(button);

      DateCell dateCell = new DateCell();
      dateCell.getDatePicker().addValueChangeHandler(new ValueChangeHandler<Date>() {

        @Override
        public void onValueChange(ValueChangeEvent<Date> event) {
          Info.display("Date Selected",
              "You selected " + DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT).format(event.getValue()));
        }
      });
      dateCell.setPropertyEditor(new DateTimePropertyEditor(DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT)));

      ColumnConfig<Plant, Date> availableColumn = new ColumnConfig<Plant, Date>(properties.available(), 170, "Date");
      availableColumn.setColumnTextStyle(fieldPaddingStyle);
      availableColumn.setCell(dateCell);

      ListStore<String> lights = new ListStore<String>(new ModelKeyProvider<String>() {
        @Override
        public String getKey(String item) {
          return item;
        }
      });

      lights.add("Mostly Shady");
      lights.add("Mostly Sunny");
      lights.add("Shade");
      lights.add("Sunny");
      lights.add("Sun or Shade");

      ColumnConfig<Plant, String> lightColumn = new ColumnConfig<Plant, String>(properties.light(), 130, "Light");
      lightColumn.setColumnTextStyle(fieldPaddingStyle);

      ComboBoxCell<String> lightCombo = new ComboBoxCell<String>(lights, new LabelProvider<String>() {
        @Override
        public String getLabel(String item) {
          return item;
        }
      });
      lightCombo.addSelectionHandler(new SelectionHandler<String>() {

        @Override
        public void onSelection(SelectionEvent<String> event) {
          CellSelectionEvent<String> sel = (CellSelectionEvent<String>) event;
          Plant p = store.get(sel.getContext().getIndex());
          Info.display("Lightness Selected", p.getName() + " selected " + event.getSelectedItem());
        }
      });
      lightCombo.setTriggerAction(TriggerAction.ALL);
      lightCombo.setForceSelection(true);

      lightColumn.setCell(lightCombo);
      lightCombo.setWidth(110);


      ColumnConfig<Plant, String> colorColumn = new ColumnConfig<Plant, String>(properties.color(), 140, "Color");
      colorColumn.setColumnTextStyle(fieldPaddingStyle);

      // This next line only works with any appearance that extends from Base
      ColorPaletteBaseAppearance appearance = GWT.create(ColorPaletteAppearance.class);
      appearance.setColumnCount(6);

      ColorPaletteCell colorPalette = new ColorPaletteCell(appearance, COLORS, COLORS) {
        @Override
        public boolean handlesSelection() {
          return true;
        }
      };
      colorPalette.addSelectionHandler(new SelectionHandler<String>() {

        @Override
        public void onSelection(SelectionEvent<String> event) {
          Info.display("Color Selected", "You selected " + event.getSelectedItem());
        }
      });
      colorColumn.setCell(colorPalette);

      ColumnConfig<Plant, Integer> difficultyColumn = new ColumnConfig<Plant, Integer>(properties.difficulty(), 150,
          "Durability");
      difficultyColumn.setColumnTextStyle(fieldPaddingStyle);

      // Custom slider tooltip configuration, which displays the tooltip to the right of the control.
      ToolTipConfig tooltipConfig = new ToolTipConfig();
      tooltipConfig.setAnchor(Side.LEFT);
      tooltipConfig.setAnchorArrow(false);
      tooltipConfig.setMouseOffsetX(25);
      tooltipConfig.setMouseOffsetY(0);
      tooltipConfig.setDismissDelay(1000);

      SliderCell slider = new SliderCell() {
        @Override
        public boolean handlesSelection() {
          return true;
        }
      };
      slider.setToolTipConfig(tooltipConfig);
      slider.setWidth(140);
      difficultyColumn.setCell(slider);

      final ColumnConfig<Plant, Double> progressColumn = new ColumnConfig<Plant, Double>(properties.progress(), 150,
          "Progress");
      progressColumn.setColumnTextStyle(fieldPaddingStyle);
      final ProgressBarCell progress = new ProgressBarCell() {
        @Override
        public boolean handlesSelection() {
          return true;
        }
      };
      progress.setProgressText("{0}% Complete");
      progress.setWidth(140);
      progressColumn.setCell(progress);

      List<ColumnConfig<Plant, ?>> l = new ArrayList<ColumnConfig<Plant, ?>>();
      l.add(nameColumn);
      l.add(availableColumn);
      l.add(lightColumn);
      l.add(colorColumn);
      l.add(difficultyColumn);
      l.add(progressColumn);
      ColumnModel<Plant> cm = new ColumnModel<Plant>(l);

      store = new ListStore<Plant>(properties.key());

      List<Plant> plants = new ArrayList<Plant>(TestData.getPlants());
      for (Plant p : plants) {
        p.setColor(COLORS[Random.nextInt(4)]);
      }

      store.addAll(plants);

      final Grid<Plant> grid = new Grid<Plant>(store, cm);
      grid.setBorders(true);
      grid.getView().setAutoExpandColumn(nameColumn);
      grid.getView().setTrackMouseOver(false);

      grid.getColumnModel().addColumnWidthChangeHandler(new CellColumnResizer<Plant, Double>(grid, progressColumn, progress));

      panel = new FramedPanel();
      panel.setHeadingText("Cell Grid Example");
      panel.setWidget(grid);
      panel.setPixelSize(920, 410);
      panel.addStyleName("margin-10");

      panel.setButtonAlign(BoxLayoutPack.CENTER);
      panel.addButton(new TextButton("Reset", new SelectHandler() {

        @Override
        public void onSelect(SelectEvent event) {
          store.rejectChanges();
        }
      }));

      panel.addButton(new TextButton("Save", new SelectHandler() {

        @Override
        public void onSelect(SelectEvent event) {
          store.commitChanges();
        }
      }));
    }
    return panel;
  }

  @Override
  public void onModuleLoad() {
    RootPanel.get().add(asWidget());
  }
}

Solution

  • Since no one answered the question for the gxt version I'm using (GXT 3.1.0), in which you need the parent of the combobox in order to set it to not editable ( public void setEditable(XElement parent, boolean editable) ). This worked for me

    private native void JSNIsetEditable(TriggerFieldCell<String> combobox, boolean editable) /*-{
        combobox.@com.sencha.gxt.cell.core.client.form.TriggerFieldCell::editable = editable;
    }-*/;
    

    I'm passing a ComboBoxCell but this class extends TriggerFieldCell which is the one that the editable variable belongs to.