Created
August 7, 2018 00:21
-
-
Save branflake2267/ec514749cf4322c0b6b4a12b390c8c97 to your computer and use it in GitHub Desktop.
GXT 4.0.3 workaround for GridInlineEditing and Grid DND (Draggable). EXTGWT-5679
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.sencha.gxt.widget.core.client.grid.editing; | |
| import java.util.HashMap; | |
| import java.util.Map; | |
| import com.google.gwt.cell.client.Cell; | |
| 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.NativeEvent; | |
| import com.google.gwt.event.dom.client.ClickEvent; | |
| import com.google.gwt.event.dom.client.ClickHandler; | |
| import com.google.gwt.event.dom.client.DoubleClickEvent; | |
| import com.google.gwt.event.dom.client.DoubleClickHandler; | |
| import com.google.gwt.event.dom.client.MouseDownEvent; | |
| import com.google.gwt.event.dom.client.MouseDownHandler; | |
| import com.google.gwt.event.dom.client.MouseUpEvent; | |
| import com.google.gwt.event.dom.client.MouseUpHandler; | |
| import com.google.gwt.event.dom.client.ScrollEvent; | |
| import com.google.gwt.event.dom.client.ScrollHandler; | |
| import com.google.gwt.event.logical.shared.AttachEvent; | |
| import com.google.gwt.event.shared.GwtEvent; | |
| import com.google.gwt.event.shared.HandlerManager; | |
| import com.google.gwt.event.shared.HandlerRegistration; | |
| import com.google.gwt.safehtml.shared.SafeHtml; | |
| import com.google.gwt.safehtml.shared.SafeHtmlBuilder; | |
| import com.google.gwt.user.client.Timer; | |
| import com.sencha.gxt.core.client.GXT; | |
| import com.sencha.gxt.core.client.dom.XElement; | |
| import com.sencha.gxt.core.client.gestures.DoubleTapGestureRecognizer; | |
| import com.sencha.gxt.core.client.gestures.DoubleTapGestureRecognizer.DoubleTapGestureEvent; | |
| import com.sencha.gxt.core.client.gestures.DoubleTapGestureRecognizer.DoubleTapGestureEvent.DoubleTapGestureEventHandler; | |
| import com.sencha.gxt.core.client.gestures.TapGestureRecognizer.TapGestureEvent; | |
| import com.sencha.gxt.core.client.gestures.TapGestureRecognizer.TapGestureEvent.TapGestureHandler; | |
| import com.sencha.gxt.core.client.gestures.TouchData; | |
| import com.sencha.gxt.core.client.util.KeyNav; | |
| import com.sencha.gxt.core.shared.event.GroupingHandlerRegistration; | |
| import com.sencha.gxt.data.shared.Converter; | |
| import com.sencha.gxt.widget.core.client.event.BeforeCollapseItemEvent; | |
| import com.sencha.gxt.widget.core.client.event.BeforeCollapseItemEvent.BeforeCollapseItemHandler; | |
| import com.sencha.gxt.widget.core.client.event.BeforeCollapseItemEvent.HasBeforeCollapseItemHandlers; | |
| import com.sencha.gxt.widget.core.client.event.BeforeExpandItemEvent; | |
| import com.sencha.gxt.widget.core.client.event.BeforeExpandItemEvent.BeforeExpandItemHandler; | |
| import com.sencha.gxt.widget.core.client.event.BeforeExpandItemEvent.HasBeforeExpandItemHandlers; | |
| import com.sencha.gxt.widget.core.client.event.BeforeStartEditEvent; | |
| import com.sencha.gxt.widget.core.client.event.BeforeStartEditEvent.BeforeStartEditHandler; | |
| import com.sencha.gxt.widget.core.client.event.CancelEditEvent; | |
| import com.sencha.gxt.widget.core.client.event.CancelEditEvent.CancelEditHandler; | |
| import com.sencha.gxt.widget.core.client.event.CollapseItemEvent.HasCollapseItemHandlers; | |
| 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.CompleteEditEvent; | |
| import com.sencha.gxt.widget.core.client.event.CompleteEditEvent.CompleteEditHandler; | |
| import com.sencha.gxt.widget.core.client.event.ExpandItemEvent.HasExpandItemHandlers; | |
| import com.sencha.gxt.widget.core.client.event.HeaderMouseDownEvent; | |
| import com.sencha.gxt.widget.core.client.event.HeaderMouseDownEvent.HeaderMouseDownHandler; | |
| import com.sencha.gxt.widget.core.client.event.ReconfigureEvent; | |
| import com.sencha.gxt.widget.core.client.event.ReconfigureEvent.ReconfigureHandler; | |
| import com.sencha.gxt.widget.core.client.event.StartEditEvent; | |
| import com.sencha.gxt.widget.core.client.event.StartEditEvent.StartEditHandler; | |
| import com.sencha.gxt.widget.core.client.form.IsField; | |
| import com.sencha.gxt.widget.core.client.form.ValueBaseField; | |
| 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.Grid.Callback; | |
| import com.sencha.gxt.widget.core.client.grid.Grid.GridCell; | |
| import com.sencha.gxt.widget.core.client.grid.GridView; | |
| import com.sencha.gxt.widget.core.client.grid.GridView.SelectableTarget; | |
| import com.sencha.gxt.widget.core.client.grid.GroupingView; | |
| import com.sencha.gxt.widget.core.client.tips.ToolTip; | |
| public abstract class AbstractGridEditing<M> implements GridEditing<M> { | |
| protected class AbstractGridEditingKeyNav extends KeyNav { | |
| @Override | |
| public void onEnter(NativeEvent evt) { | |
| AbstractGridEditing.this.onEnter(evt); | |
| } | |
| @Override | |
| public void onEsc(NativeEvent evt) { | |
| AbstractGridEditing.this.onEsc(evt); | |
| } | |
| } | |
| protected class Handler implements AttachEvent.Handler, ScrollHandler, ClickHandler, DoubleClickHandler, | |
| MouseDownHandler, MouseUpHandler, TapGestureHandler, DoubleTapGestureEventHandler, BeforeExpandItemHandler<M>, | |
| BeforeCollapseItemHandler<M>, HeaderMouseDownHandler, ReconfigureHandler, ColumnWidthChangeHandler { | |
| @Override | |
| public void onAttachOrDetach(AttachEvent event) { | |
| AbstractGridEditing.this.onAttachOrDetach(event); | |
| } | |
| @Override | |
| public void onBeforeCollapse(BeforeCollapseItemEvent<M> event) { | |
| completeEditing(); | |
| } | |
| @Override | |
| public void onBeforeExpand(BeforeExpandItemEvent<M> event) { | |
| completeEditing(); | |
| } | |
| @Override | |
| public void onClick(ClickEvent event) { | |
| AbstractGridEditing.this.onClick(event); | |
| } | |
| @Override | |
| public void onColumnWidthChange(ColumnWidthChangeEvent event) { | |
| completeEditing(); | |
| } | |
| @Override | |
| public void onDoubleClick(DoubleClickEvent event) { | |
| AbstractGridEditing.this.onDoubleClick(event); | |
| } | |
| @Override | |
| public void onTapGesture(TapGestureEvent event) { | |
| AbstractGridEditing.this.onTapGesture(event); | |
| } | |
| @Override | |
| public void onDoubleTapGesture(DoubleTapGestureEvent event) { | |
| AbstractGridEditing.this.onDoubleTapGesture(event); | |
| } | |
| @Override | |
| public void onHeaderMouseDown(HeaderMouseDownEvent event) { | |
| handleHeaderMouseDown(event); | |
| } | |
| @Override | |
| public void onMouseDown(MouseDownEvent event) { | |
| AbstractGridEditing.this.onMouseDown(event); | |
| } | |
| @Override | |
| public void onMouseUp(MouseUpEvent event) { | |
| AbstractGridEditing.this.onMouseUp(event); | |
| } | |
| @Override | |
| public void onReconfigure(ReconfigureEvent event) { | |
| AbstractGridEditing.this.onReconfigure(event); | |
| } | |
| @Override | |
| public void onScroll(ScrollEvent event) { | |
| AbstractGridEditing.this.onScroll(event); | |
| } | |
| } | |
| protected GridCell previousActiveCell; | |
| protected GridCell activeCell; | |
| protected ColumnModel<M> columnModel; | |
| protected Map<ColumnConfig<M, ?>, Converter<?, ?>> converterMap = new HashMap<ColumnConfig<M, ?>, Converter<?, ?>>(); | |
| protected Grid<M> editableGrid; | |
| protected Map<ColumnConfig<M, ?>, IsField<?>> editorMap = new HashMap<ColumnConfig<M, ?>, IsField<?>>(); | |
| protected GroupingHandlerRegistration groupRegistration; | |
| protected Handler handler; | |
| protected KeyNav keyNav; | |
| protected boolean bound; | |
| protected boolean lastValid; | |
| protected Timer monitorTimer; | |
| protected ToolTip tooltip; | |
| protected ClicksToEdit clicksToEdit = ClicksToEdit.ONE; | |
| protected HandlerManager handlerManager; | |
| protected boolean errorSummary = true; | |
| protected int monitorPoll = 200; | |
| protected boolean monitorValid = true; | |
| /** | |
| * Used for onTab | |
| */ | |
| protected Callback callback = new Callback() { | |
| @Override | |
| public boolean isSelectable(GridCell cell) { | |
| if (editableGrid != null) { | |
| ColumnModel<M> cm = editableGrid.getColumnModel(); | |
| return !cm.isHidden(cell.getCol()) && editorMap.containsKey(cm.getColumn(cell.getCol())); | |
| } | |
| return false; | |
| } | |
| }; | |
| protected SelectableTarget selectableTarget = new SelectableTarget() { | |
| @Override | |
| public boolean selectableTarget(Element target, Cell<?> cell, int colIndex) { | |
| // Like onTab, determine if the column has an editor and if it does it can be selected. | |
| if (editableGrid != null) { | |
| ColumnModel<M> cm = editableGrid.getColumnModel(); | |
| return !cm.isHidden(colIndex) && editorMap.containsKey(cm.getColumn(colIndex)); | |
| } | |
| // if there is no editor use the default | |
| if (cell != null) { | |
| return cell.handlesSelection(); | |
| } | |
| return true; | |
| } | |
| }; | |
| @Override | |
| public HandlerRegistration addBeforeStartEditHandler(BeforeStartEditHandler<M> handler) { | |
| return ensureHandlers().addHandler(BeforeStartEditEvent.getType(), handler); | |
| } | |
| @Override | |
| public HandlerRegistration addCancelEditHandler(CancelEditHandler<M> handler) { | |
| return ensureHandlers().addHandler(CancelEditEvent.getType(), handler); | |
| } | |
| @Override | |
| public HandlerRegistration addCompleteEditHandler(CompleteEditHandler<M> handler) { | |
| return ensureHandlers().addHandler(CompleteEditEvent.getType(), handler); | |
| } | |
| @Override | |
| public <N, O> void addEditor(ColumnConfig<M, N> columnConfig, Converter<N, O> converter, IsField<O> field) { | |
| assert columnConfig != null && field != null : "You have to define a columnConfig and a field."; | |
| if (converter != null) { | |
| converterMap.put(columnConfig, converter); | |
| } else { | |
| converterMap.remove(columnConfig); | |
| } | |
| editorMap.put(columnConfig, field); | |
| } | |
| @Override | |
| public <N> void addEditor(ColumnConfig<M, N> columnConfig, IsField<N> field) { | |
| addEditor(columnConfig, null, field); | |
| } | |
| @Override | |
| public HandlerRegistration addStartEditHandler(StartEditHandler<M> handler) { | |
| return ensureHandlers().addHandler(StartEditEvent.getType(), handler); | |
| } | |
| @Override | |
| public abstract void cancelEditing(); | |
| /** | |
| * Clears the editors. | |
| */ | |
| public void clearEditors() { | |
| editorMap.clear(); | |
| converterMap.clear(); | |
| } | |
| @Override | |
| public abstract void completeEditing(); | |
| @Override | |
| public void fireEvent(GwtEvent<?> event) { | |
| if (handlerManager != null) { | |
| handlerManager.fireEvent(event); | |
| } | |
| } | |
| /** | |
| * Returns the active cell. | |
| * | |
| * @return the active cell or null if no active edit | |
| */ | |
| public GridCell getActiveCell() { | |
| return activeCell; | |
| } | |
| /** | |
| * Returns the clicks to edit. | |
| * | |
| * @return the clicks to edit | |
| */ | |
| public ClicksToEdit getClicksToEdit() { | |
| return clicksToEdit; | |
| } | |
| @SuppressWarnings("unchecked") | |
| @Override | |
| public <N, O> Converter<N, O> getConverter(ColumnConfig<M, N> columnConfig) { | |
| return (Converter<N, O>) converterMap.get(columnConfig); | |
| } | |
| @Override | |
| public Grid<M> getEditableGrid() { | |
| return editableGrid; | |
| } | |
| @Override | |
| @SuppressWarnings("unchecked") | |
| public <O> IsField<O> getEditor(ColumnConfig<M, ?> columnConfig) { | |
| return (IsField<O>) editorMap.get(columnConfig); | |
| } | |
| /** | |
| * Returns the cell editor. | |
| * | |
| * @param target event target that was focused on | |
| * @return the editor | |
| * @since 4.0.4 | |
| */ | |
| public <O> IsField<O> getEditor(XElement target) { | |
| if (target == null) { | |
| return null; | |
| } | |
| GridCell cell = findCell(target); | |
| if (cell == null) { | |
| return null; | |
| } | |
| ColumnConfig<M, ?> columnConfig = columnModel.getColumn(cell.getCol()); | |
| return getEditor(columnConfig); | |
| } | |
| /** | |
| * Returns the interval that the editor is validated. | |
| * | |
| * @return the interval in ms | |
| */ | |
| public int getMonitorPoll() { | |
| return monitorPoll; | |
| } | |
| @Override | |
| public boolean isEditing() { | |
| return activeCell != null; | |
| } | |
| /** | |
| * Returns true if a tooltip with an error summary is shown. | |
| * | |
| * @return true if a tooltip with an error summary is shown | |
| */ | |
| public boolean isErrorSummary() { | |
| return errorSummary; | |
| } | |
| /** | |
| * Returns true if valid monitoring is enabled. | |
| * | |
| * @return the monitor valid state | |
| */ | |
| public boolean isMonitorValid() { | |
| return monitorValid; | |
| } | |
| @Override | |
| public void removeEditor(ColumnConfig<M, ?> columnConfig) { | |
| editorMap.remove(columnConfig); | |
| converterMap.remove(columnConfig); | |
| } | |
| /** | |
| * Sets the number of clicks to edit (defaults to ONE). | |
| * | |
| * @param clicksToEdit the clicks to edit | |
| */ | |
| public void setClicksToEdit(ClicksToEdit clicksToEdit) { | |
| this.clicksToEdit = clicksToEdit; | |
| } | |
| @Override | |
| public void setEditableGrid(Grid<M> editableGrid) { | |
| cancelEditing(); | |
| if (groupRegistration != null) { | |
| groupRegistration.removeHandler(); | |
| groupRegistration = null; | |
| } | |
| this.editableGrid = editableGrid; | |
| this.columnModel = editableGrid == null ? null : editableGrid.getColumnModel(); | |
| if (keyNav != null && editableGrid == null) { | |
| keyNav.bind(null); | |
| } else { | |
| ensureInternalKeyNav().bind(editableGrid); | |
| } | |
| if (editableGrid != null) { | |
| // Override custom cell.handleSelection() if there is an editor for the cell | |
| editableGrid.getView().setSelectableTarget(selectableTarget); | |
| GroupingHandlerRegistration reg = new GroupingHandlerRegistration(); | |
| reg.add(editableGrid.addDomHandler(ensureInternHandler(), ClickEvent.getType())); | |
| reg.add(editableGrid.addDomHandler(ensureInternHandler(), MouseDownEvent.getType())); | |
| reg.add(editableGrid.addDomHandler(ensureInternHandler(), MouseUpEvent.getType())); | |
| reg.add(editableGrid.addDomHandler(ensureInternHandler(), DoubleClickEvent.getType())); | |
| reg.add(editableGrid.addDomHandler(ensureInternHandler(), ScrollEvent.getType())); | |
| reg.add(editableGrid.addHandler(ensureInternHandler(), HeaderMouseDownEvent.getType())); | |
| reg.add(editableGrid.addHandler(ensureInternHandler(), ReconfigureEvent.getType())); | |
| if (!GXT.isMSEdge() && !GXT.isIE()) { | |
| reg.add(editableGrid.addHandler(ensureInternHandler(), TapGestureEvent.getType())); | |
| reg.add(editableGrid.addHandler(ensureInternHandler(), DoubleTapGestureEvent.getType())); | |
| } | |
| reg.add(editableGrid.getColumnModel().addColumnWidthChangeHandler(ensureInternHandler())); | |
| if (editableGrid instanceof HasExpandItemHandlers) { | |
| @SuppressWarnings({ "rawtypes", "unchecked" }) | |
| HasBeforeExpandItemHandlers<M> hasHandlers = (HasBeforeExpandItemHandlers) editableGrid; | |
| reg.add(hasHandlers.addBeforeExpandHandler(ensureInternHandler())); | |
| } | |
| if (editableGrid instanceof HasCollapseItemHandlers) { | |
| @SuppressWarnings({ "rawtypes", "unchecked" }) | |
| HasBeforeCollapseItemHandlers<M> hasHandlers = (HasBeforeCollapseItemHandlers) editableGrid; | |
| reg.add(hasHandlers.addBeforeCollapseHandler(ensureInternHandler())); | |
| } | |
| groupRegistration = reg; | |
| if (GXT.isTouch()) { | |
| boolean containsDoubleTapGestureRecognizer = false; | |
| for (int i = 0; i < editableGrid.getGestureRecognizerCount(); i++) { | |
| if (editableGrid.getGestureRecognizer(i) instanceof DoubleTapGestureRecognizer) { | |
| containsDoubleTapGestureRecognizer = true; | |
| break; | |
| } | |
| } | |
| if (!containsDoubleTapGestureRecognizer) { | |
| editableGrid.addGestureRecognizer(new DoubleTapGestureRecognizer() { | |
| @Override | |
| protected void onTap(TouchData touchData) { | |
| // do nothing here - we want to rely on the long press or tap GR to fire the single tap | |
| } | |
| @Override | |
| protected void handlePreventDefault(NativeEvent event) { | |
| // don't prevent default here | |
| } | |
| }); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * True to show a tooltip with an error summary (defaults to true) | |
| * | |
| * @param errorSummary true to show an error summary. | |
| */ | |
| public void setErrorSummary(boolean errorSummary) { | |
| this.errorSummary = errorSummary; | |
| } | |
| /** | |
| * Sets the polling interval that the row editor validation is run (defaults to 200). | |
| * | |
| * @param monitorPoll the polling interval in ms in that validation is done | |
| */ | |
| public void setMonitorPoll(int monitorPoll) { | |
| this.monitorPoll = monitorPoll; | |
| } | |
| /** | |
| * True to monitor the valid status of this editor (defaults to true). | |
| * | |
| * @param monitorValid true to monitor this row editor | |
| */ | |
| public void setMonitorValid(boolean monitorValid) { | |
| this.monitorValid = monitorValid; | |
| } | |
| @Override | |
| public abstract void startEditing(GridCell cell); | |
| protected void bindHandler() { | |
| boolean valid = isValid(); | |
| if (!valid) { | |
| lastValid = false; | |
| if (isErrorSummary()) { | |
| showTooltip(getErrorHtml()); | |
| } | |
| } else if (valid && !lastValid) { | |
| hideTooltip(); | |
| lastValid = true; | |
| } | |
| } | |
| protected HandlerManager ensureHandlers() { | |
| if (handlerManager == null) { | |
| handlerManager = new HandlerManager(this); | |
| } | |
| return handlerManager; | |
| } | |
| protected KeyNav ensureInternalKeyNav() { | |
| if (keyNav == null) { | |
| keyNav = new AbstractGridEditingKeyNav(); | |
| } | |
| return keyNav; | |
| } | |
| protected Handler ensureInternHandler() { | |
| if (handler == null) { | |
| handler = new Handler(); | |
| } | |
| return handler; | |
| } | |
| protected GridCell findCell(Element target) { | |
| if (editableGrid != null) { | |
| if (isSelectectableTarget(target) && editableGrid.getView().getBody().isOrHasChild(target)) { | |
| int row = editableGrid.getView().findRowIndex(target); | |
| int col = editableGrid.getView().findCellIndex(target, null); | |
| if (row != -1 && col != -1) { | |
| return new GridCell(row, col); | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Override the ability to turn on and off selection of a cell. See cell.handlesSelection(). | |
| */ | |
| protected boolean isSelectectableTarget(Element target) { | |
| // The cell must be able to handle selection to edit it. | |
| return editableGrid.getView().isSelectableTarget(target); | |
| } | |
| protected void focusCell(int row, int col) { | |
| // this could could be executing after the editor has been removed or hidden | |
| // which can throw an exception in IE | |
| if (getEditableGrid().isAttached()) { | |
| try { | |
| getEditableGrid().getView().focusCell(row, col, true); | |
| } catch (Exception e) { | |
| } | |
| } | |
| } | |
| protected void focusGrid() { | |
| getEditableGrid().focus(); | |
| } | |
| protected abstract SafeHtml getErrorHtml(); | |
| protected void getErrorMessage(IsField<?> field, SafeHtmlBuilder sb, SafeHtml title) { | |
| boolean result = true; | |
| if (field instanceof ValueBaseField) { | |
| ValueBaseField<?> vfield = (ValueBaseField<?>) field; | |
| result = vfield.isCurrentValid(true); | |
| } | |
| if (!result || !field.isValid(true)) { | |
| sb.appendHtmlConstant("<li><b>"); | |
| sb.append(title); | |
| sb.appendHtmlConstant("</b>: "); | |
| sb.appendEscaped(field.getErrors().get(0).getMessage()); | |
| sb.appendHtmlConstant("</li>"); | |
| } | |
| } | |
| protected void handleDoubleEdit(final NativeEvent event) { | |
| startEditing(event); | |
| } | |
| protected <N, O> void handleHeaderMouseDown(HeaderMouseDownEvent event) { | |
| completeEditing(); | |
| } | |
| protected void handleSingleEdit(final NativeEvent event) { | |
| final GridCell cell = findCell(event.getEventTarget().<Element>cast()); | |
| if (cell == null) { | |
| return; | |
| } | |
| // When starting an edit on the same row of an active edit the active edit value | |
| // is lost as the active cell does not complete the edit | |
| // this only happens with TreeGrid, not Grid which could be looked into | |
| if (activeCell != null && activeCell.getRow() == cell.getRow()) { | |
| completeEditing(); | |
| } | |
| // Edit is starting and stopping immediately when leaving another active edit that completes | |
| if (GXT.isiOS()) { | |
| startEditing(cell); | |
| } else { | |
| Scheduler.get().scheduleDeferred(new ScheduledCommand() { | |
| @Override | |
| public void execute() { | |
| startEditing(cell); | |
| } | |
| }); | |
| } | |
| } | |
| protected void hideTooltip() { | |
| if (tooltip != null) { | |
| tooltip.hide(); | |
| tooltip.disable(); | |
| } | |
| } | |
| protected abstract boolean isValid(); | |
| protected void onAttachOrDetach(AttachEvent event) { | |
| if (!event.isAttached()) { | |
| cancelEditing(); | |
| } | |
| } | |
| /** | |
| * @param event The tap event. | |
| * @since 4.0.3 | |
| */ | |
| public void onTapGesture(TapGestureEvent event) { | |
| // If this is a grouping header, ignore this | |
| GridView<M> view = getEditableGrid().getView(); | |
| if (view != null && view instanceof GroupingView) { | |
| XElement target = event.getTouchData().getLastNativeEvent().getEventTarget().cast(); | |
| GroupingView<M> groupingView = (GroupingView<M>) view; | |
| XElement head = groupingView.getGroupingAppearance().findHead(target); | |
| if (head != null) { | |
| // Ignore this if the click is on the grouping header head. | |
| return; | |
| } | |
| } | |
| if (clicksToEdit == ClicksToEdit.ONE) { | |
| // Get the target element from the touch data | |
| XElement element = event.getTouchData().getLastNativeEvent().getEventTarget().<XElement>cast(); | |
| // Retrieve the cell based on the target element | |
| final GridCell cell = findCell(element); | |
| if (cell == null) { | |
| return; | |
| } | |
| startEditing(cell); | |
| event.getTouchData().getLastNativeEvent().preventDefault(); | |
| } | |
| } | |
| /** | |
| * @param event The double tap event. | |
| * @since 4.0.3 | |
| */ | |
| public void onDoubleTapGesture(DoubleTapGestureEvent event) { | |
| // If this is a grouping header, ignore this | |
| GridView<M> view = getEditableGrid().getView(); | |
| if (view != null && view instanceof GroupingView) { | |
| XElement target = event.getTouchData().getLastNativeEvent().getEventTarget().cast(); | |
| GroupingView<M> groupingView = (GroupingView<M>) view; | |
| XElement head = groupingView.getGroupingAppearance().findHead(target); | |
| if (head != null) { | |
| // Ignore this if the click is on the grouping header head. | |
| return; | |
| } | |
| } | |
| if (clicksToEdit == ClicksToEdit.TWO) { | |
| // Get the target element from the touch data | |
| XElement element = event.getTouchData().getLastNativeEvent().getEventTarget().<XElement>cast(); | |
| // Retrieve the cell based on the target element | |
| final GridCell cell = findCell(element); | |
| if (cell == null) { | |
| return; | |
| } | |
| startEditing(cell); | |
| event.getTouchData().getLastNativeEvent().preventDefault(); | |
| } | |
| } | |
| protected void onClick(final ClickEvent event) { | |
| if (clicksToEdit == ClicksToEdit.ONE) { | |
| handleSingleEdit(event.getNativeEvent()); | |
| } | |
| } | |
| protected void onDoubleClick(DoubleClickEvent event) { | |
| if (clicksToEdit == ClicksToEdit.TWO) { | |
| handleDoubleEdit(event.getNativeEvent()); | |
| } | |
| } | |
| protected void onEnter(NativeEvent evt) { | |
| evt.preventDefault(); | |
| GridCell gc = activeCell; | |
| if (gc == null) { | |
| XElement target = evt.getEventTarget().cast(); | |
| gc = findCell(target); | |
| } | |
| if (isEditing()) { | |
| completeEditing(); | |
| } else { | |
| startEditing(gc); | |
| } | |
| if (gc != null) { | |
| focusCell(gc.getRow(), gc.getCol()); | |
| focusGrid(); | |
| } | |
| } | |
| protected void onEsc(NativeEvent evt) { | |
| GridCell gc = activeCell; | |
| cancelEditing(); | |
| if (gc != null) { | |
| focusCell(gc.getRow(), gc.getCol()); | |
| focusGrid(); | |
| } | |
| } | |
| protected void onMouseDown(MouseDownEvent event) { | |
| } | |
| protected void onMouseUp(MouseUpEvent event) { | |
| } | |
| @SuppressWarnings("unchecked") | |
| protected void onReconfigure(ReconfigureEvent event) { | |
| setEditableGrid((Grid<M>) event.getSource()); | |
| } | |
| protected void onScroll(ScrollEvent event) { | |
| cancelEditing(); | |
| } | |
| protected abstract void showTooltip(SafeHtml content); | |
| protected void startEditing(Element target) { | |
| GridCell cell = findCell(target); | |
| if (cell != null) { | |
| int row = cell.getRow(); | |
| int col = cell.getCol(); | |
| if (row != -1 && col != -1) { | |
| startEditing(new GridCell(row, col)); | |
| } | |
| } | |
| } | |
| protected void startMonitoring() { | |
| if (!bound && monitorValid) { | |
| bound = true; | |
| if (monitorTimer == null) { | |
| monitorTimer = new Timer() { | |
| @Override | |
| public void run() { | |
| bindHandler(); | |
| } | |
| }; | |
| } | |
| monitorTimer.scheduleRepeating(monitorPoll); | |
| } | |
| } | |
| protected void stopMonitoring() { | |
| bound = false; | |
| if (monitorTimer != null) { | |
| monitorTimer.cancel(); | |
| } | |
| } | |
| protected void startEditing(NativeEvent evt) { | |
| if (Element.is(evt.getEventTarget())) { | |
| startEditing(Element.as(evt.getEventTarget())); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.sencha.gxt.fx.client; | |
| import java.util.List; | |
| import com.google.gwt.core.client.GWT; | |
| import com.google.gwt.dom.client.Document; | |
| import com.google.gwt.dom.client.Element; | |
| import com.google.gwt.dom.client.Style.Visibility; | |
| import com.google.gwt.event.dom.client.KeyCodes; | |
| import com.google.gwt.event.dom.client.MouseDownEvent; | |
| import com.google.gwt.event.dom.client.MouseDownHandler; | |
| import com.google.gwt.event.shared.HandlerRegistration; | |
| import com.google.gwt.event.shared.SimpleEventBus; | |
| import com.google.gwt.user.client.Event; | |
| import com.google.gwt.user.client.Event.NativePreviewEvent; | |
| import com.google.gwt.user.client.Window; | |
| import com.google.gwt.user.client.ui.Widget; | |
| import com.sencha.gxt.core.client.Style; | |
| import com.sencha.gxt.core.client.dom.XDOM; | |
| import com.sencha.gxt.core.client.dom.XElement; | |
| import com.sencha.gxt.core.client.gestures.DragGestureRecognizer; | |
| 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.resources.CommonStyles.Styles; | |
| import com.sencha.gxt.core.client.util.BaseEventPreview; | |
| import com.sencha.gxt.core.client.util.Point; | |
| import com.sencha.gxt.core.client.util.Rectangle; | |
| import com.sencha.gxt.core.shared.event.GroupingHandlerRegistration; | |
| import com.sencha.gxt.fx.client.DragCancelEvent.DragCancelHandler; | |
| import com.sencha.gxt.fx.client.DragCancelEvent.HasDragCancelHandlers; | |
| import com.sencha.gxt.fx.client.DragEndEvent.DragEndHandler; | |
| import com.sencha.gxt.fx.client.DragEndEvent.HasDragEndHandlers; | |
| import com.sencha.gxt.fx.client.DragHandler.HasDragHandlers; | |
| import com.sencha.gxt.fx.client.DragMoveEvent.DragMoveHandler; | |
| import com.sencha.gxt.fx.client.DragMoveEvent.HasDragMoveHandlers; | |
| import com.sencha.gxt.fx.client.DragStartEvent.DragStartHandler; | |
| import com.sencha.gxt.fx.client.DragStartEvent.HasDragStartHandlers; | |
| /** | |
| * <p>Adds drag behavior to any widget. Drag operations can be initiated from the | |
| * widget itself, or another widget, such as the header in a dialog.</p> | |
| * <p>It is possible to specify event targets that will be ignored. If the target | |
| * element has the {@link Styles#nodrag()} style (as returned by | |
| * {@link CommonStyles#get()}) it will not trigger a drag operation.</p> | |
| */ | |
| public class Draggable implements HasDragStartHandlers, HasDragEndHandlers, HasDragMoveHandlers, HasDragCancelHandlers, | |
| HasDragHandlers { | |
| public interface DraggableAppearance { | |
| void addUnselectableStyle(Element element); | |
| Element createProxy(); | |
| void removeUnselectableStyle(Element element); | |
| void setProxyStyle(String proxyClass); | |
| } | |
| protected int conX, conY, conWidth, conHeight; | |
| protected int dragStartX, dragStartY; | |
| protected int lastX, lastY; | |
| protected XElement proxyEl; | |
| protected Rectangle startBounds; | |
| protected DragGestureRecognizer dragGestureRecognizer; | |
| protected final DraggableAppearance appearance; | |
| protected int clientWidth, clientHeight; | |
| protected boolean constrainClient = true; | |
| protected boolean constrainHorizontal; | |
| protected boolean constrainVertical; | |
| protected Widget container; | |
| protected boolean dragging; | |
| protected Widget dragWidget; | |
| protected XElement dragWidgetElement; | |
| protected boolean enabled = true; | |
| protected Widget handleWidget; | |
| protected GroupingHandlerRegistration handlerRegistrations; | |
| protected boolean moveAfterProxyDrag = true; | |
| protected BaseEventPreview eventPreview; | |
| protected boolean sizeProxyToSource = true; | |
| protected int startDragDistance = 2; | |
| protected Element startElement; | |
| // config | |
| protected boolean updateZIndex = true; | |
| protected boolean useProxy = true; | |
| protected int xLeft = Style.DEFAULT, xRight = Style.DEFAULT; | |
| protected int xTop = Style.DEFAULT, xBottom = Style.DEFAULT; | |
| protected SimpleEventBus eventBus; | |
| /** | |
| * Creates a new draggable instance. | |
| * | |
| * @param dragWidget the widget to be dragged | |
| */ | |
| public Draggable(Widget dragWidget) { | |
| this(dragWidget, dragWidget, GWT.<DraggableAppearance> create(DraggableAppearance.class)); | |
| } | |
| /** | |
| * Creates a new draggable instance. | |
| * | |
| * @param dragWidget the widget to be dragged | |
| * @param appearance the appearance with which to render the component | |
| */ | |
| public Draggable(Widget dragWidget, DraggableAppearance appearance) { | |
| this(dragWidget, dragWidget, appearance); | |
| } | |
| /** | |
| * Create a new draggable instance. | |
| * | |
| * @param dragWidget the widget to be dragged | |
| * @param handleWidget the widget drags will be initiated from | |
| */ | |
| public Draggable(final Widget dragWidget, final Widget handleWidget) { | |
| this(dragWidget, handleWidget, GWT.<DraggableAppearance>create(DraggableAppearance.class)); | |
| } | |
| /** | |
| * Create a new draggable instance. | |
| * | |
| * @param dragWidget the widget to be dragged | |
| * @param handleWidget the widget drags will be initiated from | |
| * @param appearance the appearance with which to render the component | |
| */ | |
| public Draggable(final Widget dragWidget, final Widget handleWidget, DraggableAppearance appearance) { | |
| this.dragWidget = dragWidget; | |
| this.handleWidget = handleWidget; | |
| this.appearance = appearance; | |
| handleWidget.getElement().getStyle().setProperty("touchAction", "none"); | |
| handleWidget.getElement().getStyle().setProperty("msTouchAction", "none"); | |
| dragWidgetElement = dragWidget.getElement().cast(); | |
| // Determines where the drop target is for mouse | |
| // Mouse over provides drop over element target | |
| eventPreview = new BaseEventPreview() { | |
| @Override | |
| public boolean onPreview(NativePreviewEvent event) { | |
| Event e = event.getNativeEvent().cast(); | |
| e.preventDefault(); | |
| switch (event.getTypeInt()) { | |
| case Event.ONKEYDOWN: | |
| if (dragging && e.getKeyCode() == KeyCodes.KEY_ESCAPE) { | |
| cancelDrag(); | |
| } | |
| break; | |
| case Event.ONMOUSEMOVE: | |
| onMouseMove(e); | |
| break; | |
| case Event.ONMOUSEUP: | |
| stopDrag(e); | |
| break; | |
| } | |
| return true; | |
| } | |
| }; | |
| eventPreview.setAutoHide(false); | |
| MouseDownHandler mouseDownHandler = new MouseDownHandler() { | |
| @Override | |
| public void onMouseDown(MouseDownEvent event) { | |
| Draggable.this.onMouseDown(event); | |
| } | |
| }; | |
| // Register mouse and touch listeners | |
| handlerRegistrations = new GroupingHandlerRegistration(); | |
| handlerRegistrations.add(handleWidget.addDomHandler(mouseDownHandler, MouseDownEvent.getType())); | |
| dragGestureRecognizer = getDragGestureRecognizer(); | |
| } | |
| /** | |
| * Crates the drag gesture recognizer and passes along the touch events to Draggable. | |
| * | |
| * @return the grag gesture recognizer | |
| */ | |
| protected DragGestureRecognizer getDragGestureRecognizer() { | |
| if (dragGestureRecognizer == null) { | |
| dragGestureRecognizer = new DragGestureRecognizer() { | |
| @Override | |
| protected boolean onStart(TouchData startedTouch) { | |
| super.onStart(startedTouch); | |
| return onTouchStart(startedTouch); | |
| } | |
| @Override | |
| protected void onMove(List<TouchData> touches) { | |
| super.onMove(touches); | |
| onTouchMove(touches); | |
| } | |
| @Override | |
| protected void onCancel(List<TouchData> touches) { | |
| super.onCancel(touches); | |
| onTouchEnd(touches); | |
| } | |
| @Override | |
| protected void onEnd(List<TouchData> touches) { | |
| super.onEnd(touches); | |
| onTouchEnd(touches); | |
| } | |
| }; | |
| TouchEventToGestureAdapter touchEventsGestureAdapter = new TouchEventToGestureAdapter(handleWidget, dragGestureRecognizer); | |
| handlerRegistrations.add(touchEventsGestureAdapter.getHandlerRegistration()); | |
| } | |
| return dragGestureRecognizer; | |
| } | |
| protected void onTouchEnd(List<TouchData> touches) { | |
| stopDrag(touches.get(0).getLastNativeEvent().<Event>cast()); | |
| } | |
| protected void onTouchMove(List<TouchData> touches) { | |
| TouchData touch = touches.get(0); | |
| Point pos = touch.getLastPosition(); | |
| handleMove(pos.getX(), pos.getY(), touch.getLastNativeEvent().<Event>cast()); | |
| } | |
| protected boolean onTouchStart(TouchData startedTouch) { | |
| if (!enabled) { | |
| return false; | |
| } | |
| Element target = startedTouch.getStartElement().asElement(); | |
| if (hasClassName(target, CommonStyles.get().nodrag())) { | |
| return false; | |
| } | |
| if (!handleWidget.getElement().isOrHasChild(target)) { | |
| return false; | |
| } | |
| Point position = startedTouch.getStartPosition(); | |
| Element start = startedTouch.getStartElement() != null ? startedTouch.getStartElement().asElement() : null; | |
| handleStart(position.getX(), position.getY(), startedTouch.getLastNativeEvent().<Event>cast(), start); | |
| return true; | |
| } | |
| @Override | |
| public HandlerRegistration addDragCancelHandler(DragCancelHandler handler) { | |
| return ensureHandlers().addHandler(DragCancelEvent.getType(), handler); | |
| } | |
| @Override | |
| public HandlerRegistration addDragEndHandler(DragEndHandler handler) { | |
| return ensureHandlers().addHandler(DragEndEvent.getType(), handler); | |
| } | |
| @Override | |
| public HandlerRegistration addDragHandler(DragHandler handler) { | |
| GroupingHandlerRegistration reg = new GroupingHandlerRegistration(); | |
| reg.add(ensureHandlers().addHandler(DragStartEvent.getType(), handler)); | |
| reg.add(ensureHandlers().addHandler(DragEndEvent.getType(), handler)); | |
| reg.add(ensureHandlers().addHandler(DragMoveEvent.getType(), handler)); | |
| reg.add(ensureHandlers().addHandler(DragCancelEvent.getType(), handler)); | |
| return reg; | |
| } | |
| @Override | |
| public HandlerRegistration addDragMoveHandler(DragMoveHandler handler) { | |
| return ensureHandlers().addHandler(DragMoveEvent.getType(), handler); | |
| } | |
| @Override | |
| public HandlerRegistration addDragStartHandler(DragStartHandler handler) { | |
| return ensureHandlers().addHandler(DragStartEvent.getType(), handler); | |
| } | |
| /** | |
| * Cancels the drag if running. | |
| */ | |
| public void cancelDrag() { | |
| eventPreview.remove(); | |
| if (dragging) { | |
| dragging = false; | |
| if (isUseProxy()) { | |
| proxyEl.disableTextSelection(false); | |
| proxyEl.getStyle().setVisibility(Visibility.HIDDEN); | |
| proxyEl.removeFromParent(); | |
| } else { | |
| dragWidgetElement.setXY(startBounds.getX(), startBounds.getY()); | |
| } | |
| ensureHandlers().fireEventFromSource(new DragCancelEvent(dragWidget, startElement), this); | |
| afterDrag(); | |
| } | |
| startElement = null; | |
| } | |
| /** | |
| * Returns the drag container. | |
| * | |
| * @return the drag container | |
| */ | |
| public Widget getContainer() { | |
| return container; | |
| } | |
| /** | |
| * Specifies a container to which the drag widget is constrained. | |
| * | |
| * @param container the container | |
| */ | |
| public void setContainer(Widget container) { | |
| this.container = container; | |
| } | |
| /** | |
| * Returns the drag handleWidget. | |
| * | |
| * @return the drag handleWidget | |
| */ | |
| public Widget getDragHandle() { | |
| return handleWidget; | |
| } | |
| /** | |
| * Returns the widget being dragged. | |
| * | |
| * @return the drag widget | |
| */ | |
| public Widget getDragWidget() { | |
| return dragWidget; | |
| } | |
| /** | |
| * Returns the number of pixels the cursor must move before dragging begins. | |
| * | |
| * @return the distance in pixels | |
| */ | |
| public int getStartDragDistance() { | |
| return startDragDistance; | |
| } | |
| /** | |
| * Specifies how far the cursor must move after mousedown to start dragging | |
| * (defaults to 2). | |
| * | |
| * @param startDragDistance the start distance in pixels | |
| */ | |
| public void setStartDragDistance(int startDragDistance) { | |
| this.startDragDistance = startDragDistance; | |
| } | |
| /** | |
| * Returns true if drag is constrained to the viewport. | |
| * | |
| * @return the constrain client state | |
| */ | |
| public boolean isConstrainClient() { | |
| return constrainClient; | |
| } | |
| /** | |
| * True to set constrain movement to the viewport (defaults to true). | |
| * | |
| * @param constrainClient true to constrain to viewport | |
| */ | |
| public void setConstrainClient(boolean constrainClient) { | |
| this.constrainClient = constrainClient; | |
| } | |
| /** | |
| * Returns true if horizontal movement is constrained. | |
| * | |
| * @return the horizontal constrain state | |
| */ | |
| public boolean isConstrainHorizontal() { | |
| return constrainHorizontal; | |
| } | |
| /** | |
| * True to stop horizontal movement (defaults to false). | |
| * | |
| * @param constrainHorizontal true to stop horizontal movement | |
| */ | |
| public void setConstrainHorizontal(boolean constrainHorizontal) { | |
| this.constrainHorizontal = constrainHorizontal; | |
| } | |
| /** | |
| * Returns true if vertical movement is constrained. | |
| * | |
| * @return true if vertical movement is constrained | |
| */ | |
| public boolean isConstrainVertical() { | |
| return constrainVertical; | |
| } | |
| /** | |
| * True to stop vertical movement (defaults to false). | |
| * | |
| * @param constrainVertical true to stop vertical movement | |
| */ | |
| public void setConstrainVertical(boolean constrainVertical) { | |
| this.constrainVertical = constrainVertical; | |
| } | |
| /** | |
| * Returns true if a drag is in progress. | |
| * | |
| * @return the drag state | |
| */ | |
| public boolean isDragging() { | |
| return dragging; | |
| } | |
| /** | |
| * Returns true if enabled. | |
| * | |
| * @return the enable state | |
| */ | |
| public boolean isEnabled() { | |
| return enabled; | |
| } | |
| /** | |
| * Enables dragging if the argument is true, and disables it | |
| * otherwise. | |
| * | |
| * @param enabled the new enabled state | |
| */ | |
| public void setEnabled(boolean enabled) { | |
| this.enabled = enabled; | |
| } | |
| /** | |
| * Returns true if the drag widget is moved after a proxy drag. | |
| * | |
| * @return the move after proxy state | |
| */ | |
| public boolean isMoveAfterProxyDrag() { | |
| return moveAfterProxyDrag; | |
| } | |
| /** | |
| * True to move source widget after a proxy drag (defaults to true). | |
| * | |
| * @param moveAfterProxyDrag true to move after a proxy drag | |
| */ | |
| public void setMoveAfterProxyDrag(boolean moveAfterProxyDrag) { | |
| this.moveAfterProxyDrag = moveAfterProxyDrag; | |
| } | |
| /** | |
| * Returns true if the proxy element is sized to match the drag widget. | |
| * | |
| * @return the size proxy to source state | |
| */ | |
| public boolean isSizeProxyToSource() { | |
| return sizeProxyToSource; | |
| } | |
| /** | |
| * True to set proxy dimensions the same as the drag widget (defaults to | |
| * true). | |
| * | |
| * @param sizeProxyToSource true to update proxy size | |
| */ | |
| public void setSizeProxyToSource(boolean sizeProxyToSource) { | |
| this.sizeProxyToSource = sizeProxyToSource; | |
| } | |
| /** | |
| * Returns true if the z-index is updated after a drag. | |
| * | |
| * @return the update z-index state | |
| */ | |
| public boolean isUpdateZIndex() { | |
| return updateZIndex; | |
| } | |
| /** | |
| * True if the CSS z-index should be updated on the widget being dragged. | |
| * Setting this value to true will ensure that the dragged | |
| * element is always displayed over all other widgets (defaults to true). | |
| * | |
| * @param updateZIndex true update the z-index | |
| */ | |
| public void setUpdateZIndex(boolean updateZIndex) { | |
| this.updateZIndex = updateZIndex; | |
| } | |
| /** | |
| * Returns true if proxy element is enabled. | |
| * | |
| * @return the use proxy state | |
| */ | |
| public boolean isUseProxy() { | |
| return useProxy; | |
| } | |
| /** | |
| * True to use a proxy widget during drag operation (defaults to true). | |
| * | |
| * @param useProxy true use a proxy | |
| */ | |
| public void setUseProxy(boolean useProxy) { | |
| this.useProxy = useProxy; | |
| } | |
| /** | |
| * Removes the drag handles. | |
| */ | |
| public void release() { | |
| cancelDrag(); | |
| handlerRegistrations.removeHandler(); | |
| } | |
| /** | |
| * Sets the proxy element. | |
| * | |
| * @param element the proxy element | |
| */ | |
| public void setProxy(XElement element) { | |
| proxyEl = element; | |
| } | |
| public void setProxyStyle(String proxyClass) { | |
| appearance.setProxyStyle(proxyClass); | |
| } | |
| /** | |
| * Constrains the horizontal travel. | |
| * | |
| * @param left the number of pixels the element can move to the left | |
| * @param right the number of pixels the element can move to the right | |
| */ | |
| public void setXConstraint(int left, int right) { | |
| xLeft = left; | |
| xRight = right; | |
| } | |
| /** | |
| * Constrains the vertical travel. | |
| * | |
| * @param top the number of pixels the element can move to the up | |
| * @param bottom the number of pixels the element can move to the down | |
| */ | |
| public void setYConstraint(int top, int bottom) { | |
| xTop = top; | |
| xBottom = bottom; | |
| } | |
| protected void afterDrag() { | |
| appearance.removeUnselectableStyle(Document.get().getBody()); | |
| Shim.get().uncover(); | |
| } | |
| protected XElement createProxy() { | |
| return proxyEl = appearance.createProxy().cast(); | |
| } | |
| protected void handleStart(int x, int y, Event event, Element target) { | |
| startBounds = dragWidgetElement.getBounds(); | |
| startElement = target; | |
| dragStartX = x; | |
| dragStartY = y; | |
| eventPreview.add(); | |
| clientWidth = Window.getClientWidth() + XDOM.getBodyScrollLeft(); | |
| clientHeight = Window.getClientHeight() + XDOM.getBodyScrollTop(); | |
| if (container != null) { | |
| conX = container.getAbsoluteLeft(); | |
| conY = container.getAbsoluteTop(); | |
| conWidth = container.getOffsetWidth(); | |
| conHeight = container.getOffsetHeight(); | |
| } | |
| if (startDragDistance == 0) { | |
| startDrag(event); | |
| } | |
| } | |
| protected void onMouseDown(MouseDownEvent e) { | |
| if (!enabled || e.getNativeEvent().getButton() != Event.BUTTON_LEFT) { | |
| return; | |
| } | |
| Element target = e.getNativeEvent().getEventTarget().cast(); | |
| // Skip dragging using class name CommonStyles.get().nodrag(). | |
| if (hasClassName(target, CommonStyles.get().nodrag())) { | |
| // Skip Dragging on this target | |
| return; | |
| } | |
| // Allow the option of overriding text selection | |
| // This would happen in the event it's a grid inline editing with dnd | |
| if (!target.getPropertyBoolean("ignoreTextSelection")) { | |
| // still allow text selection, prevent drag of other elements | |
| if ((!"input".equalsIgnoreCase(target.getTagName()) && !"textarea".equalsIgnoreCase(target.getTagName())) | |
| || target.getPropertyBoolean("disabled")) { | |
| e.getNativeEvent().preventDefault(); | |
| } | |
| } | |
| handleStart(e.getClientX(), e.getClientY(), e.getNativeEvent().<Event> cast(), target); | |
| } | |
| protected void handleMove(int x, int y, Event event) { | |
| if (!dragging && (Math.abs(dragStartX - x) > startDragDistance || Math.abs(dragStartY - y) > startDragDistance)) { | |
| startDrag(event); | |
| } | |
| if (dragging) { | |
| int left = constrainHorizontal ? startBounds.getX() : startBounds.getX() + (x - dragStartX); | |
| int top = constrainVertical ? startBounds.getY() : startBounds.getY() + (y - dragStartY); | |
| if (constrainClient) { | |
| if (!constrainHorizontal) { | |
| int width = startBounds.getWidth(); | |
| left = Math.max(left, 0); | |
| left = Math.max(0, Math.min(clientWidth - width, left)); | |
| } | |
| if (!constrainVertical) { | |
| top = Math.max(top, 0); | |
| int height = startBounds.getHeight(); | |
| if (Math.min(clientHeight - height, top) > 0) { | |
| top = Math.max(2, Math.min(clientHeight - height, top)); | |
| } | |
| } | |
| } | |
| if (container != null) { | |
| int width = startBounds.getWidth(); | |
| int height = startBounds.getHeight(); | |
| if (!constrainHorizontal) { | |
| left = Math.max(left, conX); | |
| left = Math.min(conX + conWidth - width, left); | |
| } | |
| if (!constrainVertical) { | |
| top = Math.min(conY + conHeight - height, top); | |
| top = Math.max(top, conY); | |
| } | |
| } | |
| if (!constrainHorizontal) { | |
| if (xLeft != Style.DEFAULT) { | |
| left = Math.max(startBounds.getX() - xLeft, left); | |
| } | |
| if (xRight != Style.DEFAULT) { | |
| left = Math.min(startBounds.getX() + xRight, left); | |
| } | |
| } | |
| if (!constrainVertical) { | |
| if (xTop != Style.DEFAULT) { | |
| top = Math.max(startBounds.getY() - xTop, top); | |
| } | |
| if (xBottom != Style.DEFAULT) { | |
| top = Math.min(startBounds.getY() + xBottom, top); | |
| } | |
| } | |
| lastX = left; | |
| lastY = top; | |
| DragMoveEvent evt = new DragMoveEvent(dragWidget, startElement, lastX, lastY, event); | |
| ensureHandlers().fireEventFromSource(evt, this); | |
| if (evt.isCancelled()) { | |
| cancelDrag(); | |
| return; | |
| } | |
| int tl = evt.getX() != lastX ? evt.getX() : lastX; | |
| int tt = evt.getY() != lastY ? evt.getY() : lastY; | |
| if (useProxy) { | |
| proxyEl.setXY(tl, tt); | |
| } else { | |
| dragWidgetElement.setXY(tl, tt); | |
| } | |
| } | |
| } | |
| protected void onMouseMove(Event event) { | |
| Element elem = event.getEventTarget().cast(); | |
| // elem.getClassName throwing GWT exception when dragged widget is over SVG / VML | |
| if (hasClassName(elem, "x-insert")) { | |
| return; | |
| } | |
| handleMove(event.getClientX(), event.getClientY(), event); | |
| } | |
| protected void startDrag(Event event) { | |
| DragStartEvent de = new DragStartEvent(dragWidget, startElement, startBounds.getX(), startBounds.getY(), event); | |
| ensureHandlers().fireEventFromSource(de, this); | |
| if (de.isCancelled()) { | |
| cancelDrag(); | |
| return; | |
| } | |
| dragging = true; | |
| appearance.addUnselectableStyle(Document.get().getBody()); | |
| if (!useProxy) { | |
| dragWidget.getElement().<XElement> cast().makePositionable(); | |
| } | |
| if (event != null) { | |
| event.preventDefault(); | |
| } | |
| Shim.get().cover(true); | |
| lastX = startBounds.getX(); | |
| lastY = startBounds.getY(); | |
| if (useProxy) { | |
| if (proxyEl == null) { | |
| createProxy(); | |
| } | |
| if (container == null) { | |
| Document.get().getBody().appendChild(proxyEl); | |
| } else { | |
| container.getElement().appendChild(proxyEl); | |
| } | |
| proxyEl.setVisibility(true); | |
| proxyEl.setZIndex(XDOM.getTopZIndex()); | |
| proxyEl.makePositionable(true); | |
| if (sizeProxyToSource) { | |
| proxyEl.setBounds(startBounds); | |
| } else { | |
| proxyEl.setXY(startBounds.getX() + 50, startBounds.getY() + 50); | |
| } | |
| // did listeners change size? | |
| if (de.getHeight() > 0 && de.getWidth() > 0) { | |
| proxyEl.setSize(de.getWidth(), de.getHeight(), true); | |
| } else if (de.getHeight() > 0) { | |
| proxyEl.setHeight(de.getHeight(), true); | |
| } else if (de.getWidth() > 0) { | |
| proxyEl.setWidth(de.getWidth(), true); | |
| } | |
| } else if (updateZIndex) { | |
| dragWidget.getElement().<XElement> cast().setZIndex(XDOM.getTopZIndex()); | |
| } | |
| } | |
| protected void stopDrag(Event event) { | |
| eventPreview.remove(); | |
| if (dragging) { | |
| dragging = false; | |
| if (isUseProxy()) { | |
| if (isMoveAfterProxyDrag()) { | |
| Rectangle rect = proxyEl.getBounds(); | |
| dragWidget.getElement().<XElement> cast().setXY(rect.getX(), rect.getY()); | |
| } | |
| proxyEl.setVisibility(false); | |
| proxyEl.disableTextSelection(false); | |
| proxyEl.removeFromParent(); | |
| } | |
| DragEndEvent de = new DragEndEvent(dragWidget, startElement, lastX, lastY, event); | |
| ensureHandlers().fireEventFromSource(de, this); | |
| afterDrag(); | |
| } | |
| startElement = null; | |
| } | |
| SimpleEventBus ensureHandlers() { | |
| return eventBus == null ? eventBus = new SimpleEventBus() : eventBus; | |
| } | |
| protected native boolean hasClassName(Element elem, String className) /*-{ | |
| return !!elem.hasAttribute && elem.hasAttribute("class") && elem.getAttribute("class").indexOf(className) != -1; | |
| }-*/; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.sencha.gxt.widget.core.client.grid.editing; | |
| import java.util.logging.Logger; | |
| 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.NativeEvent; | |
| import com.google.gwt.dom.client.Style; | |
| import com.google.gwt.dom.client.Style.Unit; | |
| import com.google.gwt.event.dom.client.MouseDownEvent; | |
| import com.google.gwt.event.dom.client.MouseUpEvent; | |
| import com.google.gwt.event.dom.client.ScrollEvent; | |
| import com.google.gwt.event.logical.shared.ValueChangeEvent; | |
| import com.google.gwt.event.logical.shared.ValueChangeHandler; | |
| import com.google.gwt.safehtml.shared.SafeHtml; | |
| import com.google.gwt.safehtml.shared.SafeHtmlBuilder; | |
| import com.google.gwt.user.client.Timer; | |
| import com.google.gwt.user.client.ui.Focusable; | |
| import com.google.gwt.user.client.ui.IsWidget; | |
| import com.google.gwt.user.client.ui.Widget; | |
| import com.sencha.gxt.core.client.GXT; | |
| import com.sencha.gxt.core.client.GXTLogConfiguration; | |
| import com.sencha.gxt.core.client.Style.Side; | |
| import com.sencha.gxt.core.client.ValueProvider; | |
| import com.sencha.gxt.core.client.dom.XElement; | |
| import com.sencha.gxt.core.client.util.KeyNav; | |
| import com.sencha.gxt.core.shared.event.GroupingHandlerRegistration; | |
| import com.sencha.gxt.data.shared.Converter; | |
| import com.sencha.gxt.data.shared.ListStore; | |
| import com.sencha.gxt.widget.core.client.Component; | |
| import com.sencha.gxt.widget.core.client.ComponentHelper; | |
| import com.sencha.gxt.widget.core.client.event.BeforeStartEditEvent; | |
| import com.sencha.gxt.widget.core.client.event.BlurEvent; | |
| import com.sencha.gxt.widget.core.client.event.BlurEvent.BlurHandler; | |
| import com.sencha.gxt.widget.core.client.event.CancelEditEvent; | |
| import com.sencha.gxt.widget.core.client.event.CompleteEditEvent; | |
| import com.sencha.gxt.widget.core.client.event.HeaderMouseDownEvent; | |
| import com.sencha.gxt.widget.core.client.event.StartEditEvent; | |
| import com.sencha.gxt.widget.core.client.form.CheckBox; | |
| import com.sencha.gxt.widget.core.client.form.Field; | |
| import com.sencha.gxt.widget.core.client.form.IsField; | |
| import com.sencha.gxt.widget.core.client.form.TextArea; | |
| import com.sencha.gxt.widget.core.client.form.TriggerField; | |
| import com.sencha.gxt.widget.core.client.form.ValueBaseField; | |
| import com.sencha.gxt.widget.core.client.form.error.HasErrorHandler; | |
| import com.sencha.gxt.widget.core.client.grid.CellSelectionModel; | |
| import com.sencha.gxt.widget.core.client.grid.ColumnConfig; | |
| import com.sencha.gxt.widget.core.client.grid.Grid; | |
| import com.sencha.gxt.widget.core.client.grid.Grid.GridCell; | |
| import com.sencha.gxt.widget.core.client.grid.GridSelectionModel; | |
| import com.sencha.gxt.widget.core.client.selection.CellSelection; | |
| import com.sencha.gxt.widget.core.client.tips.ToolTip; | |
| import com.sencha.gxt.widget.core.client.tips.ToolTipConfig; | |
| /** | |
| * Cell based inline editing. | |
| * | |
| * @param <M> the model type | |
| */ | |
| public class GridInlineEditing<M> extends AbstractGridEditing<M> { | |
| protected class GridEditingKeyNav extends AbstractGridEditingKeyNav { | |
| @Override | |
| public void onTab(NativeEvent evt) { | |
| GridInlineEditing.this.onTab(evt); | |
| } | |
| } | |
| protected static Logger logger = Logger.getLogger(GridInlineEditing.class.getName()); | |
| protected GroupingHandlerRegistration fieldRegistration = new GroupingHandlerRegistration(); | |
| protected boolean ignoreScroll; | |
| protected boolean activeEdit; | |
| protected boolean rowUpdated; | |
| protected boolean ignoreNextEnter; | |
| protected boolean focusOnComplete; | |
| protected boolean revertInvalid = false; | |
| public GridInlineEditing(Grid<M> editableGrid) { | |
| setEditableGrid(editableGrid); | |
| } | |
| @Override | |
| public void cancelEditing() { | |
| ignoreScroll = false; | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("cancelEditing active is " + (activeCell == null ? "null" : "no null")); | |
| } | |
| if (activeCell != null) { | |
| Element elem = getEditableGrid().getView().getCell(activeCell.getRow(), activeCell.getCol()); | |
| elem.getFirstChildElement().getStyle().setVisibility(Style.Visibility.VISIBLE); | |
| cancelEditing(activeCell); | |
| final GridCell gc = activeCell; | |
| activeCell = null; | |
| fireEvent(new CancelEditEvent<M>(gc)); | |
| if (focusOnComplete) { | |
| focusOnComplete = false; | |
| focusGrid(); | |
| // Focus of grid not working after canceling an edit in IE. | |
| // something is stealing focus and the only fix so far is to run focus call in a timer. Deferred does not fix. | |
| // Need to find why focus is not staying on first call. | |
| if (GXT.isIE()) { | |
| Timer t = new Timer() { | |
| @Override | |
| public void run() { | |
| focusGrid(); | |
| } | |
| }; | |
| t.schedule(100); | |
| } | |
| } | |
| } | |
| stopMonitoring(); | |
| } | |
| protected void cancelEditing(GridCell activeCell) { | |
| ColumnConfig<M, ?> columnConfig = columnModel.getColumn(activeCell.getCol()); | |
| final IsField<?> field = getEditor(columnConfig); | |
| removeEditor(activeCell, field); | |
| if (GXT.isiOS()) { | |
| field.clear(); | |
| } else { | |
| Scheduler.get().scheduleDeferred(new ScheduledCommand() { | |
| @Override | |
| public void execute() { | |
| field.clear(); | |
| } | |
| }); | |
| } | |
| } | |
| @Override | |
| public void completeEditing() { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("completeEditing active is " + (activeCell == null ? "null" : "no null")); | |
| } | |
| if (activeCell != null) { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("completeEditing"); | |
| } | |
| Element elem = getEditableGrid().getView().getCell(activeCell.getRow(), activeCell.getCol()); | |
| elem.getFirstChildElement().getStyle().setVisibility(Style.Visibility.VISIBLE); | |
| doCompleteEditing(); | |
| } | |
| stopMonitoring(); | |
| } | |
| /** | |
| * Returns {@code true} of the editor reverts the value to the start value on invalid. | |
| * | |
| * @return the revert invalid state | |
| */ | |
| public boolean isRevertInvalid() { | |
| return revertInvalid; | |
| } | |
| /** | |
| * True to automatically revert the field value and cancel the edit when the user completes an edit and the field | |
| * validation fails (defaults to {@code false}). | |
| * | |
| * @param revertInvalid true to revert | |
| */ | |
| public void setRevertInvalid(boolean revertInvalid) { | |
| this.revertInvalid = revertInvalid; | |
| } | |
| @Override | |
| public void startEditing(final GridCell cell) { | |
| if (getEditableGrid() != null && getEditableGrid().isAttached() && cell != null) { | |
| ColumnConfig<M, ?> c = columnModel.getColumn(cell.getCol()); | |
| M value = getEditableGrid().getStore().get(cell.getRow()); | |
| // editable | |
| if (value != null && getEditor(c) != null) { | |
| BeforeStartEditEvent<M> ce = new BeforeStartEditEvent<M>(cell); | |
| fireEvent(ce); | |
| if (ce.isCancelled()) { | |
| return; | |
| } | |
| if (getEditableGrid().getSelectionModel() instanceof CellSelectionModel) { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("startEditing selectCell"); | |
| } | |
| ((CellSelectionModel<?>) getEditableGrid().getSelectionModel()).selectCell(cell.getRow(), cell.getCol()); | |
| } | |
| Element elem = getEditableGrid().getView().getCell(cell.getRow(), cell.getCol()); | |
| elem.getFirstChildElement().getStyle().setVisibility(Style.Visibility.HIDDEN); | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("startEditing call cancelEditing, ignoreScroll true, ensure visible"); | |
| } | |
| cancelEditing(); | |
| ignoreScroll = true; | |
| getEditableGrid().getView().ensureVisible(cell.getRow(), cell.getCol(), true); | |
| doStartEditing(cell); | |
| } | |
| } | |
| } | |
| @SuppressWarnings("unchecked") | |
| protected <N, O> void doCompleteEditing() { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("doCompleteEditing activeCell is " + (activeCell != null ? " is not null" : "is null")); | |
| } | |
| if (activeCell != null) { | |
| final ColumnConfig<M, N> c = columnModel.getColumn(activeCell.getCol()); | |
| IsField<O> field = getEditor(c); | |
| if (field != null) { | |
| Converter<N, O> converter = getConverter(c); | |
| if (!field.isValid(false) && revertInvalid) { | |
| cancelEditing(); | |
| return; | |
| } | |
| O fieldValue = null; | |
| if (field instanceof ValueBaseField) { | |
| fieldValue = ((ValueBaseField<O>) field).getCurrentValue(); | |
| } else { | |
| fieldValue = field.getValue(); | |
| } | |
| final N convertedValue; | |
| if (converter != null) { | |
| convertedValue = converter.convertFieldValue(fieldValue); | |
| } else { | |
| convertedValue = (N) fieldValue; | |
| } | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("Converted value: " + convertedValue); | |
| } | |
| removeEditor(activeCell, field); | |
| ListStore<M> store = getEditableGrid().getStore(); | |
| ListStore<M>.Record record = store.getRecord(store.get(activeCell.getRow())); | |
| rowUpdated = true; | |
| record.addChange(c.getValueProvider(), convertedValue); | |
| fireEvent(new CompleteEditEvent<M>(activeCell)); | |
| if (focusOnComplete) { | |
| focusOnComplete = false; | |
| focusGrid(); | |
| } | |
| } | |
| activeCell = null; | |
| } | |
| } | |
| protected void doFocus(IsWidget field) { | |
| try { | |
| Widget widget = field.asWidget(); | |
| if (widget instanceof Component) { | |
| ((Component) widget).focus(); | |
| } else if (widget instanceof Focusable) { | |
| ((Focusable) widget).setFocus(true); | |
| } else { | |
| widget.getElement().focus(); | |
| } | |
| } catch (Exception e) { | |
| // IE throws exception if element not focusable | |
| } | |
| } | |
| @SuppressWarnings("unchecked") | |
| protected <N, O> void doStartEditing(final GridCell cell) { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("doStartEditing"); | |
| } | |
| if (getEditableGrid() != null && getEditableGrid().isAttached() && cell != null) { | |
| M value = getEditableGrid().getStore().get(cell.getRow()); | |
| ColumnConfig<M, N> columnConfig = columnModel.getColumn(cell.getCol()); | |
| if (columnConfig != null && value != null) { | |
| Converter<N, O> converter = getConverter(columnConfig); | |
| ValueProvider<? super M, N> valueProvider = columnConfig.getValueProvider(); | |
| N colValue = getEditableGrid().getStore().hasRecord(value) | |
| ? getEditableGrid().getStore().getRecord(value).getValue(valueProvider) | |
| : valueProvider.getValue(value); | |
| O convertedValue; | |
| if (converter != null) { | |
| convertedValue = converter.convertModelValue(colValue); | |
| } else { | |
| convertedValue = (O) colValue; | |
| } | |
| IsField<O> field = getEditor(columnConfig); | |
| if (field != null) { | |
| handleStartEditing(cell, columnConfig, field, convertedValue); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * @since 4.0.3 | |
| */ | |
| protected <N, O> void handleStartEditing(final GridCell cell, ColumnConfig<M, N> columnConfig, final IsField<O> field, | |
| O convertedValue) { | |
| if (field instanceof HasErrorHandler) { | |
| ((HasErrorHandler) field).setErrorSupport(null); | |
| } | |
| // Used with onTab | |
| previousActiveCell = cell; | |
| activeCell = cell; | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("doStartEditing convertedValue: " + convertedValue); | |
| } | |
| field.setValue(convertedValue); | |
| if (field instanceof TriggerField<?>) { | |
| ((TriggerField<?>) field).setMonitorTab(false); | |
| } | |
| if (field instanceof CheckBox) { | |
| ((CheckBox) field).setBorders(true); | |
| } | |
| final Widget widget = field.asWidget(); | |
| getEditableGrid().getView().getEditorParent().appendChild(widget.getElement()); | |
| ComponentHelper.setParent(getEditableGrid(), widget); | |
| ComponentHelper.doAttach(widget); | |
| widget.getElement().<XElement>cast().makePositionable(true); | |
| Element cellEl = getEditableGrid().getView().getCell(cell.getRow(), cell.getCol()); | |
| Element rowEl = getEditableGrid().getView().getRow(cell.getRow()); | |
| final int widgetWidth = cellEl.getOffsetWidth(); | |
| final int widgetHeight = cellEl.getOffsetHeight(); | |
| int left = 0; | |
| for (int i = 0; i < cell.getCol(); i++) { | |
| if (!columnModel.isHidden(i)) { | |
| left += columnModel.getColumnWidth(i); | |
| } | |
| } | |
| int top = rowEl.getAbsoluteTop() - getEditableGrid().getView().getBody().getAbsoluteTop(); | |
| widget.getElement().<XElement>cast().setLeftTop(left, top); | |
| // If the widget has already been sized in a previous edit | |
| // This is a workaround for changing the size after it's been set, this will trigger on widget.onResize | |
| widget.setPixelSize(widgetWidth + 2, widgetHeight + 2); | |
| widget.setPixelSize(widgetWidth, widgetHeight); | |
| if (field instanceof CheckBox) { | |
| int borderWidth = widget.getElement().<XElement>cast().getBorders(Side.TOP, Side.BOTTOM); | |
| widget.getElement().<XElement>cast().setHeight(rowEl.getOffsetHeight() - borderWidth); | |
| widget.getElement().<XElement>cast().getStyle().setLineHeight(rowEl.getOffsetHeight() - borderWidth, Unit.PX); | |
| } | |
| field.asWidget().setVisible(true); | |
| startMonitoring(); | |
| if (GXT.isiOS()) { | |
| // iOS can not have a timer in the loop. So focus will draw the keyboard. | |
| deferHandleStartEditing(cell, field); | |
| } else { | |
| Scheduler.get().scheduleDeferred(new ScheduledCommand() { | |
| @Override | |
| public void execute() { | |
| deferHandleStartEditing(cell, field); | |
| } | |
| }); | |
| } | |
| } | |
| /** | |
| * @since 4.0.3 | |
| */ | |
| protected <N, O> void deferHandleStartEditing(final GridCell cell, final IsField<O> field) { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("doStartEditing scheduleDeferred doFocus "); | |
| } | |
| // browsers select all when tabbing into a input and put cursor at location when clicking into an input | |
| // with inline editing, the field is not visible at time of click so we select all. we ignore | |
| // field.isSelectOnFocus as this only applies when clicking into a field | |
| if (field instanceof ValueBaseField<?> && !GXT.isiOS()) { | |
| ValueBaseField<?> valueBaseField = (ValueBaseField<?>) field; | |
| valueBaseField.selectAll(); | |
| } | |
| // Calling doFocus before selectAll is causing blur to fire which ends the edit immediately | |
| // after it starts | |
| doFocus(field); | |
| ignoreScroll = false; | |
| fieldRegistration.removeHandler(); | |
| fieldRegistration.add(field.addValueChangeHandler(new ValueChangeHandler<O>() { | |
| @Override | |
| public void onValueChange(ValueChangeEvent<O> event) { | |
| onEditingValueChange(cell, field, event); | |
| } | |
| })); | |
| fieldRegistration.add(field.addBlurHandler(new BlurHandler() { | |
| @Override | |
| public void onBlur(final BlurEvent event) { | |
| onEditingBlur(cell, field, event); | |
| } | |
| })); | |
| fireEvent(new StartEditEvent<M>(cell)); | |
| afterStartEdit(field); | |
| } | |
| /** | |
| * @since 4.0.3 | |
| */ | |
| protected <N, O> void onEditingValueChange(GridCell cell, IsField<O> field, ValueChangeEvent<O> event) { | |
| // if enter key cause value change we want to ignore the next enter key otherwise | |
| // new edit will start by onEnter | |
| ignoreNextEnter = true; | |
| Timer timer = new Timer() { | |
| @Override | |
| public void run() { | |
| ignoreNextEnter = false; | |
| } | |
| }; | |
| completeEditing(); | |
| timer.schedule(100); | |
| } | |
| /** | |
| * @since 4.0.3 | |
| */ | |
| protected <N, O> void onEditingBlur(GridCell cell, IsField<O> field, BlurEvent event) { | |
| if (field instanceof CheckBox) { | |
| ignoreNextEnter = true; | |
| Timer timer = new Timer() { | |
| @Override | |
| public void run() { | |
| ignoreNextEnter = false; | |
| cancelEditing(); | |
| } | |
| }; | |
| timer.schedule(100); | |
| } else { | |
| ignoreNextEnter = true; | |
| Timer timer = new Timer() { | |
| @Override | |
| public void run() { | |
| ignoreNextEnter = false; | |
| } | |
| }; | |
| cancelEditing(); | |
| timer.schedule(100); | |
| } | |
| } | |
| protected void afterStartEdit(IsField<?> field) { | |
| } | |
| @Override | |
| protected KeyNav ensureInternalKeyNav() { | |
| if (keyNav == null) { | |
| keyNav = new GridEditingKeyNav(); | |
| } | |
| return keyNav; | |
| } | |
| @Override | |
| protected SafeHtml getErrorHtml() { | |
| SafeHtmlBuilder sb = new SafeHtmlBuilder(); | |
| sb.appendHtmlConstant("<ul>"); | |
| ColumnConfig<M, ?> c = columnModel.getColumn(activeCell.getCol()); | |
| IsField<?> f = getEditor(c); | |
| getErrorMessage(f, sb, c.getHeader()); | |
| sb.appendHtmlConstant("</ul>"); | |
| return sb.toSafeHtml(); | |
| } | |
| @Override | |
| protected <N, O> void handleHeaderMouseDown(HeaderMouseDownEvent event) { | |
| if (activeCell != null) { | |
| final ColumnConfig<M, N> c = columnModel.getColumn(activeCell.getCol()); | |
| IsField<O> field = getEditor(c); | |
| // Rather than calling completeEditing directly, have the field | |
| // finish editing which will cause completeEditing to be called in the correct sequence | |
| field.finishEditing(); | |
| } | |
| } | |
| @Override | |
| protected boolean isValid() { | |
| if (activeCell == null) { | |
| return true; | |
| } | |
| ColumnConfig<M, ?> c = columnModel.getColumn(activeCell.getCol()); | |
| IsWidget w = getEditor(c); | |
| if (w instanceof ValueBaseField<?>) { | |
| ValueBaseField<?> f = (ValueBaseField<?>) w; | |
| if (!f.isCurrentValid(true)) { | |
| return false; | |
| } | |
| } else if (w instanceof Field<?>) { | |
| Field<?> f = (Field<?>) w; | |
| if (!f.isValid(true)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| @Override | |
| protected void onEnter(NativeEvent evt) { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("onEnter"); | |
| } | |
| if (ignoreNextEnter) { | |
| ignoreNextEnter = false; | |
| focusGrid(); | |
| return; | |
| } | |
| // enter key with no value changed fired | |
| if (activeCell != null) { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("onEnter activeCell not null (enter key no value change), cancel edit"); | |
| } | |
| ColumnConfig<M, ?> c = columnModel.getColumn(activeCell.getCol()); | |
| IsField<?> f = getEditor(c); | |
| if (f instanceof TextArea) { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("onEnter editor type TextArea so ignoring"); | |
| } | |
| return; | |
| } | |
| focusOnComplete = true; | |
| cancelEditing(); | |
| return; | |
| } | |
| GridSelectionModel<M> sm = getEditableGrid().getSelectionModel(); | |
| if (sm instanceof CellSelectionModel) { | |
| CellSelection<M> cell = ((CellSelectionModel<M>) sm).getSelectCell(); | |
| if (cell != null) { | |
| evt.preventDefault(); | |
| startEditing(new GridCell(cell.getRow(), cell.getCell())); | |
| } | |
| } | |
| } | |
| @Override | |
| protected void onEsc(NativeEvent evt) { | |
| if (activeCell != null) { | |
| focusOnComplete = true; | |
| super.onEsc(evt); | |
| } | |
| } | |
| @Override | |
| protected void onMouseDown(MouseDownEvent event) { | |
| turnOffTextSelectionInDraggable(event); | |
| // do we have an active edit at time of mouse down | |
| activeEdit = activeCell != null; | |
| rowUpdated = false; | |
| } | |
| /** | |
| * Turn off text selection in draggable. | |
| * | |
| * @param event The mouse down event. | |
| * @since 4.0.4 | |
| */ | |
| protected void turnOffTextSelectionInDraggable(MouseDownEvent event) { | |
| XElement target = event.getNativeEvent().getEventTarget().cast(); | |
| // This will prevent text selection. | |
| // See Draggable mouse down handler. | |
| // At this time there is no way to support both dnd and text selection | |
| // And the reason for that, if not ignored, then a blur will not happen, and cause all kinds of side effects. | |
| if (target != null) { | |
| target.setPropertyBoolean("ignoreTextSelection", true); | |
| } | |
| } | |
| @Override | |
| protected void onMouseUp(MouseUpEvent event) { | |
| // there was an active edit on mouse down and that edit has ended | |
| // we do not get a "click" event if the previous edit caused the row to be updated | |
| if (getClicksToEdit() == ClicksToEdit.ONE && activeEdit && rowUpdated && activeCell == null) { | |
| Element target = event.getNativeEvent().getEventTarget().cast(); | |
| startEditing(target); | |
| } | |
| activeEdit = false; | |
| rowUpdated = false; | |
| } | |
| @Override | |
| protected void onScroll(ScrollEvent event) { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("onScroll ignoreScroll " + (ignoreScroll ? "true" : "false calling cancelEditing")); | |
| } | |
| if (!ignoreScroll) { | |
| cancelEditing(); | |
| } | |
| } | |
| protected void onTab(NativeEvent event) { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("onTab"); | |
| } | |
| // Start by finish editing on the previous cell to start with | |
| if (previousActiveCell != null) { | |
| ColumnConfig<M, ?> columnConfig = columnModel.getColumn(previousActiveCell.getCol()); | |
| IsField<?> field = getEditor(columnConfig); | |
| event.preventDefault(); | |
| field.finishEditing(); | |
| } | |
| // Get rid of listening to blur and change events from the previous editing | |
| fieldRegistration.removeHandler(); | |
| // Keep active cell since we manually fire blur (finishEditing) which will call cancel edit | |
| // clearing active cell | |
| final GridCell active = previousActiveCell; | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("onTab activeCell is " + (activeCell == null ? "null" : "not null")); | |
| } | |
| if (activeCell != null) { | |
| ColumnConfig<M, ?> columnConfig = columnModel.getColumn(activeCell.getCol()); | |
| IsField<?> field = getEditor(columnConfig); | |
| // We handle navigation programmatically | |
| event.preventDefault(); | |
| // Since we are preventingDefault on tab key, the field will not blur on its | |
| // own, which means the value change event will not fire so we manually blur | |
| // the field, so we call finishEditing | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("onTab calling field.finishEditing()"); | |
| } | |
| field.finishEditing(); | |
| } | |
| if (active != null) { | |
| GridCell newCell = null; | |
| if (event.getShiftKey()) { | |
| newCell = getEditableGrid().walkCells(active.getRow(), active.getCol() - 1, -1, callback); | |
| } else { | |
| newCell = getEditableGrid().walkCells(active.getRow(), active.getCol() + 1, 1, callback); | |
| } | |
| if (newCell != null) { | |
| final GridCell gridCell = newCell; | |
| Scheduler.get().scheduleFinally(new ScheduledCommand() { | |
| @Override | |
| public void execute() { | |
| if (GXTLogConfiguration.loggingIsEnabled()) { | |
| logger.finest("onTab scheduleFinally startEditing"); | |
| } | |
| startEditing(gridCell); | |
| } | |
| }); | |
| } else { | |
| // when tabbing and no next cell to start edit, current edit is not ending | |
| // the focusCell call is not causing field to blur and finish editing | |
| if (isEditing()) { | |
| completeEditing(); | |
| } | |
| getEditableGrid().getView().focusCell(active.getRow(), active.getCol(), true); | |
| } | |
| } | |
| } | |
| @Override | |
| protected void showTooltip(SafeHtml msg) { | |
| if (activeCell == null) { | |
| return; | |
| } | |
| ColumnConfig<M, ?> c = columnModel.getColumn(activeCell.getCol()); | |
| IsField<?> f = getEditor(c); | |
| if (tooltip == null) { | |
| ToolTipConfig config = new ToolTipConfig(); | |
| config.setAutoHide(false); | |
| config.setMouseOffsetX(0); | |
| config.setMouseOffsetY(0); | |
| config.setAnchor(Side.LEFT); | |
| tooltip = new ToolTip(f.asWidget(), config); | |
| tooltip.setMaxWidth(600); | |
| } | |
| tooltip.initTarget(f.asWidget()); | |
| ToolTipConfig config = tooltip.getToolTipConfig(); | |
| config.setBody(msg); | |
| tooltip.update(config); | |
| tooltip.enable(); | |
| if (!tooltip.isAttached()) { | |
| tooltip.show(); | |
| } | |
| } | |
| protected void removeEditor(final GridCell cell, final IsField<?> field) { | |
| assert field != null; | |
| removeFieldBlurHandler(); | |
| if (GXT.isIE() && field instanceof ValueBaseField<?>) { | |
| ValueBaseField<?> valueBaseField = (ValueBaseField<?>) field; | |
| valueBaseField.getCell().getInputElement(valueBaseField.getElement()).blur(); | |
| } | |
| Widget widget = field.asWidget(); | |
| if (field != null && widget.isAttached()) { | |
| field.asWidget().setVisible(false); | |
| ComponentHelper.setParent(null, widget); | |
| ComponentHelper.doDetach(widget); | |
| } | |
| } | |
| protected void removeFieldBlurHandler() { | |
| fieldRegistration.removeHandler(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To use this workaround for Grid inline editing and DND.