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.

MeasuredSize.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. * Copyright 2000-2021 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;
  17. import java.util.logging.Logger;
  18. import com.google.gwt.core.client.JsArrayString;
  19. import com.google.gwt.dom.client.Document;
  20. import com.google.gwt.dom.client.Element;
  21. public class MeasuredSize {
  22. private static final boolean DEBUG_SIZE_CHANGES = false;
  23. public static class MeasureResult {
  24. private final boolean widthChanged;
  25. private final boolean heightChanged;
  26. private MeasureResult(boolean widthChanged, boolean heightChanged) {
  27. this.widthChanged = widthChanged;
  28. this.heightChanged = heightChanged;
  29. }
  30. public boolean isHeightChanged() {
  31. return heightChanged;
  32. }
  33. public boolean isWidthChanged() {
  34. return widthChanged;
  35. }
  36. public boolean isChanged() {
  37. return heightChanged || widthChanged;
  38. }
  39. }
  40. private double width = -1;
  41. private double height = -1;
  42. private int[] paddings = new int[4];
  43. private int[] borders = new int[4];
  44. private int[] margins = new int[4];
  45. private FastStringSet dependents = FastStringSet.create();
  46. public double getOuterHeight() {
  47. return height;
  48. }
  49. public double getOuterWidth() {
  50. return width;
  51. }
  52. public void addDependent(String pid) {
  53. dependents.add(pid);
  54. }
  55. public void removeDependent(String pid) {
  56. dependents.remove(pid);
  57. }
  58. public boolean hasDependents() {
  59. return !dependents.isEmpty();
  60. }
  61. public JsArrayString getDependents() {
  62. return dependents.dump();
  63. }
  64. private static int sumWidths(int[] sizes) {
  65. return sizes[1] + sizes[3];
  66. }
  67. private static int sumHeights(int[] sizes) {
  68. return sizes[0] + sizes[2];
  69. }
  70. public double getInnerHeight() {
  71. return height - sumHeights(margins) - sumHeights(borders)
  72. - sumHeights(paddings);
  73. }
  74. public double getInnerWidth() {
  75. return width - sumWidths(margins) - sumWidths(borders)
  76. - sumWidths(paddings);
  77. }
  78. public boolean setOuterHeight(double height) {
  79. if (this.height != height) {
  80. this.height = height;
  81. return true;
  82. } else {
  83. return false;
  84. }
  85. }
  86. public boolean setOuterWidth(double width) {
  87. if (this.width != width) {
  88. this.width = width;
  89. return true;
  90. } else {
  91. return false;
  92. }
  93. }
  94. public int getBorderHeight() {
  95. return sumHeights(borders);
  96. }
  97. public int getBorderWidth() {
  98. return sumWidths(borders);
  99. }
  100. public int getPaddingHeight() {
  101. return sumHeights(paddings);
  102. }
  103. public int getPaddingWidth() {
  104. return sumWidths(paddings);
  105. }
  106. public int getMarginHeight() {
  107. return sumHeights(margins);
  108. }
  109. public int getMarginWidth() {
  110. return sumWidths(margins);
  111. }
  112. public int getMarginTop() {
  113. return margins[0];
  114. }
  115. public int getMarginRight() {
  116. return margins[1];
  117. }
  118. public int getMarginBottom() {
  119. return margins[2];
  120. }
  121. public int getMarginLeft() {
  122. return margins[3];
  123. }
  124. public int getBorderTop() {
  125. return borders[0];
  126. }
  127. public int getBorderRight() {
  128. return borders[1];
  129. }
  130. public int getBorderBottom() {
  131. return borders[2];
  132. }
  133. public int getBorderLeft() {
  134. return borders[3];
  135. }
  136. public int getPaddingTop() {
  137. return paddings[0];
  138. }
  139. public int getPaddingRight() {
  140. return paddings[1];
  141. }
  142. public int getPaddingBottom() {
  143. return paddings[2];
  144. }
  145. public int getPaddingLeft() {
  146. return paddings[3];
  147. }
  148. /**
  149. * Measures paddings, margins, border, height, and weight of the given
  150. * element and stores the results within this {@link MeasuredSize} object.
  151. * The measurements are unreliable if the element or any of its parents are
  152. * in the middle of a transform animation.
  153. *
  154. * @param element
  155. * element to be measured
  156. * @return data object for whether the width or height of the given element
  157. * has changed since previous measure
  158. * @see {@link #measure(Element, boolean)}
  159. */
  160. public MeasureResult measure(Element element) {
  161. return measure(element, false);
  162. }
  163. /**
  164. * Measures paddings, margins, border, height, and weight of the given
  165. * element and stores the results within this {@link MeasuredSize} object.
  166. *
  167. * @param element
  168. * element to be measured
  169. * @param thoroughSizeCheck
  170. * {@code true} if the measuring should use the more reliable
  171. * size check that requires ensuring that the element is still
  172. * present in the DOM tree, {@code false} for the slightly faster
  173. * check that will give incorrect size information if this method
  174. * is called while the element or any of its parents are in the
  175. * middle of a transform animation.
  176. * @return data object for whether the width or height of the given element
  177. * has changed since previous measure
  178. */
  179. public MeasureResult measure(Element element, boolean thoroughSizeCheck) {
  180. if (thoroughSizeCheck
  181. && !Document.get().getBody().isOrHasChild(element)) {
  182. return new MeasureResult(false, false);
  183. }
  184. Profiler.enter("MeasuredSize.measure");
  185. boolean heightChanged = false;
  186. boolean widthChanged = false;
  187. Profiler.enter("new ComputedStyle");
  188. ComputedStyle computedStyle = new ComputedStyle(element);
  189. int[] paddings = computedStyle.getPadding();
  190. // Some browsers do not reflow until accessing data from the computed
  191. // style object
  192. Profiler.leave("new ComputedStyle");
  193. Profiler.enter("Measure paddings");
  194. if (!heightChanged && hasHeightChanged(this.paddings, paddings)) {
  195. debugSizeChange(element, "Height (padding)", this.paddings,
  196. paddings);
  197. heightChanged = true;
  198. }
  199. if (!widthChanged && hasWidthChanged(this.paddings, paddings)) {
  200. debugSizeChange(element, "Width (padding)", this.paddings,
  201. paddings);
  202. widthChanged = true;
  203. }
  204. this.paddings = paddings;
  205. Profiler.leave("Measure paddings");
  206. Profiler.enter("Measure margins");
  207. int[] margins = computedStyle.getMargin();
  208. if (!heightChanged && hasHeightChanged(this.margins, margins)) {
  209. debugSizeChange(element, "Height (margins)", this.margins, margins);
  210. heightChanged = true;
  211. }
  212. if (!widthChanged && hasWidthChanged(this.margins, margins)) {
  213. debugSizeChange(element, "Width (margins)", this.margins, margins);
  214. widthChanged = true;
  215. }
  216. this.margins = margins;
  217. Profiler.leave("Measure margins");
  218. Profiler.enter("Measure borders");
  219. int[] borders = computedStyle.getBorder();
  220. if (!heightChanged && hasHeightChanged(this.borders, borders)) {
  221. debugSizeChange(element, "Height (borders)", this.borders, borders);
  222. heightChanged = true;
  223. }
  224. if (!widthChanged && hasWidthChanged(this.borders, borders)) {
  225. debugSizeChange(element, "Width (borders)", this.borders, borders);
  226. widthChanged = true;
  227. }
  228. this.borders = borders;
  229. Profiler.leave("Measure borders");
  230. Profiler.enter("Measure height");
  231. double requiredHeight;
  232. if (thoroughSizeCheck) {
  233. requiredHeight = computedStyle.getHeightIncludingBorderPadding();
  234. if (Double.isNaN(requiredHeight)) {
  235. requiredHeight = 0;
  236. }
  237. } else {
  238. requiredHeight = WidgetUtil.getRequiredHeightDouble(element);
  239. }
  240. double outerHeight = requiredHeight + sumHeights(margins);
  241. double oldHeight = height;
  242. if (setOuterHeight(outerHeight)) {
  243. debugSizeChange(element, "Height (outer)", oldHeight, height);
  244. heightChanged = true;
  245. }
  246. Profiler.leave("Measure height");
  247. Profiler.enter("Measure width");
  248. double requiredWidth;
  249. if (thoroughSizeCheck) {
  250. requiredWidth = computedStyle.getWidthIncludingBorderPadding();
  251. if (Double.isNaN(requiredWidth)) {
  252. requiredWidth = 0;
  253. }
  254. } else {
  255. requiredWidth = WidgetUtil.getRequiredWidthDouble(element);
  256. }
  257. double outerWidth = requiredWidth + sumWidths(margins);
  258. double oldWidth = width;
  259. if (setOuterWidth(outerWidth)) {
  260. debugSizeChange(element, "Width (outer)", oldWidth, width);
  261. widthChanged = true;
  262. }
  263. Profiler.leave("Measure width");
  264. Profiler.leave("MeasuredSize.measure");
  265. return new MeasureResult(widthChanged, heightChanged);
  266. }
  267. private void debugSizeChange(Element element, String sizeChangeType,
  268. int[] changedFrom, int[] changedTo) {
  269. debugSizeChange(element, sizeChangeType,
  270. java.util.Arrays.asList(changedFrom).toString(),
  271. java.util.Arrays.asList(changedTo).toString());
  272. }
  273. private void debugSizeChange(Element element, String sizeChangeType,
  274. double changedFrom, double changedTo) {
  275. debugSizeChange(element, sizeChangeType, String.valueOf(changedFrom),
  276. String.valueOf(changedTo));
  277. }
  278. private void debugSizeChange(Element element, String sizeChangeType,
  279. String changedFrom, String changedTo) {
  280. if (DEBUG_SIZE_CHANGES) {
  281. getLogger().info(sizeChangeType + " has changed from " + changedFrom
  282. + " to " + changedTo + " for " + element);
  283. }
  284. }
  285. private static boolean hasWidthChanged(int[] sizes1, int[] sizes2) {
  286. return sizes1[1] != sizes2[1] || sizes1[3] != sizes2[3];
  287. }
  288. private static boolean hasHeightChanged(int[] sizes1, int[] sizes2) {
  289. return sizes1[0] != sizes2[0] || sizes1[2] != sizes2[2];
  290. }
  291. private static Logger getLogger() {
  292. return Logger.getLogger(MeasuredSize.class.getName());
  293. }
  294. }