From 1476a0c26b95555b25085fb0280d90aac329f07e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Johannes=20Dahlstr=C3=B6m?= Date: Thu, 16 Feb 2012 14:47:47 +0000 Subject: [PATCH] #5100 test case svn changeset:23048/svn branch:6.8 --- build/build.xml | 2 +- .../terminal/gwt/client/ui/VTabsheet.java | 126 ++++++++++++++++-- src/com/vaadin/ui/TabSheet.java | 37 ++++- .../tabsheet/TabKeyboardNavigation.java | 54 ++++++++ 4 files changed, 209 insertions(+), 10 deletions(-) create mode 100644 tests/testbench/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.java diff --git a/build/build.xml b/build/build.xml index 1fdcc0ee25..1f099c6352 100644 --- a/build/build.xml +++ b/build/build.xml @@ -542,7 +542,7 @@ - + Compiling widgetset ${widgetset}. Output directory: ${widgetsets-output-dir} diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java b/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java index 38140988ad..2a12238c4b 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VTabsheet.java @@ -12,8 +12,19 @@ import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.TableCellElement; import com.google.gwt.dom.client.TableElement; +import com.google.gwt.event.dom.client.BlurEvent; +import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.dom.client.FocusEvent; +import com.google.gwt.event.dom.client.FocusHandler; +import com.google.gwt.event.dom.client.HasBlurHandlers; +import com.google.gwt.event.dom.client.HasFocusHandlers; +import com.google.gwt.event.dom.client.HasKeyDownHandlers; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.KeyDownEvent; +import com.google.gwt.event.dom.client.KeyDownHandler; +import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; @@ -21,8 +32,10 @@ import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.ComplexPanel; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.user.client.ui.impl.FocusImpl; import com.vaadin.terminal.gwt.client.ApplicationConnection; import com.vaadin.terminal.gwt.client.BrowserInfo; +import com.vaadin.terminal.gwt.client.Focusable; import com.vaadin.terminal.gwt.client.Paintable; import com.vaadin.terminal.gwt.client.RenderInformation; import com.vaadin.terminal.gwt.client.RenderSpace; @@ -30,8 +43,10 @@ import com.vaadin.terminal.gwt.client.TooltipInfo; import com.vaadin.terminal.gwt.client.UIDL; import com.vaadin.terminal.gwt.client.Util; import com.vaadin.terminal.gwt.client.VCaption; +import com.vaadin.terminal.gwt.client.VConsole; -public class VTabsheet extends VTabsheetBase { +public class VTabsheet extends VTabsheetBase implements Focusable, + FocusHandler, BlurHandler, KeyDownHandler { private static class VCloseEvent { private Tab tab; @@ -54,7 +69,8 @@ public class VTabsheet extends VTabsheetBase { * Representation of a single "tab" shown in the TabBar * */ - private static class Tab extends SimplePanel { + private static class Tab extends SimplePanel implements HasFocusHandlers, + HasBlurHandlers, HasKeyDownHandlers { private static final String TD_CLASSNAME = CLASSNAME + "-tabitemcell"; private static final String TD_FIRST_CLASSNAME = TD_CLASSNAME + "-first"; @@ -94,6 +110,9 @@ public class VTabsheet extends VTabsheetBase { .getApplicationConnection()); add(tabCaption); + addFocusHandler(getTabsheet()); + addBlurHandler(getTabsheet()); + addKeyDownHandler(getTabsheet()); } public boolean isHiddenOnServer() { @@ -136,6 +155,9 @@ public class VTabsheet extends VTabsheetBase { * true if the Tab is the first visible Tab */ public void setStyleNames(boolean selected, boolean first) { + // TODO #5100 doesn't belong here + td.setAttribute("tabindex", selected ? "0" : "-1"); + setStyleName(td, TD_FIRST_CLASSNAME, first); setStyleName(td, TD_SELECTED_CLASSNAME, selected); setStyleName(td, TD_SELECTED_FIRST_CLASSNAME, selected && first); @@ -143,7 +165,10 @@ public class VTabsheet extends VTabsheetBase { } public void onClose() { + VConsole.log("OnClose"); + closeHandler.onClose(new VCloseEvent(this)); + VConsole.log("End OnClose"); } public VTabsheet getTabsheet() { @@ -179,6 +204,25 @@ public class VTabsheet extends VTabsheetBase { tabCaption.setWidth(tabCaption.getRequiredWidth() + "px"); } + public HandlerRegistration addFocusHandler(FocusHandler handler) { + return addDomHandler(handler, FocusEvent.getType()); + } + + public HandlerRegistration addBlurHandler(BlurHandler handler) { + return addDomHandler(handler, BlurEvent.getType()); + } + + public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) { + return addDomHandler(handler, KeyDownEvent.getType()); + } + + public void focus() { + td.focus(); + } + + public void blur() { + td.blur(); + } } private static class TabCaption extends VCaption { @@ -296,7 +340,6 @@ public class VTabsheet extends VTabsheetBase { } return width; } - } static class TabBar extends ComplexPanel implements ClickHandler, @@ -362,6 +405,7 @@ public class VTabsheet extends VTabsheetBase { Widget caption = (Widget) event.getSource(); int index = getWidgetIndex(caption.getParent()); getTabsheet().onTabSelected(index); + getTabsheet().focus(); } public VTabsheet getTabsheet() { @@ -483,7 +527,6 @@ public class VTabsheet extends VTabsheetBase { currentFirst.recalculateCaptionWidth(); return nextVisible; } - } public static final String CLASSNAME = "v-tabsheet"; @@ -494,11 +537,15 @@ public class VTabsheet extends VTabsheetBase { // Can't use "style" as it's already in use public static final String TAB_STYLE_NAME = "tabstyle"; + private static final FocusImpl focusImpl = FocusImpl.getFocusImplForPanel(); + private final Element tabs; // tabbar and 'scroller' container private final Element scroller; // tab-scroller element private final Element scrollerNext; // tab-scroller next button element private final Element scrollerPrev; // tab-scroller prev button element + private Tab focusedTab; + /** * The index of the first visible tab (when scrolled) */ @@ -536,6 +583,12 @@ public class VTabsheet extends VTabsheetBase { } if (client != null && activeTabIndex != tabIndex) { tb.selectTab(tabIndex); + + if (focusedTab != null) { + focusedTab.blur(); + tb.getTab(tabIndex).focus(); + } + addStyleDependentName("loading"); // run updating variables in deferred command to bypass some FF // optimization issues @@ -581,6 +634,9 @@ public class VTabsheet extends VTabsheetBase { public VTabsheet() { super(CLASSNAME); + addHandler(this, FocusEvent.getType()); + addHandler(this, BlurEvent.getType()); + // Tab scrolling DOM.setStyleAttribute(getElement(), "overflow", "hidden"); tabs = DOM.createDiv(); @@ -694,8 +750,13 @@ public class VTabsheet extends VTabsheetBase { updateOpenTabSize(); } - iLayout(); + // If a tab was focused before, focus the new active tab + if (focusedTab != null && tb.getTabCount() > 0) { + focusedTab = tb.getTab(activeTabIndex); + focusedTab.focus(); + } + iLayout(); // Re run relative size update to ensure optimal scrollbars // TODO isolate to situation that visible tab has undefined height try { @@ -923,9 +984,9 @@ public class VTabsheet extends VTabsheetBase { updateOpenTabSize(); VTabsheet.this.removeStyleDependentName("loading"); if (previousVisibleWidget != null) { - DOM.setStyleAttribute( - DOM.getParent(previousVisibleWidget.getElement()), - "visibility", ""); + // DOM.setStyleAttribute( + // DOM.getParent(previousVisibleWidget.getElement()), + // "visibility", ""); previousVisibleWidget = null; } } @@ -1214,4 +1275,53 @@ public class VTabsheet extends VTabsheetBase { } } + public void onBlur(BlurEvent event) { + focusedTab = null; + // TODO Auto-generated method stub + VConsole.log("BLUR " + event); + } + + public void onFocus(FocusEvent event) { + // TODO Auto-generated method stub + VConsole.log("FOCUS " + event); + if (event.getSource() instanceof Tab) { + focusedTab = (Tab) event.getSource(); + } + } + + public void focus() { + if (focusedTab == null) { + focusedTab = tb.getTab(activeTabIndex); + focusedTab.focus(); + } + } + + public void blur() { + if (focusedTab != null) { + focusedTab.blur(); + focusedTab = null; + } + } + + public void onKeyDown(KeyDownEvent event) { + if (event.getSource() instanceof Tab) { + VConsole.log("KEYDOWN"); + int keycode = event.getNativeEvent().getKeyCode(); + if (keycode == KeyCodes.KEY_LEFT) { + int newTabIndex = activeTabIndex == 0 ? tb.getTabCount() - 1 + : activeTabIndex - 1; + onTabSelected(newTabIndex); + activeTabIndex = newTabIndex; + } else if (keycode == KeyCodes.KEY_RIGHT) { + int newTabIndex = (activeTabIndex + 1) % tb.getTabCount(); + onTabSelected(newTabIndex); + activeTabIndex = newTabIndex; + } else if (keycode == KeyCodes.KEY_DELETE) { + VConsole.log("INDEX=" + activeTabIndex); + focusedTab.onClose(); + removeTab(activeTabIndex); + } + VConsole.log("tabindex -> " + activeTabIndex); + } + } } diff --git a/src/com/vaadin/ui/TabSheet.java b/src/com/vaadin/ui/TabSheet.java index a13c336943..1275eda69f 100644 --- a/src/com/vaadin/ui/TabSheet.java +++ b/src/com/vaadin/ui/TabSheet.java @@ -14,6 +14,10 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; +import com.vaadin.event.FieldEvents.BlurEvent; +import com.vaadin.event.FieldEvents.BlurListener; +import com.vaadin.event.FieldEvents.FocusEvent; +import com.vaadin.event.FieldEvents.FocusListener; import com.vaadin.terminal.ErrorMessage; import com.vaadin.terminal.KeyMapper; import com.vaadin.terminal.PaintException; @@ -21,6 +25,7 @@ import com.vaadin.terminal.PaintTarget; import com.vaadin.terminal.Resource; import com.vaadin.terminal.gwt.client.ui.VTabsheet; import com.vaadin.terminal.gwt.server.CommunicationManager; +import com.vaadin.ui.Component.Focusable; import com.vaadin.ui.themes.Reindeer; import com.vaadin.ui.themes.Runo; @@ -55,7 +60,8 @@ import com.vaadin.ui.themes.Runo; */ @SuppressWarnings("serial") @ClientWidget(VTabsheet.class) -public class TabSheet extends AbstractComponentContainer { +public class TabSheet extends AbstractComponentContainer implements Focusable, + FocusListener, BlurListener { /** * List of component tabs (tab contents). In addition to being on this list, @@ -95,6 +101,8 @@ public class TabSheet extends AbstractComponentContainer { */ private CloseHandler closeHandler; + private int tabIndex; + /** * Constructs a new Tabsheet. Tabsheet is immediate by default, and the * default close handler removes the tab being closed. @@ -363,6 +371,10 @@ public class TabSheet extends AbstractComponentContainer { target.addAttribute("hidetabs", true); } + if (tabIndex != 0) { + target.addAttribute("tabindex", tabIndex); + } + target.startTag("tabs"); Collection orphaned = new HashSet(paintedTabs); @@ -1226,4 +1238,27 @@ public class TabSheet extends AbstractComponentContainer { return components.indexOf(tab.getComponent()); } + public void blur(BlurEvent event) { + // TODO Auto-generated method stub + + } + + public void focus(FocusEvent event) { + // TODO Auto-generated method stub + } + + @Override + public void focus() { + super.focus(); + } + + public int getTabIndex() { + return tabIndex; + } + + public void setTabIndex(int tabIndex) { + this.tabIndex = tabIndex; + requestRepaint(); + } + } diff --git a/tests/testbench/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.java b/tests/testbench/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.java new file mode 100644 index 0000000000..43f3380e16 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.java @@ -0,0 +1,54 @@ +package com.vaadin.tests.components.tabsheet; + +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Label; +import com.vaadin.ui.Layout; +import com.vaadin.ui.TabSheet; +import com.vaadin.ui.TabSheet.Tab; +import com.vaadin.ui.TextField; +import com.vaadin.ui.VerticalLayout; + +public class TabKeyboardNavigation extends TestBase { + + int index = 1; + TabSheet ts = new TabSheet(); + + @Override + protected void setup() { + ts.setWidth("500px"); + ts.setHeight("500px"); + for (int i = 0; i < 4; ++i) { + addTab(); + } + Button b = new Button("Add a tab", new Button.ClickListener() { + public void buttonClick(ClickEvent event) { + addTab(); + } + }); + addComponent(b); + addComponent(new TextField()); + addComponent(ts); + addComponent(new TextField()); + } + + @Override + protected String getDescription() { + return "The tab bar should be focusable and arrow keys should switch tabs"; + } + + @Override + protected Integer getTicketNumber() { + return 5100; + } + + private Tab addTab() { + Layout content = new VerticalLayout(); + content.addComponent(new Label("Tab " + index)); + content.addComponent(new TextField()); + Tab tab = ts.addTab(content, "Tab " + index++, null); + tab.setClosable(true); + return tab; + } +} -- 2.39.5