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
68                      .setClassName(VPanel.CLASSNAME + "-deco");
69              panel.captionNode.setClassName(VPanel.CLASSNAME + "-caption");
70              boolean hasCaption = hasCaption();
71              if (hasCaption) {
72                  panel.setCaption(getState().caption);
73              } else {
74                  panel.setCaption("");
75                  panel.captionNode
76                          .setClassName(VPanel.CLASSNAME + "-nocaption");
77              }
78  
79              // Add proper stylenames for all elements. This way we can prevent
80              // unwanted CSS selector inheritance.
81              final String captionBaseClass = VPanel.CLASSNAME
82                      + (hasCaption ? "-caption" : "-nocaption");
83              final String contentBaseClass = VPanel.CLASSNAME + "-content";
84              final String decoBaseClass = VPanel.CLASSNAME + "-deco";
85              String captionClass = captionBaseClass;
86              String contentClass = contentBaseClass;
87              String decoClass = decoBaseClass;
88              if (ComponentStateUtil.hasStyles(getState())) {
89                  for (String style : getState().styles) {
90                      captionClass += " " + captionBaseClass + "-" + style;
91                      contentClass += " " + contentBaseClass + "-" + style;
92                      decoClass += " " + decoBaseClass + "-" + style;
93                  }
94              }
95              panel.captionNode.setClassName(captionClass);
96              panel.contentNode.setClassName(contentClass);
97              panel.bottomDecoration.setClassName(decoClass);
98  
99              panel.makeScrollable();
100         }
101 
102         if (!isRealUpdate(uidl)) {
103             return;
104         }
105 
106         clickEventHandler.handleEventHandlerRegistration();
107 
108         panel.client = client;
109         panel.id = uidl.getId();
110 
111         if (getIconUri() != null) {
112             panel.setIconUri(getIconUri(), client);
113         } else {
114             panel.setIconUri(null, client);
115         }
116 
117         // We may have actions attached to this panel
118         if (uidl.getChildCount() > 0) {
119             final int cnt = uidl.getChildCount();
120             for (int i = 0; i < cnt; i++) {
121                 UIDL childUidl = uidl.getChildUIDL(i);
122                 if (childUidl.getTag().equals("actions")) {
123                     if (panel.shortcutHandler == null) {
124                         panel.shortcutHandler = new EnterFriendlyShortcutActionHandler(
125                                 getConnectorId(), client);
126                     }
127                     panel.shortcutHandler.updateActionMap(childUidl);
128                 }
129             }
130         }
131 
132         if (getState().scrollTop != panel.scrollTop) {
133             // Sizes are not yet up to date, so changing the scroll position
134             // is deferred to after the layout phase
135             uidlScrollTop = getState().scrollTop;
136         }
137 
138         if (getState().scrollLeft != panel.scrollLeft) {
139             // Sizes are not yet up to date, so changing the scroll position
140             // is deferred to after the layout phase
141             uidlScrollLeft = getState().scrollLeft;
142         }
143 
144         // And apply tab index
145         panel.contentNode.setTabIndex(getState().tabIndex);
146     }
147 
148     /**
149      * Detects if caption div should be visible.
150      *
151      * @return {@code true} if caption div should be shown
152      */
153     protected boolean hasCaption() {
154         return getState().caption != null && !getState().caption.isEmpty();
155     }
156 
157     @Override
158     public void postLayout() {
159         VPanel panel = getWidget();
160         if (uidlScrollTop != null) {
161             // IE / Safari fix for when scroll top is set to greater than panel
162             // height
163             int maxScroll = panel.getWidget().getOffsetHeight();
164             if (uidlScrollTop > maxScroll) {
165                 uidlScrollTop = maxScroll;
166             }
167             panel.contentNode.setScrollTop(uidlScrollTop.intValue());
168 
169             // Read actual value back to ensure update logic is correct
170             // TODO Does this trigger reflows?
171             panel.scrollTop = panel.contentNode.getScrollTop();
172             uidlScrollTop = null;
173         }
174 
175         if (uidlScrollLeft != null) {
176             panel.contentNode.setScrollLeft(uidlScrollLeft.intValue());
177             // Read actual value back to ensure update logic is correct
178             // TODO Does this trigger reflows?
179             panel.scrollLeft = panel.contentNode.getScrollLeft();
180             uidlScrollLeft = null;
181         }
182     }
183 
184     /**
185      * We override default {@link ShortcutActionHandler} to bypass shortcut handling when an 'ENTER' keyboard event comes from a text area.
186      */
187     private static class EnterFriendlyShortcutActionHandler extends ShortcutActionHandler {
188 
189         public EnterFriendlyShortcutActionHandler(String pid, ApplicationConnection c) {
190             super(pid, c);
191     }
192 
193         public void handleKeyboardEvent(Event event) {
194             // ignore keyboard shortcut if enter was pressed within a textarea
195             int keyCode = DOM.eventGetKeyCode(event);
196             if (keyCode == KeyCodes.KEY_ENTER) {
197                 Element el = DOM.eventGetTarget(event);
198                 if (el.getTagName().equalsIgnoreCase(TextAreaElement.TAG)) {
199                     return;
200                 }
201             }
202             super.handleKeyboardEvent(event);
203         }
204     }
205 }