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