View Javadoc
1   /*
2    * Copyright 2000-2016 Vaadin Ltd.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package com.vaadin.client.ui.panel;
17  
18  import com.google.gwt.dom.client.Element;
19  import com.google.gwt.dom.client.NativeEvent;
20  import com.google.gwt.dom.client.Style;
21  import com.google.gwt.dom.client.TextAreaElement;
22  import com.google.gwt.dom.client.Style.Unit;
23  import com.google.gwt.event.dom.client.KeyCodes;
24  import com.google.gwt.user.client.DOM;
25  import com.google.gwt.user.client.Event;
26  import com.vaadin.client.ApplicationConnection;
27  import com.vaadin.client.ComponentConnector;
28  import com.vaadin.client.ConnectorHierarchyChangeEvent;
29  import com.vaadin.client.LayoutManager;
30  import com.vaadin.client.Paintable;
31  import com.vaadin.client.Profiler;
32  import com.vaadin.client.UIDL;
33  import com.vaadin.client.ui.AbstractSingleComponentContainerConnector;
34  import com.vaadin.client.ui.ClickEventHandler;
35  import com.vaadin.client.ui.PostLayoutListener;
36  import com.vaadin.client.ui.ShortcutActionHandler;
37  import com.vaadin.client.ui.SimpleManagedLayout;
38  import com.vaadin.client.ui.VPanel;
39  import com.vaadin.client.ui.layout.MayScrollChildren;
40  import com.vaadin.client.ui.panel.PanelConnector;
41  import com.vaadin.shared.MouseEventDetails;
42  import com.vaadin.shared.ui.ComponentStateUtil;
43  import com.vaadin.shared.ui.Connect;
44  import com.vaadin.shared.ui.panel.PanelServerRpc;
45  import com.vaadin.shared.ui.panel.PanelState;
46  import com.vaadin.ui.Panel;
47  
48  @Connect(Panel.class)
49  public class EnterFriendlyPanelConnector extends PanelConnector
50          implements Paintable, SimpleManagedLayout, PostLayoutListener,
51          MayScrollChildren {
52  
53      private Integer uidlScrollTop;
54  
55      private ClickEventHandler clickEventHandler = new ClickEventHandler(this) {
56  
57          @Override
58          protected void fireClick(NativeEvent event,
59                  MouseEventDetails mouseDetails) {
60              getRpcProxy(PanelServerRpc.class).click(mouseDetails);
61          }
62      };
63  
64      private Integer uidlScrollLeft;
65  
66      @Override
67      public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
68          if (isRealUpdate(uidl)) {
69  
70              // Handle caption displaying and style names, prior generics.
71              // Affects size calculations
72  
73              // Restore default stylenames
74              getWidget().contentNode.setClassName(VPanel.CLASSNAME + "-content");
75              getWidget().bottomDecoration
76                      .setClassName(VPanel.CLASSNAME + "-deco");
77              getWidget().captionNode.setClassName(VPanel.CLASSNAME + "-caption");
78              boolean hasCaption = false;
79              if (getState().caption != null && !"".equals(getState().caption)) {
80                  getWidget().setCaption(getState().caption);
81                  hasCaption = true;
82              } else {
83                  getWidget().setCaption("");
84                  getWidget().captionNode
85                          .setClassName(VPanel.CLASSNAME + "-nocaption");
86              }
87  
88              // Add proper stylenames for all elements. This way we can prevent
89              // unwanted CSS selector inheritance.
90              final String captionBaseClass = VPanel.CLASSNAME
91                      + (hasCaption ? "-caption" : "-nocaption");
92              final String contentBaseClass = VPanel.CLASSNAME + "-content";
93              final String decoBaseClass = VPanel.CLASSNAME + "-deco";
94              String captionClass = captionBaseClass;
95              String contentClass = contentBaseClass;
96              String decoClass = decoBaseClass;
97              if (ComponentStateUtil.hasStyles(getState())) {
98                  for (String style : getState().styles) {
99                      captionClass += " " + captionBaseClass + "-" + style;
100                     contentClass += " " + contentBaseClass + "-" + style;
101                     decoClass += " " + decoBaseClass + "-" + style;
102                 }
103             }
104             getWidget().captionNode.setClassName(captionClass);
105             getWidget().contentNode.setClassName(contentClass);
106             getWidget().bottomDecoration.setClassName(decoClass);
107 
108             getWidget().makeScrollable();
109         }
110 
111         if (!isRealUpdate(uidl)) {
112             return;
113         }
114 
115         clickEventHandler.handleEventHandlerRegistration();
116 
117         getWidget().client = client;
118         getWidget().id = uidl.getId();
119 
120         if (getIconUri() != null) {
121             getWidget().setIconUri(getIconUri(), client);
122         } else {
123             getWidget().setIconUri(null, client);
124         }
125 
126         getWidget().setErrorIndicatorVisible(isErrorIndicatorVisible());
127 
128         // We may have actions attached to this panel
129         if (uidl.getChildCount() > 0) {
130             final int cnt = uidl.getChildCount();
131             for (int i = 0; i < cnt; i++) {
132                 UIDL childUidl = uidl.getChildUIDL(i);
133                 if (childUidl.getTag().equals("actions")) {
134                     if (getWidget().shortcutHandler == null) {
135                         getWidget().shortcutHandler = new EnterFriendlyShortcutActionHandler(
136                                 getConnectorId(), client);
137                     }
138                     getWidget().shortcutHandler.updateActionMap(childUidl);
139                 }
140             }
141         }
142 
143         if (getState().scrollTop != getWidget().scrollTop) {
144             // Sizes are not yet up to date, so changing the scroll position
145             // is deferred to after the layout phase
146             uidlScrollTop = getState().scrollTop;
147         }
148 
149         if (getState().scrollLeft != getWidget().scrollLeft) {
150             // Sizes are not yet up to date, so changing the scroll position
151             // is deferred to after the layout phase
152             uidlScrollLeft = getState().scrollLeft;
153         }
154 
155         // And apply tab index
156         getWidget().contentNode.setTabIndex(getState().tabIndex);
157     }
158 
159     @Override
160     public void postLayout() {
161         VPanel panel = getWidget();
162         if (uidlScrollTop != null) {
163             // IE / Safari fix for when scroll top is set to greater than panel
164             // height
165             int maxScroll = panel.getWidget().getOffsetHeight();
166             if (uidlScrollTop > maxScroll) {
167                 uidlScrollTop = maxScroll;
168             }
169             panel.contentNode.setScrollTop(uidlScrollTop.intValue());
170 
171             // Read actual value back to ensure update logic is correct
172             // TODO Does this trigger reflows?
173             panel.scrollTop = panel.contentNode.getScrollTop();
174             uidlScrollTop = null;
175         }
176 
177         if (uidlScrollLeft != null) {
178             panel.contentNode.setScrollLeft(uidlScrollLeft.intValue());
179             // Read actual value back to ensure update logic is correct
180             // TODO Does this trigger reflows?
181             panel.scrollLeft = panel.contentNode.getScrollLeft();
182             uidlScrollLeft = null;
183         }
184     }
185 
186     /**
187      * We override default {@link ShortcutActionHandler} to bypass shortcut handling when an 'ENTER' keyboard event comes from a text area.
188      */
189     private static class EnterFriendlyShortcutActionHandler extends ShortcutActionHandler {
190 
191         public EnterFriendlyShortcutActionHandler(String pid, ApplicationConnection c) {
192             super(pid, c);
193     }
194 
195         public void handleKeyboardEvent(Event event) {
196             // ignore keyboard shortcut if enter was pressed within a textarea
197             int keyCode = DOM.eventGetKeyCode(event);
198             if (keyCode == KeyCodes.KEY_ENTER) {
199                 Element el = DOM.eventGetTarget(event);
200                 if (el.getTagName().equalsIgnoreCase(TextAreaElement.TAG)) {
201                     return;
202                 }
203             }
204             super.handleKeyboardEvent(event);
205         }
206     }
207 }