| /* |
| * Copyright 2010 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.gwt.user.client.ui.impl; |
| |
| import com.google.gwt.core.client.JavaScriptObject; |
| import com.google.gwt.user.client.Element; |
| |
| /** |
| * Implementation of {@link com.google.gwt.user.client.ui.impl.FocusImpl} that |
| * uses a hidden input element to serve as a 'proxy' for accesskeys, which are |
| * only supported on form elements in most browsers. |
| */ |
| public class FocusImplStandard extends FocusImpl { |
| |
| /** |
| * Single focusHandler shared by all focusable. |
| */ |
| static JavaScriptObject focusHandler; |
| |
| private static native Element createFocusable0(JavaScriptObject focusHandler) /*-{ |
| // Divs are focusable in all browsers, but only IE supports the accessKey |
| // property on divs. We use the infamous 'hidden input' trick to add an |
| // accessKey to the focusable div. Note that the input is only used to |
| // capture focus when the accessKey is pressed. Focus is forwarded to the |
| // div immediately. |
| var div = $doc.createElement('div'); |
| div.tabIndex = 0; |
| |
| var input = $doc.createElement('input'); |
| input.type = 'text'; |
| input.tabIndex = -1; |
| var style = input.style; |
| style.opacity = 0; |
| style.height = '1px'; |
| style.width = '1px'; |
| style.zIndex = -1; |
| style.overflow = 'hidden'; |
| style.position = 'absolute'; |
| |
| // Note that we're using isolated lambda methods as the event listeners |
| // to avoid creating a memory leaks. (Lambdas here would create cycles |
| // involving the div and input). This also allows us to share a single |
| // set of handlers among every focusable item. |
| input.addEventListener('focus', focusHandler, false); |
| |
| div.appendChild(input); |
| return div; |
| }-*/; |
| |
| @Override |
| public Element createFocusable() { |
| return createFocusable0(ensureFocusHandler()); |
| } |
| |
| @Override |
| public native void setAccessKey(Element elem, char key) /*-{ |
| elem.firstChild.accessKey = String.fromCharCode(key); |
| }-*/; |
| |
| /** |
| * Use an isolated method call to create the handler to avoid creating memory |
| * leaks via handler-closures-element. |
| */ |
| private native JavaScriptObject createFocusHandler() /*-{ |
| return function(evt) { |
| // This function is called directly as an event handler, so 'this' is |
| // set up by the browser to be the input on which the event is fired. We |
| // call focus() in a timeout or the element may be blurred when this event |
| // ends. |
| var div = this.parentNode; |
| if (div.onfocus) { |
| $wnd.setTimeout(function() { |
| div.focus(); |
| }, 0); |
| } |
| }; |
| }-*/; |
| |
| private JavaScriptObject ensureFocusHandler() { |
| return focusHandler != null ? focusHandler : (focusHandler = createFocusHandler()); |
| } |
| } |