You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FormLayoutConnector.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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.formlayout;
  17. import java.util.HashMap;
  18. import java.util.Map;
  19. import com.google.gwt.dom.client.Element;
  20. import com.google.gwt.user.client.ui.Widget;
  21. import com.vaadin.client.ComponentConnector;
  22. import com.vaadin.client.ConnectorHierarchyChangeEvent;
  23. import com.vaadin.client.LayoutManager;
  24. import com.vaadin.client.TooltipInfo;
  25. import com.vaadin.client.Util;
  26. import com.vaadin.client.WidgetUtil;
  27. import com.vaadin.client.communication.StateChangeEvent;
  28. import com.vaadin.client.ui.AbstractLayoutConnector;
  29. import com.vaadin.client.ui.HasErrorIndicator;
  30. import com.vaadin.client.ui.LayoutClickEventHandler;
  31. import com.vaadin.client.ui.PostLayoutListener;
  32. import com.vaadin.client.ui.VFormLayout;
  33. import com.vaadin.client.ui.VFormLayout.Caption;
  34. import com.vaadin.client.ui.VFormLayout.ErrorFlag;
  35. import com.vaadin.client.ui.VFormLayout.VFormLayoutTable;
  36. import com.vaadin.client.ui.layout.ElementResizeListener;
  37. import com.vaadin.shared.ui.Connect;
  38. import com.vaadin.shared.ui.LayoutClickRpc;
  39. import com.vaadin.shared.ui.MarginInfo;
  40. import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc;
  41. import com.vaadin.shared.ui.orderedlayout.FormLayoutState;
  42. import com.vaadin.ui.FormLayout;
  43. @Connect(FormLayout.class)
  44. public class FormLayoutConnector extends AbstractLayoutConnector
  45. implements PostLayoutListener {
  46. /*
  47. * Handlers & Listeners
  48. */
  49. private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
  50. this) {
  51. @Override
  52. protected ComponentConnector getChildComponent(
  53. com.google.gwt.user.client.Element element) {
  54. return Util.getConnectorForElement(getConnection(), getWidget(),
  55. element);
  56. }
  57. @Override
  58. protected LayoutClickRpc getLayoutClickRPC() {
  59. return getRpcProxy(AbstractOrderedLayoutServerRpc.class);
  60. }
  61. };
  62. private Map<ComponentConnector, String> oldMaxWidths = null;
  63. private static final ElementResizeListener DUMMY_FIRST_CELL_RESIZE_LISTENER = event -> {
  64. // Ignore event, listener added just to make measurements available
  65. };
  66. // Detects situations when there's something inside the FormLayout that
  67. // prevents it from shrinking
  68. private ElementResizeListener resizeListener = event -> {
  69. LayoutManager layoutManager = getLayoutManager();
  70. double tableWidth = layoutManager
  71. .getOuterWidthDouble(getWidget().table.getElement());
  72. double ownWidth = layoutManager
  73. .getInnerWidthDouble(getWidget().getElement());
  74. if (ownWidth < tableWidth) {
  75. // Something inside the table prevents it from shrinking,
  76. // temporarily force column widths
  77. double excessWidth = tableWidth - ownWidth;
  78. // All td elements in the component column have the same width,
  79. // so we only need to check the width of the first one to know
  80. // how wide the column is.
  81. Element firstComponentTd = findFirstComponentTd();
  82. if (firstComponentTd == null) {
  83. // Can't do anything if there are no rows
  84. return;
  85. }
  86. double componentColWidth = layoutManager
  87. .getOuterWidthDouble(firstComponentTd);
  88. if (componentColWidth == -1) {
  89. // Didn't get a proper width reading, best to not touch
  90. // anything
  91. return;
  92. }
  93. // Restrict content td width
  94. // Round down to prevent interactions with fractional sizes of
  95. // other columns
  96. int targetWidth = (int) Math.floor(componentColWidth - excessWidth);
  97. // Target might be negative if captions are wider than the total
  98. // available width
  99. targetWidth = Math.max(0, targetWidth);
  100. if (oldMaxWidths == null) {
  101. oldMaxWidths = new HashMap<>();
  102. }
  103. for (ComponentConnector child : getChildComponents()) {
  104. Element childElement = child.getWidget().getElement();
  105. if (!oldMaxWidths.containsKey(child)) {
  106. oldMaxWidths.put(child,
  107. childElement.getPropertyString("maxWidth"));
  108. }
  109. childElement.getStyle().setPropertyPx("maxWidth", targetWidth);
  110. layoutManager.reportOuterWidth(child, targetWidth);
  111. }
  112. }
  113. };
  114. @Override
  115. protected void init() {
  116. super.init();
  117. getLayoutManager().addElementResizeListener(
  118. getWidget().table.getElement(), resizeListener);
  119. getLayoutManager().addElementResizeListener(getWidget().getElement(),
  120. resizeListener);
  121. addComponentCellListener();
  122. }
  123. @Override
  124. public void onUnregister() {
  125. getLayoutManager().removeElementResizeListener(
  126. getWidget().table.getElement(), resizeListener);
  127. getLayoutManager().removeElementResizeListener(getWidget().getElement(),
  128. resizeListener);
  129. removeComponentCellListener();
  130. super.onUnregister();
  131. }
  132. @Override
  133. public FormLayoutState getState() {
  134. return (FormLayoutState) super.getState();
  135. }
  136. @Override
  137. public void onStateChanged(StateChangeEvent stateChangeEvent) {
  138. super.onStateChanged(stateChangeEvent);
  139. clickEventHandler.handleEventHandlerRegistration();
  140. VFormLayoutTable formLayoutTable = getWidget().table;
  141. formLayoutTable.setMargins(new MarginInfo(getState().marginsBitmask));
  142. formLayoutTable.setSpacing(getState().spacing);
  143. }
  144. @Override
  145. public void onConnectorHierarchyChange(
  146. ConnectorHierarchyChangeEvent event) {
  147. VFormLayout formLayout = getWidget();
  148. VFormLayoutTable formLayoutTable = getWidget().table;
  149. removeComponentCellListener();
  150. int childId = 0;
  151. formLayoutTable.setRowCount(getChildComponents().size());
  152. for (ComponentConnector child : getChildComponents()) {
  153. Widget childWidget = child.getWidget();
  154. Caption caption = formLayoutTable.getCaption(childWidget);
  155. if (caption == null) {
  156. caption = formLayout.new Caption(child);
  157. caption.addClickHandler(formLayoutTable);
  158. }
  159. ErrorFlag error = formLayoutTable.getError(childWidget);
  160. if (error == null) {
  161. error = formLayout.new ErrorFlag(child);
  162. }
  163. formLayoutTable.setChild(childId, childWidget, caption, error);
  164. childId++;
  165. }
  166. for (ComponentConnector oldChild : event.getOldChildren()) {
  167. if (oldChild.getParent() == this) {
  168. continue;
  169. }
  170. formLayoutTable.cleanReferences(oldChild.getWidget());
  171. }
  172. addComponentCellListener();
  173. }
  174. private void addComponentCellListener() {
  175. Element td = findFirstComponentTd();
  176. if (td != null) {
  177. getLayoutManager().addElementResizeListener(td,
  178. DUMMY_FIRST_CELL_RESIZE_LISTENER);
  179. }
  180. }
  181. private void removeComponentCellListener() {
  182. Element td = findFirstComponentTd();
  183. if (td != null) {
  184. getLayoutManager().removeElementResizeListener(td,
  185. DUMMY_FIRST_CELL_RESIZE_LISTENER);
  186. }
  187. }
  188. private Element findFirstComponentTd() {
  189. VFormLayoutTable table = getWidget().table;
  190. if (table.getRowCount() == 0) {
  191. return null;
  192. } else {
  193. return table.getCellFormatter().getElement(0,
  194. VFormLayoutTable.COLUMN_WIDGET);
  195. }
  196. }
  197. @Override
  198. public void updateCaption(ComponentConnector component) {
  199. getWidget().table.updateCaption(component.getWidget(),
  200. component.getState(), component.isEnabled());
  201. boolean hideErrors = false;
  202. if (component instanceof HasErrorIndicator) {
  203. hideErrors = !((HasErrorIndicator) component)
  204. .isErrorIndicatorVisible();
  205. }
  206. getWidget().table.updateError(component.getWidget(),
  207. component.getState().errorMessage,
  208. component.getState().errorLevel, hideErrors);
  209. }
  210. @Override
  211. public VFormLayout getWidget() {
  212. return (VFormLayout) super.getWidget();
  213. }
  214. @Override
  215. public TooltipInfo getTooltipInfo(Element element) {
  216. TooltipInfo info = null;
  217. if (element != getWidget().getElement()) {
  218. Object node = WidgetUtil.findWidget(element,
  219. VFormLayout.Caption.class);
  220. if (node != null) {
  221. VFormLayout.Caption caption = (VFormLayout.Caption) node;
  222. info = caption.getOwner().getTooltipInfo(element);
  223. } else {
  224. node = WidgetUtil.findWidget(element,
  225. VFormLayout.ErrorFlag.class);
  226. if (node != null) {
  227. VFormLayout.ErrorFlag flag = (VFormLayout.ErrorFlag) node;
  228. info = flag.getOwner().getTooltipInfo(element);
  229. }
  230. }
  231. }
  232. if (info == null) {
  233. info = super.getTooltipInfo(element);
  234. }
  235. return info;
  236. }
  237. @Override
  238. public boolean hasTooltip() {
  239. /*
  240. * Tooltips are fetched from child connectors -> there's no quick way of
  241. * checking whether there might a tooltip hiding somewhere
  242. */
  243. return true;
  244. }
  245. @Override
  246. public void postLayout() {
  247. if (oldMaxWidths != null) {
  248. for (ComponentConnector child : getChildComponents()) {
  249. Element childNode = child.getWidget().getElement();
  250. String oldValue = oldMaxWidths.get(child);
  251. if (oldValue == null) {
  252. childNode.getStyle().clearProperty("maxWidth");
  253. } else {
  254. childNode.getStyle().setProperty("maxWidth", oldValue);
  255. }
  256. }
  257. oldMaxWidths = null;
  258. }
  259. }
  260. }