瀏覽代碼

merged table related bugfixes from 6.5 branch by hand

svn changeset:18399/svn branch:6.6
tags/6.7.0.beta1
Matti Tahvonen 13 年之前
父節點
當前提交
bd31087d6e

+ 101
- 1
src/com/vaadin/terminal/gwt/client/ui/FocusableScrollPanel.java 查看文件

@@ -1,20 +1,31 @@
package com.vaadin.terminal.gwt.client.ui;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.HasScrollHandlers;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.impl.FocusImpl;
import com.vaadin.terminal.gwt.client.BrowserInfo;

/**
* A scrollhandlers similar to {@link ScrollPanel}.
*
*/
public class FocusableScrollPanel extends SimpleFocusablePanel implements
HasScrollHandlers {
HasScrollHandlers, ScrollHandler {

public FocusableScrollPanel() {
// Prevent IE standard mode bug when a AbsolutePanel is contained.
@@ -24,6 +35,85 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
style.setPosition(Position.RELATIVE);
}

private DivElement focusElement;

public FocusableScrollPanel(boolean useFakeFocusElement) {
this();
if (useFakeFocusElement) {
focusElement = Document.get().createDivElement();
}
}

private boolean useFakeFocusElement() {
return focusElement != null;
}

@Override
public void setWidget(Widget w) {
super.setWidget(w);
if (useFakeFocusElement()) {
if (focusElement.getParentElement() == null) {
Style style = focusElement.getStyle();
if (BrowserInfo.get().isIE6()) {
style.setOverflow(Overflow.HIDDEN);
style.setHeight(0, Unit.PX);
style.setWidth(0, Unit.PX);
style.setPosition(Position.ABSOLUTE);

addScrollHandler(this);
} else {
style.setPosition(Position.FIXED);
style.setTop(0, Unit.PX);
style.setLeft(0, Unit.PX);
}
getElement().appendChild(focusElement);
/* Sink from focusElemet too as focusa and blur don't bubble */
DOM.sinkEvents(
(com.google.gwt.user.client.Element) focusElement
.cast(), Event.FOCUSEVENTS);
// revert to original, not focusable
getElement().setPropertyObject("tabIndex", null);

} else {
moveFocusElementAfterWidget();
}
}
}

/**
* Helper to keep focus element always in domChild[1]. Aids testing.
*/
private void moveFocusElementAfterWidget() {
getElement().insertAfter(focusElement, getWidget().getElement());
}

@Override
public void setFocus(boolean focus) {
if (useFakeFocusElement()) {
if (focus) {
FocusImpl.getFocusImplForPanel().focus(
(Element) focusElement.cast());
} else {
FocusImpl.getFocusImplForPanel().blur(
(Element) focusElement.cast());
}
} else {
super.setFocus(focus);
}
}

@Override
public void setTabIndex(int tabIndex) {
if (useFakeFocusElement()) {
getElement().setTabIndex(-1);
if (focusElement != null) {
focusElement.setTabIndex(tabIndex);
}
} else {
super.setTabIndex(tabIndex);
}
}

public HandlerRegistration addScrollHandler(ScrollHandler handler) {
return addDomHandler(handler, ScrollEvent.getType());
}
@@ -66,4 +156,14 @@ public class FocusableScrollPanel extends SimpleFocusablePanel implements
getElement().setScrollTop(position);
}

public void onScroll(ScrollEvent event) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
public void execute() {
focusElement.getStyle().setTop(getScrollPosition(), Unit.PX);
focusElement.getStyle().setLeft(getHorizontalScrollPosition(),
Unit.PX);
}
});
}

}

+ 5
- 1
src/com/vaadin/terminal/gwt/client/ui/SimpleFocusablePanel.java 查看文件

@@ -30,7 +30,7 @@ public class SimpleFocusablePanel extends SimplePanel implements
public SimpleFocusablePanel() {
// make focusable, as we don't need access key magic we don't need to
// use FocusImpl.createFocusable
getElement().setTabIndex(0);
setTabIndex(0);
}

public HandlerRegistration addFocusHandler(FocusHandler handler) {
@@ -64,4 +64,8 @@ public class SimpleFocusablePanel extends SimplePanel implements
public void focus() {
setFocus(true);
}

public void setTabIndex(int tabIndex) {
getElement().setTabIndex(tabIndex);
}
}

+ 202
- 153
src/com/vaadin/terminal/gwt/client/ui/VScrollTable.java 查看文件

@@ -15,6 +15,7 @@ import java.util.Set;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.NodeList;
@@ -97,8 +98,7 @@ import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
* TODO implement unregistering for child components in Cells
*/
public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
VHasDropHandler, KeyPressHandler, KeyDownHandler, FocusHandler,
BlurHandler, Focusable, KeyUpHandler {
VHasDropHandler, FocusHandler, BlurHandler, Focusable {

public static final String CLASSNAME = "v-table";
public static final String CLASSNAME_SELECTION_FOCUS = CLASSNAME + "-focus";
@@ -278,8 +278,96 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,

private final TableFooter tFoot = new TableFooter();

private final FocusableScrollPanel scrollBodyPanel = new FocusableScrollPanel();
private final FocusableScrollPanel scrollBodyPanel = new FocusableScrollPanel(
true);

private KeyPressHandler navKeyPressHandler = new KeyPressHandler() {
public void onKeyPress(KeyPressEvent keyPressEvent) {
// This is used for Firefox only, since Firefox auto-repeat
// works correctly only if we use a key press handler, other
// browsers handle it correctly when using a key down handler
if (!BrowserInfo.get().isGecko()) {
return;
}

NativeEvent event = keyPressEvent.getNativeEvent();
if (!enabled) {
// Cancel default keyboard events on a disabled Table
// (prevents scrolling)
event.preventDefault();
} else if (hasFocus) {
// Key code in Firefox/onKeyPress is present only for
// special keys, otherwise 0 is returned
int keyCode = event.getKeyCode();
if (keyCode == 0 && event.getCharCode() == ' ') {
// Provide a keyCode for space to be compatible with
// FireFox keypress event
keyCode = CHARCODE_SPACE;
}

if (handleNavigation(keyCode,
event.getCtrlKey() || event.getMetaKey(),
event.getShiftKey())) {
event.preventDefault();
}

startScrollingVelocityTimer();
}
}

};

private KeyUpHandler navKeyUpHandler = new KeyUpHandler() {

public void onKeyUp(KeyUpEvent keyUpEvent) {
NativeEvent event = keyUpEvent.getNativeEvent();
int keyCode = event.getKeyCode();

if (!isFocusable()) {
cancelScrollingVelocityTimer();
} else if (isNavigationKey(keyCode)) {
if (keyCode == getNavigationDownKey()
|| keyCode == getNavigationUpKey()) {
/*
* in multiselect mode the server may still have value from
* previous page. Clear it unless doing multiselection or
* just moving focus.
*/
if (!event.getShiftKey() && !event.getCtrlKey()) {
instructServerToForgetPreviousSelections();
}
sendSelectedRows();
}
cancelScrollingVelocityTimer();
navKeyDown = false;
}
}
};

private KeyDownHandler navKeyDownHandler = new KeyDownHandler() {

public void onKeyDown(KeyDownEvent keyDownEvent) {
NativeEvent event = keyDownEvent.getNativeEvent();
// This is not used for Firefox
if (BrowserInfo.get().isGecko()) {
return;
}

if (!enabled) {
// Cancel default keyboard events on a disabled Table
// (prevents scrolling)
event.preventDefault();
} else if (hasFocus) {
if (handleNavigation(event.getKeyCode(), event.getCtrlKey()
|| event.getMetaKey(), event.getShiftKey())) {
navKeyDown = true;
event.preventDefault();
}

startScrollingVelocityTimer();
}
}
};
private int totalRows;

private Set<String> collapsedColumns;
@@ -326,6 +414,11 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,

public VScrollTable() {
scrollBodyPanel.setStyleName(CLASSNAME + "-body-wrapper");
scrollBodyPanel.addFocusHandler(this);
scrollBodyPanel.addBlurHandler(this);

scrollBodyPanel.addScrollHandler(this);
scrollBodyPanel.setStyleName(CLASSNAME + "-body");

/*
* Firefox auto-repeat works correctly only if we use a key press
@@ -333,16 +426,11 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* handler
*/
if (BrowserInfo.get().isGecko()) {
scrollBodyPanel.addKeyPressHandler(this);
scrollBodyPanel.addKeyPressHandler(navKeyPressHandler);
} else {
scrollBodyPanel.addKeyDownHandler(this);
scrollBodyPanel.addKeyDownHandler(navKeyDownHandler);
}
scrollBodyPanel.addKeyUpHandler(this);

scrollBodyPanel.addFocusHandler(this);
scrollBodyPanel.addBlurHandler(this);

scrollBodyPanel.addScrollHandler(this);
scrollBodyPanel.addKeyUpHandler(navKeyUpHandler);

scrollBodyPanel.sinkEvents(Event.TOUCHEVENTS);
scrollBodyPanel.addDomHandler(new TouchStartHandler() {
@@ -351,8 +439,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}, TouchStartEvent.getType());

scrollBodyPanel.setStyleName(CLASSNAME + "-body");

setStyleName(CLASSNAME);

add(tHead);
@@ -903,17 +989,8 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,

if (focusedRow != null) {
if (!focusedRow.isAttached()) {
// focused row has orphaned, can't focus
focusedRow = null;
if (SELECT_MODE_SINGLE == selectMode
&& selectedRowKeys.size() > 0) {
// try to focusa row currently selected and in viewport
String selectedRowKey = selectedRowKeys.iterator().next();
if (selectedRowKey != null) {
setRowFocus(getRenderedRowByKey(selectedRowKey));
}
}
// TODO what should happen in multiselect mode?
// focused row has been orphaned, can't focus
focusRowFromBody();
}
}

@@ -937,6 +1014,24 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,

}

private void focusRowFromBody() {
if (selectedRowKeys.size() == 1) {
// try to focus a row currently selected and in viewport
String selectedRowKey = selectedRowKeys.iterator().next();
if (selectedRowKey != null) {
VScrollTableRow renderedRow = getRenderedRowByKey(selectedRowKey);
if (renderedRow == null || !renderedRow.isInViewPort()) {
setRowFocus(scrollBody.getRowByRowIndex(firstRowInViewPort));
} else {
setRowFocus(renderedRow);
}
}
} else {
// multiselect mode
setRowFocus(scrollBody.getRowByRowIndex(firstRowInViewPort));
}
}

protected VScrollTableBody createScrollBody() {
return new VScrollTableBody();
}
@@ -2595,11 +2690,13 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,

String colKey;
private boolean collapsed;
private VScrollTableRow currentlyFocusedRow;

public VisibleColumnAction(String colKey) {
super(VScrollTable.TableHead.this);
this.colKey = colKey;
caption = tHead.getHeaderCell(colKey).getCaption();
currentlyFocusedRow = focusedRow;
}

@Override
@@ -2620,6 +2717,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
.size()]), false);
// let rowRequestHandler determine proper rows
rowRequestHandler.refreshContent();
lazyRevertFocusToRow(currentlyFocusedRow);
}

public void setCollapsed(boolean b) {
@@ -3772,7 +3870,26 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
setElement(rowElement);
DOM.sinkEvents(getElement(), Event.MOUSEEVENTS
| Event.TOUCHEVENTS | Event.ONDBLCLICK
| Event.ONCONTEXTMENU | Event.ONKEYDOWN);
| Event.ONCONTEXTMENU);
}

/**
* Detects whether row is visible in tables viewport.
*
* @return
*/
public boolean isInViewPort() {
int absoluteTop = getAbsoluteTop();
int scrollPosition = scrollBodyPanel.getScrollPosition();
if (absoluteTop < scrollPosition) {
return false;
}
int maxVisible = scrollPosition
+ scrollBodyPanel.getOffsetHeight() - getOffsetHeight();
if (absoluteTop > maxVisible) {
return false;
}
return true;
}

/**
@@ -4077,7 +4194,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
if (targetCellOrRowFound) {
mDown = false;
handleClickEvent(event, targetTdOrTr);
scrollBodyPanel.setFocus(true);
if (event.getButton() == Event.BUTTON_LEFT
&& isSelectable()) {

@@ -4256,6 +4372,7 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
case Event.ONMOUSEDOWN:
if (targetCellOrRowFound) {
setRowFocus(this);
ensureFocus();
if (dragmode != 0
&& (event.getButton() == NativeEvent.BUTTON_LEFT)) {
startRowDrag(event, type, targetTdOrTr);
@@ -4265,10 +4382,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
&& selectMode == SELECT_MODE_MULTI
&& multiselectmode == MULTISELECT_MODE_DEFAULT) {

// because we are preventing the default (due to
// prevent text selection) we must ensure
// gaining the focus.
ensureFocus();
// Prevent default text selection in Firefox
event.preventDefault();

@@ -4351,10 +4464,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
} else {
ev.createDragImage(getElement(), true);
}
// because we are preventing the default (due to
// prevent text selection) we must ensure
// gaining the focus.
ensureFocus();
if (type == Event.ONMOUSEDOWN) {
event.preventDefault();
}
@@ -4535,7 +4644,13 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
for (int i = 0; i < actions.length; i++) {
final String actionKey = actionKeys[i];
final TreeAction a = new TreeAction(this,
String.valueOf(rowKey), actionKey);
String.valueOf(rowKey), actionKey) {
@Override
public void execute() {
super.execute();
lazyRevertFocusToRow(VScrollTableRow.this);
}
};
a.setCaption(getActionCaption(actionKey));
a.setIconUrl(getActionIcon(actionKey));
actions[i] = a;
@@ -4629,7 +4744,9 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* focus only if not currently focused.
*/
protected void ensureFocus() {
focus();
if (!hasFocus) {
scrollBodyPanel.setFocus(true);
}
}
}

@@ -4648,14 +4765,14 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
selectedRowRanges.clear();
// also notify server that it clears all previous selections (the client
// side does not know about the invisible ones)
instructServerToForgotPreviousSelections();
instructServerToForgetPreviousSelections();
}

/**
* Used in multiselect mode when the client side knows that all selections
* are in the next request.
*/
private void instructServerToForgotPreviousSelections() {
private void instructServerToForgetPreviousSelections() {
client.updateVariable(paintableId, "clearSelections", true, false);
}

@@ -4831,6 +4948,16 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
Util.runWebkitOverflowAutoFix(scrollBodyPanel.getElement());
}
});

if (BrowserInfo.get().isIE()) {
/*
* IE does not fire onscroll event if scroll position is
* reverted to 0 due to the content element size growth. Ensure
* headers are in sync with content manually. Safe to use null
* event as we don't actually use the event object in listener.
*/
onScroll(null);
}
}
};

@@ -5261,7 +5388,9 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
// Apply focus style to new selection
row.addStyleName(CLASSNAME_SELECTION_FOCUS);

// Trying to set focus on already focused row
/*
* Trying to set focus on already focused row
*/
if (row == focusedRow) {
return false;
}
@@ -5582,86 +5711,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
scrollBodyPanel.setScrollPosition(newPixels);
}

/*
* (non-Javadoc)
*
* @see
* com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google
* .gwt.event.dom.client.KeyPressEvent)
*/
public void onKeyPress(KeyPressEvent event) {
// This is used for Firefox only
if (!BrowserInfo.get().isGecko()) {
return;
}

if (!enabled) {
// Cancel default keyboard events on a disabled Table (prevents
// scrolling)
event.preventDefault();
} else if (hasFocus) {
// Key code in Firefox/onKeyPress is present only for special keys,
// otherwise 0 is returned
NativeEvent nativeEvent = event.getNativeEvent();
int keyCode = nativeEvent.getKeyCode();
if (keyCode == 0 && nativeEvent.getCharCode() == ' ') {
// Provide a keyCode for space to be compatible with FireFox
// keypress event
keyCode = CHARCODE_SPACE;
}

if (handleNavigation(keyCode,
event.isControlKeyDown() || event.isMetaKeyDown(),
event.isShiftKeyDown())) {
event.preventDefault();
}

// Start the velocityTimer
if (scrollingVelocityTimer == null) {
scrollingVelocityTimer = new Timer() {
@Override
public void run() {
scrollingVelocity++;
}
};
scrollingVelocityTimer.scheduleRepeating(100);
}
}
}

/*
* (non-Javadoc)
*
* @see
* com.google.gwt.event.dom.client.KeyDownHandler#onKeyDown(com.google.gwt
* .event.dom.client.KeyDownEvent)
*/
public void onKeyDown(KeyDownEvent event) {
if (!enabled) {
// Cancel default keyboard events on a disabled Table (prevents
// scrolling)
event.preventDefault();
} else if (hasFocus) {
if (handleNavigation(event.getNativeEvent().getKeyCode(),
event.isControlKeyDown() || event.isMetaKeyDown(),
event.isShiftKeyDown())) {
navKeyDown = true;
event.preventDefault();
}

// Start the velocityTimer
if (scrollingVelocityTimer == null) {
scrollingVelocityTimer = new Timer() {
@Override
public void run() {
scrollingVelocity++;
}
};
scrollingVelocityTimer.scheduleRepeating(100);
}
}
}

/*
* (non-Javadoc)
*
@@ -5671,12 +5720,11 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
*/
public void onFocus(FocusEvent event) {
if (isFocusable()) {
scrollBodyPanel.addStyleName("focused");
hasFocus = true;

// Focus a row if no row is in focus
if (focusedRow == null) {
setRowFocus((VScrollTableRow) scrollBody.iterator().next());
focusRowFromBody();
} else {
setRowFocus(focusedRow);
}
@@ -5691,7 +5739,6 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
* .dom.client.BlurEvent)
*/
public void onBlur(BlurEvent event) {
scrollBodyPanel.removeStyleName("focused");
hasFocus = false;
navKeyDown = false;

@@ -5777,9 +5824,9 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}

if (tabIndex == 0 && !isFocusable()) {
scrollBodyPanel.getElement().setTabIndex(-1);
scrollBodyPanel.setTabIndex(-1);
} else {
scrollBodyPanel.getElement().setTabIndex(tabIndex);
scrollBodyPanel.setTabIndex(tabIndex);
}

if (BrowserInfo.get().getOperaVersion() >= 11) {
@@ -5789,36 +5836,24 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
}
}

public void onKeyUp(KeyUpEvent event) {
int keyCode = event.getNativeKeyCode();

if (!isFocusable()) {
if (scrollingVelocityTimer != null) {
// Remove velocityTimer if it exists and the Table is disabled
scrollingVelocityTimer.cancel();
scrollingVelocityTimer = null;
scrollingVelocity = 10;
}
} else if (isNavigationKey(keyCode)) {
if (keyCode == getNavigationDownKey()
|| keyCode == getNavigationUpKey()) {
/*
* in multiselect mode the server may still have value from
* previous page. Clear it unless doing multiselection or just
* moving focus.
*/
if (!event.getNativeEvent().getShiftKey()
&& !event.getNativeEvent().getCtrlKey()) {
instructServerToForgotPreviousSelections();
public void startScrollingVelocityTimer() {
if (scrollingVelocityTimer == null) {
scrollingVelocityTimer = new Timer() {
@Override
public void run() {
scrollingVelocity++;
}
sendSelectedRows();
}
if (scrollingVelocityTimer != null) {
scrollingVelocityTimer.cancel();
scrollingVelocityTimer = null;
scrollingVelocity = 10;
}
navKeyDown = false;
};
scrollingVelocityTimer.scheduleRepeating(100);
}
}

public void cancelScrollingVelocityTimer() {
if (scrollingVelocityTimer != null) {
// Remove velocityTimer if it exists and the Table is disabled
scrollingVelocityTimer.cancel();
scrollingVelocityTimer = null;
scrollingVelocity = 10;
}
}

@@ -5837,4 +5872,18 @@ public class VScrollTable extends FlowPanel implements Table, ScrollHandler,
|| keyCode == getNavigationEndKey()
|| keyCode == getNavigationStartKey();
}

public void lazyRevertFocusToRow(final VScrollTableRow currentlyFocusedRow) {
Scheduler.get().scheduleFinally(new ScheduledCommand() {
public void execute() {
if (currentlyFocusedRow != null) {
setRowFocus(currentlyFocusedRow);
} else {
VConsole.log("no row?");
focusRowFromBody();
}
scrollBody.ensureFocus();
}
});
}
}

+ 2
- 4
tests/src/com/vaadin/tests/components/table/TableScrollsOnSelection.java 查看文件

@@ -4,14 +4,12 @@ import com.vaadin.data.Item;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.tests.components.TestBase;
import com.vaadin.ui.Table;
import com.vaadin.ui.Window;
public class TableScrollsOnSelection extends TestBase {
@Override
protected void setup() {
Window mainWindow = new Window("Playground Application");
setMainWindow(mainWindow);
getMainWindow().getContent().setSizeUndefined();
IndexedContainer cont = new IndexedContainer();
cont.addContainerProperty("number", String.class, null);
@@ -23,7 +21,7 @@ public class TableScrollsOnSelection extends TestBase {
table.setPageLength(0);
table.setContainerDataSource(cont);
table.setSelectable(true);
mainWindow.addComponent(table);
addComponent(table);
}
@Override

Loading…
取消
儲存