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.

VGridLayout.java 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  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;
  17. import java.util.HashMap;
  18. import java.util.LinkedList;
  19. import java.util.List;
  20. import java.util.Set;
  21. import com.google.gwt.dom.client.DivElement;
  22. import com.google.gwt.dom.client.Document;
  23. import com.google.gwt.dom.client.Element;
  24. import com.google.gwt.dom.client.Style;
  25. import com.google.gwt.dom.client.Style.Position;
  26. import com.google.gwt.dom.client.Style.Unit;
  27. import com.google.gwt.user.client.DOM;
  28. import com.google.gwt.user.client.ui.ComplexPanel;
  29. import com.google.gwt.user.client.ui.Widget;
  30. import com.vaadin.client.ApplicationConnection;
  31. import com.vaadin.client.ComponentConnector;
  32. import com.vaadin.client.ConnectorMap;
  33. import com.vaadin.client.LayoutManager;
  34. import com.vaadin.client.StyleConstants;
  35. import com.vaadin.client.Util;
  36. import com.vaadin.client.VCaption;
  37. import com.vaadin.client.ui.gridlayout.GridLayoutConnector;
  38. import com.vaadin.client.ui.layout.ComponentConnectorLayoutSlot;
  39. import com.vaadin.client.ui.layout.VLayoutSlot;
  40. import com.vaadin.shared.ui.AlignmentInfo;
  41. import com.vaadin.shared.ui.MarginInfo;
  42. import com.vaadin.shared.ui.gridlayout.GridLayoutState.ChildComponentData;
  43. public class VGridLayout extends ComplexPanel {
  44. public static final String CLASSNAME = "v-gridlayout";
  45. /** For internal use only. May be removed or replaced in the future. */
  46. public ApplicationConnection client;
  47. /** For internal use only. May be removed or replaced in the future. */
  48. public HashMap<Widget, Cell> widgetToCell = new HashMap<>();
  49. /** For internal use only. May be removed or replaced in the future. */
  50. public int[] columnWidths;
  51. /** For internal use only. May be removed or replaced in the future. */
  52. public int[] rowHeights;
  53. /** For internal use only. May be removed or replaced in the future. */
  54. public float[] colExpandRatioArray;
  55. /** For internal use only. May be removed or replaced in the future. */
  56. public float[] rowExpandRatioArray;
  57. int[] minColumnWidths;
  58. private int[] minRowHeights;
  59. /** For internal use only. May be removed or replaced in the future. */
  60. public DivElement spacingMeasureElement;
  61. public Set<Integer> explicitRowRatios;
  62. public Set<Integer> explicitColRatios;
  63. public boolean hideEmptyRowsAndColumns = false;
  64. public VGridLayout() {
  65. super();
  66. setElement(Document.get().createDivElement());
  67. spacingMeasureElement = Document.get().createDivElement();
  68. Style spacingStyle = spacingMeasureElement.getStyle();
  69. spacingStyle.setPosition(Position.ABSOLUTE);
  70. getElement().appendChild(spacingMeasureElement);
  71. setStyleName(CLASSNAME);
  72. addStyleName(StyleConstants.UI_LAYOUT);
  73. publishJSHelpers(getElement());
  74. }
  75. private native void publishJSHelpers(Element root)
  76. /*-{
  77. var self = this;
  78. root.getRowCount = $entry(function () {
  79. return self.@VGridLayout::getRowCount()();
  80. });
  81. root.getColumnCount = $entry(function () {
  82. return self.@VGridLayout::getColumnCount()();
  83. });
  84. root.getCell = $entry(function (row,column) {
  85. return self.@VGridLayout::getCellElement(*)(row, column);
  86. });
  87. }-*/;
  88. private GridLayoutConnector getConnector() {
  89. return (GridLayoutConnector) ConnectorMap.get(client)
  90. .getConnector(this);
  91. }
  92. /**
  93. * Returns the column widths measured in pixels
  94. *
  95. * @return
  96. */
  97. protected int[] getColumnWidths() {
  98. return columnWidths;
  99. }
  100. /**
  101. * Returns the row heights measured in pixels
  102. *
  103. * @return
  104. */
  105. protected int[] getRowHeights() {
  106. return rowHeights;
  107. }
  108. /**
  109. * Returns the spacing between the cells horizontally in pixels
  110. *
  111. * @return
  112. */
  113. protected int getHorizontalSpacing() {
  114. return LayoutManager.get(client).getOuterWidth(spacingMeasureElement);
  115. }
  116. /**
  117. * Returns the spacing between the cells vertically in pixels
  118. *
  119. * @return
  120. */
  121. protected int getVerticalSpacing() {
  122. return LayoutManager.get(client).getOuterHeight(spacingMeasureElement);
  123. }
  124. static int[] cloneArray(int[] toBeCloned) {
  125. int[] clone = new int[toBeCloned.length];
  126. for (int i = 0; i < clone.length; i++) {
  127. clone[i] = toBeCloned[i] * 1;
  128. }
  129. return clone;
  130. }
  131. void expandRows() {
  132. if (!isUndefinedHeight()) {
  133. int usedSpace = calcRowUsedSpace();
  134. float[] actualExpandRatio = calcRowExpandRatio();
  135. // Round down to avoid problems with fractions (100.1px available ->
  136. // can use 100, not 101)
  137. int availableSpace = (int) LayoutManager.get(client)
  138. .getInnerHeightDouble(getElement());
  139. int excessSpace = availableSpace - usedSpace;
  140. int distributed = 0;
  141. if (excessSpace > 0) {
  142. float expandRatioSum = 0;
  143. for (int i = 0; i < rowHeights.length; i++) {
  144. expandRatioSum += actualExpandRatio[i];
  145. }
  146. for (int i = 0; i < rowHeights.length; i++) {
  147. int ew = (int) (excessSpace * actualExpandRatio[i]
  148. / expandRatioSum);
  149. rowHeights[i] = minRowHeights[i] + ew;
  150. distributed += ew;
  151. }
  152. excessSpace -= distributed;
  153. int c = 0;
  154. while (excessSpace > 0) {
  155. rowHeights[c % rowHeights.length]++;
  156. excessSpace--;
  157. c++;
  158. }
  159. }
  160. }
  161. }
  162. private float[] calcRowExpandRatio() {
  163. float[] actualExpandRatio = new float[minRowHeights.length];
  164. for (int i = 0; i < minRowHeights.length; i++) {
  165. if (hiddenEmptyRow(i)) {
  166. actualExpandRatio[i] = 0;
  167. } else {
  168. actualExpandRatio[i] = rowExpandRatioArray[i];
  169. }
  170. }
  171. return actualExpandRatio;
  172. }
  173. /**
  174. * Checks if it is ok to hide (or ignore) the given row.
  175. *
  176. * @param rowIndex
  177. * the row to check
  178. * @return true, if the row should be interpreted as non-existant (hides
  179. * extra spacing)
  180. */
  181. private boolean hiddenEmptyRow(int rowIndex) {
  182. return hideEmptyRowsAndColumns && !rowHasComponentsOrRowSpan(rowIndex)
  183. && !explicitRowRatios.contains(rowIndex);
  184. }
  185. /**
  186. * Checks if it is ok to hide (or ignore) the given column.
  187. *
  188. * @param columnIndex
  189. * the column to check
  190. * @return true, if the column should be interpreted as non-existant (hides
  191. * extra spacing)
  192. */
  193. private boolean hiddenEmptyColumn(int columnIndex) {
  194. return hideEmptyRowsAndColumns
  195. && !colHasComponentsOrColSpan(columnIndex)
  196. && !explicitColRatios.contains(columnIndex);
  197. }
  198. private int calcRowUsedSpace() {
  199. int usedSpace = minRowHeights[0];
  200. int verticalSpacing = getVerticalSpacing();
  201. for (int i = 1; i < minRowHeights.length; i++) {
  202. if (minRowHeights[i] > 0 || !hiddenEmptyRow(i)) {
  203. usedSpace += verticalSpacing + minRowHeights[i];
  204. }
  205. }
  206. return usedSpace;
  207. }
  208. void expandColumns() {
  209. if (!isUndefinedWidth()) {
  210. int usedSpace = calcColumnUsedSpace();
  211. float[] actualExpandRatio = calcColumnExpandRatio();
  212. // Round down to avoid problems with fractions (100.1px available ->
  213. // can use 100, not 101)
  214. int availableSpace = (int) LayoutManager.get(client)
  215. .getInnerWidthDouble(getElement());
  216. int excessSpace = availableSpace - usedSpace;
  217. int distributed = 0;
  218. if (excessSpace > 0) {
  219. float expandRatioSum = 0;
  220. for (int i = 0; i < columnWidths.length; i++) {
  221. expandRatioSum += actualExpandRatio[i];
  222. }
  223. for (int i = 0; i < columnWidths.length; i++) {
  224. int ew = (int) (excessSpace * actualExpandRatio[i]
  225. / expandRatioSum);
  226. columnWidths[i] = minColumnWidths[i] + ew;
  227. distributed += ew;
  228. }
  229. excessSpace -= distributed;
  230. int c = 0;
  231. while (excessSpace > 0) {
  232. columnWidths[c % columnWidths.length]++;
  233. excessSpace--;
  234. c++;
  235. }
  236. }
  237. }
  238. }
  239. /**
  240. * Calculates column expand ratio.
  241. */
  242. private float[] calcColumnExpandRatio() {
  243. float[] actualExpandRatio = new float[minColumnWidths.length];
  244. for (int i = 0; i < minColumnWidths.length; i++) {
  245. if (!hiddenEmptyColumn(i)) {
  246. actualExpandRatio[i] = colExpandRatioArray[i];
  247. } else {
  248. actualExpandRatio[i] = 0;
  249. }
  250. }
  251. return actualExpandRatio;
  252. }
  253. /**
  254. * Calculates column used space
  255. */
  256. private int calcColumnUsedSpace() {
  257. int usedSpace = minColumnWidths[0];
  258. int horizontalSpacing = getHorizontalSpacing();
  259. for (int i = 1; i < minColumnWidths.length; i++) {
  260. if (minColumnWidths[i] > 0 || !hiddenEmptyColumn(i)) {
  261. usedSpace += horizontalSpacing + minColumnWidths[i];
  262. }
  263. }
  264. return usedSpace;
  265. }
  266. private boolean rowHasComponentsOrRowSpan(int i) {
  267. for (Cell cell : widgetToCell.values()) {
  268. if (cell.row == i) {
  269. return true;
  270. }
  271. }
  272. for (SpanList l : rowSpans) {
  273. for (Cell cell : l.cells) {
  274. if (cell.row <= i && i < cell.row + cell.rowspan) {
  275. return true;
  276. }
  277. }
  278. }
  279. return false;
  280. }
  281. private boolean colHasComponentsOrColSpan(int i) {
  282. for (Cell cell : widgetToCell.values()) {
  283. if (cell.col == i) {
  284. return true;
  285. }
  286. }
  287. for (SpanList l : colSpans) {
  288. for (Cell cell : l.cells) {
  289. if (cell.col <= i && i < cell.col + cell.colspan) {
  290. return true;
  291. }
  292. }
  293. }
  294. return false;
  295. }
  296. /** For internal use only. May be removed or replaced in the future. */
  297. public void updateHeight() {
  298. // Detect minimum heights & calculate spans
  299. detectRowHeights();
  300. // Expand
  301. expandRows();
  302. // Position
  303. layoutCellsVertically();
  304. }
  305. /** For internal use only. May be removed or replaced in the future. */
  306. public void updateWidth() {
  307. // Detect widths & calculate spans
  308. detectColWidths();
  309. // Expand
  310. expandColumns();
  311. // Position
  312. layoutCellsHorizontally();
  313. }
  314. void layoutCellsVertically() {
  315. int verticalSpacing = getVerticalSpacing();
  316. LayoutManager layoutManager = LayoutManager.get(client);
  317. Element element = getElement();
  318. int paddingTop = layoutManager.getPaddingTop(element);
  319. int paddingBottom = layoutManager.getPaddingBottom(element);
  320. int y = paddingTop;
  321. for (int column = 0; column < cells.length; column++) {
  322. y = paddingTop + 1 - 1; // Ensure IE10 does not optimize this out by
  323. // adding something to evaluate on the RHS
  324. // #11303
  325. for (int row = 0; row < cells[column].length; row++) {
  326. Cell cell = cells[column][row];
  327. if (cell != null) {
  328. int reservedMargin;
  329. if (cell.rowspan + row >= cells[column].length) {
  330. // Make room for layout padding for cells reaching the
  331. // bottom of the layout
  332. reservedMargin = paddingBottom;
  333. } else {
  334. reservedMargin = 0;
  335. }
  336. cell.layoutVertically(y, reservedMargin);
  337. }
  338. if (!hideEmptyRowsAndColumns || rowHasComponentsOrRowSpan(row)
  339. || rowHeights[row] > 0) {
  340. y += rowHeights[row] + verticalSpacing;
  341. }
  342. }
  343. }
  344. if (isUndefinedHeight()) {
  345. int outerHeight = y - verticalSpacing
  346. + layoutManager.getPaddingBottom(element)
  347. + layoutManager.getBorderHeight(element);
  348. element.getStyle().setHeight(outerHeight, Unit.PX);
  349. getConnector().getLayoutManager().reportOuterHeight(getConnector(),
  350. outerHeight);
  351. }
  352. }
  353. void layoutCellsHorizontally() {
  354. LayoutManager layoutManager = LayoutManager.get(client);
  355. Element element = getElement();
  356. int x = layoutManager.getPaddingLeft(element);
  357. int paddingRight = layoutManager.getPaddingRight(element);
  358. int horizontalSpacing = getHorizontalSpacing();
  359. for (int i = 0; i < cells.length; i++) {
  360. for (int j = 0; j < cells[i].length; j++) {
  361. Cell cell = cells[i][j];
  362. if (cell != null) {
  363. int reservedMargin;
  364. // Make room for layout padding for cells reaching the
  365. // right edge of the layout
  366. if (i + cell.colspan >= cells.length) {
  367. reservedMargin = paddingRight;
  368. } else {
  369. reservedMargin = 0;
  370. }
  371. cell.layoutHorizontally(x, reservedMargin);
  372. }
  373. }
  374. if (!hideEmptyRowsAndColumns || colHasComponentsOrColSpan(i)
  375. || columnWidths[i] > 0) {
  376. x += columnWidths[i] + horizontalSpacing;
  377. }
  378. }
  379. if (isUndefinedWidth()) {
  380. int outerWidth = x - horizontalSpacing
  381. + layoutManager.getPaddingRight(element)
  382. + layoutManager.getBorderWidth(element);
  383. element.getStyle().setWidth(outerWidth, Unit.PX);
  384. getConnector().getLayoutManager().reportOuterWidth(getConnector(),
  385. outerWidth);
  386. }
  387. }
  388. private boolean isUndefinedHeight() {
  389. return getConnector().isUndefinedHeight();
  390. }
  391. private boolean isUndefinedWidth() {
  392. return getConnector().isUndefinedWidth();
  393. }
  394. private void detectRowHeights() {
  395. for (int i = 0; i < rowHeights.length; i++) {
  396. rowHeights[i] = 0;
  397. }
  398. // collect min rowheight from non-rowspanned cells
  399. for (int i = 0; i < cells.length; i++) {
  400. for (int j = 0; j < cells[i].length; j++) {
  401. Cell cell = cells[i][j];
  402. if (cell != null) {
  403. if (cell.rowspan == 1) {
  404. if (!cell.hasRelativeHeight()
  405. && rowHeights[j] < cell.getHeight()) {
  406. rowHeights[j] = cell.getHeight();
  407. }
  408. } else {
  409. storeRowSpannedCell(cell);
  410. }
  411. }
  412. }
  413. }
  414. distributeRowSpanHeights();
  415. minRowHeights = cloneArray(rowHeights);
  416. }
  417. private void detectColWidths() {
  418. // collect min colwidths from non-colspanned cells
  419. for (int i = 0; i < columnWidths.length; i++) {
  420. columnWidths[i] = 0;
  421. }
  422. for (int i = 0; i < cells.length; i++) {
  423. for (int j = 0; j < cells[i].length; j++) {
  424. Cell cell = cells[i][j];
  425. if (cell != null) {
  426. if (cell.colspan == 1) {
  427. if (!cell.hasRelativeWidth()
  428. && columnWidths[i] < cell.getWidth()) {
  429. columnWidths[i] = cell.getWidth();
  430. }
  431. } else {
  432. storeColSpannedCell(cell);
  433. }
  434. }
  435. }
  436. }
  437. distributeColSpanWidths();
  438. minColumnWidths = cloneArray(columnWidths);
  439. }
  440. private void storeRowSpannedCell(Cell cell) {
  441. SpanList l = null;
  442. for (SpanList list : rowSpans) {
  443. if (list.span < cell.rowspan) {
  444. continue;
  445. } else {
  446. // insert before this
  447. l = list;
  448. break;
  449. }
  450. }
  451. if (l == null) {
  452. l = new SpanList(cell.rowspan);
  453. rowSpans.add(l);
  454. } else if (l.span != cell.rowspan) {
  455. SpanList newL = new SpanList(cell.rowspan);
  456. rowSpans.add(rowSpans.indexOf(l), newL);
  457. l = newL;
  458. }
  459. l.cells.add(cell);
  460. }
  461. /**
  462. * Iterates colspanned cells, ensures cols have enough space to accommodate
  463. * them
  464. */
  465. void distributeColSpanWidths() {
  466. for (SpanList list : colSpans) {
  467. for (Cell cell : list.cells) {
  468. // cells with relative content may return non 0 here if on
  469. // subsequent renders
  470. int width = cell.hasRelativeWidth() ? 0 : cell.getWidth();
  471. distributeSpanSize(columnWidths, cell.col, cell.colspan,
  472. getHorizontalSpacing(), width, colExpandRatioArray);
  473. }
  474. }
  475. }
  476. /**
  477. * Iterates rowspanned cells, ensures rows have enough space to accommodate
  478. * them
  479. */
  480. private void distributeRowSpanHeights() {
  481. for (SpanList list : rowSpans) {
  482. for (Cell cell : list.cells) {
  483. // cells with relative content may return non 0 here if on
  484. // subsequent renders
  485. int height = cell.hasRelativeHeight() ? 0 : cell.getHeight();
  486. distributeSpanSize(rowHeights, cell.row, cell.rowspan,
  487. getVerticalSpacing(), height, rowExpandRatioArray);
  488. }
  489. }
  490. }
  491. private static void distributeSpanSize(int[] dimensions, int spanStartIndex,
  492. int spanSize, int spacingSize, int size, float[] expansionRatios) {
  493. int allocated = dimensions[spanStartIndex];
  494. for (int i = 1; i < spanSize; i++) {
  495. allocated += spacingSize + dimensions[spanStartIndex + i];
  496. }
  497. if (allocated < size) {
  498. // dimensions needs to be expanded due spanned cell
  499. int neededExtraSpace = size - allocated;
  500. int allocatedExtraSpace = 0;
  501. // Divide space according to expansion ratios if any span has a
  502. // ratio
  503. int totalExpansion = 0;
  504. for (int i = 0; i < spanSize; i++) {
  505. int itemIndex = spanStartIndex + i;
  506. totalExpansion += expansionRatios[itemIndex];
  507. }
  508. for (int i = 0; i < spanSize; i++) {
  509. int itemIndex = spanStartIndex + i;
  510. int expansion;
  511. if (totalExpansion == 0) {
  512. // Divide equally among all cells if there are no
  513. // expansion ratios
  514. expansion = neededExtraSpace / spanSize;
  515. } else {
  516. expansion = (int) (neededExtraSpace
  517. * expansionRatios[itemIndex] / totalExpansion);
  518. }
  519. dimensions[itemIndex] += expansion;
  520. allocatedExtraSpace += expansion;
  521. }
  522. // We might still miss a couple of pixels because of
  523. // rounding errors...
  524. if (neededExtraSpace > allocatedExtraSpace) {
  525. for (int i = 0; i < spanSize; i++) {
  526. // Add one pixel to every cell until we have
  527. // compensated for any rounding error
  528. int itemIndex = spanStartIndex + i;
  529. dimensions[itemIndex] += 1;
  530. allocatedExtraSpace += 1;
  531. if (neededExtraSpace == allocatedExtraSpace) {
  532. break;
  533. }
  534. }
  535. }
  536. }
  537. }
  538. private LinkedList<SpanList> colSpans = new LinkedList<>();
  539. private LinkedList<SpanList> rowSpans = new LinkedList<>();
  540. private class SpanList {
  541. final int span;
  542. List<Cell> cells = new LinkedList<>();
  543. public SpanList(int span) {
  544. this.span = span;
  545. }
  546. }
  547. void storeColSpannedCell(Cell cell) {
  548. SpanList l = null;
  549. for (SpanList list : colSpans) {
  550. if (list.span < cell.colspan) {
  551. continue;
  552. } else {
  553. // insert before this
  554. l = list;
  555. break;
  556. }
  557. }
  558. if (l == null) {
  559. l = new SpanList(cell.colspan);
  560. colSpans.add(l);
  561. } else if (l.span != cell.colspan) {
  562. SpanList newL = new SpanList(cell.colspan);
  563. colSpans.add(colSpans.indexOf(l), newL);
  564. l = newL;
  565. }
  566. l.cells.add(cell);
  567. }
  568. Cell[][] cells;
  569. private int rowCount;
  570. private int columnCount;
  571. /**
  572. * Private helper class.
  573. */
  574. /** For internal use only. May be removed or replaced in the future. */
  575. public class Cell {
  576. public Cell(int row, int col) {
  577. this.row = row;
  578. this.col = col;
  579. }
  580. public boolean hasRelativeHeight() {
  581. if (slot != null) {
  582. return slot.getChild().isRelativeHeight();
  583. } else {
  584. return true;
  585. }
  586. }
  587. /**
  588. * @return total of spanned cols
  589. */
  590. private int getAvailableWidth() {
  591. int width = columnWidths[col];
  592. for (int i = 1; i < colspan; i++) {
  593. width += getHorizontalSpacing() + columnWidths[col + i];
  594. }
  595. return width;
  596. }
  597. /**
  598. * @return total of spanned rows
  599. */
  600. private int getAvailableHeight() {
  601. int height = rowHeights[row];
  602. for (int i = 1; i < rowspan; i++) {
  603. height += getVerticalSpacing() + rowHeights[row + i];
  604. }
  605. return height;
  606. }
  607. public void layoutHorizontally(int x, int marginRight) {
  608. if (slot != null) {
  609. slot.positionHorizontally(x, getAvailableWidth(), marginRight);
  610. }
  611. }
  612. public void layoutVertically(int y, int marginBottom) {
  613. if (slot != null) {
  614. slot.positionVertically(y, getAvailableHeight(), marginBottom);
  615. }
  616. }
  617. public int getWidth() {
  618. if (slot != null) {
  619. return slot.getUsedWidth();
  620. } else {
  621. return 0;
  622. }
  623. }
  624. public int getHeight() {
  625. if (slot != null) {
  626. return slot.getUsedHeight();
  627. } else {
  628. return 0;
  629. }
  630. }
  631. protected boolean hasRelativeWidth() {
  632. if (slot != null) {
  633. return slot.getChild().isRelativeWidth();
  634. } else {
  635. return true;
  636. }
  637. }
  638. private int row;
  639. private int col;
  640. int colspan = 1;
  641. int rowspan = 1;
  642. private AlignmentInfo alignment;
  643. /** For internal use only. May be removed or replaced in the future. */
  644. public ComponentConnectorLayoutSlot slot;
  645. public void updateCell(ChildComponentData childComponentData) {
  646. if (row != childComponentData.row1
  647. || col != childComponentData.column1) {
  648. // cell has been moved, update matrix
  649. if (col < cells.length && cells.length != 0
  650. && row < cells[0].length && cells[col][row] == this) {
  651. // Remove old reference if still relevant
  652. cells[col][row] = null;
  653. }
  654. row = childComponentData.row1;
  655. col = childComponentData.column1;
  656. cells[col][row] = this;
  657. }
  658. // Set cell width
  659. colspan = 1 + childComponentData.column2
  660. - childComponentData.column1;
  661. // Set cell height
  662. rowspan = 1 + childComponentData.row2 - childComponentData.row1;
  663. setAlignment(new AlignmentInfo(childComponentData.alignment));
  664. }
  665. public void setComponent(ComponentConnector component,
  666. List<ComponentConnector> ordering) {
  667. if (slot == null || slot.getChild() != component) {
  668. slot = new ComponentConnectorLayoutSlot(CLASSNAME, component,
  669. getConnector());
  670. slot.setAlignment(alignment);
  671. if (component.isRelativeWidth()) {
  672. slot.getWrapperElement().getStyle().setWidth(100, Unit.PCT);
  673. }
  674. Element slotWrapper = slot.getWrapperElement();
  675. int childIndex = ordering.indexOf(component);
  676. // insert new slot by proper index
  677. // do not break default focus order
  678. com.google.gwt.user.client.Element element = getElement();
  679. if (childIndex == ordering.size()) {
  680. element.appendChild(slotWrapper);
  681. } else if (childIndex == 0) {
  682. element.insertAfter(slotWrapper, spacingMeasureElement);
  683. } else {
  684. // here we use childIndex - 1 + 1(spacingMeasureElement)
  685. Element previousSlot = (Element) element
  686. .getChild(childIndex);
  687. element.insertAfter(slotWrapper, previousSlot);
  688. }
  689. Widget widget = component.getWidget();
  690. insert(widget, slotWrapper, getWidgetCount(), false);
  691. Cell oldCell = widgetToCell.put(widget, this);
  692. if (oldCell != null) {
  693. oldCell.slot.getWrapperElement().removeFromParent();
  694. oldCell.slot = null;
  695. }
  696. }
  697. }
  698. public void setAlignment(AlignmentInfo alignmentInfo) {
  699. alignment = alignmentInfo;
  700. if (slot != null) {
  701. slot.setAlignment(alignmentInfo);
  702. }
  703. }
  704. }
  705. /** For internal use only. May be removed or replaced in the future. */
  706. public Cell getCell(int row, int col) {
  707. return cells[col][row];
  708. }
  709. private Element getCellElement(int row, int col) {
  710. if (row < 0 || row >= getRowCount() || col < 0
  711. || col >= getColumnCount()) {
  712. return null;
  713. }
  714. Cell cell = cells[col][row];
  715. if (cell == null || cell.slot == null) {
  716. return null;
  717. }
  718. return cell.slot.getWrapperElement();
  719. }
  720. /**
  721. * Creates a new Cell with the given coordinates.
  722. * <p>
  723. * For internal use only. May be removed or replaced in the future.
  724. *
  725. * @param row
  726. * @param col
  727. * @return
  728. */
  729. public Cell createNewCell(int row, int col) {
  730. Cell cell = new Cell(row, col);
  731. cells[col][row] = cell;
  732. return cell;
  733. }
  734. /**
  735. * Returns the deepest nested child component which contains "element". The
  736. * child component is also returned if "element" is part of its caption.
  737. * <p>
  738. * For internal use only. May be removed or replaced in the future.
  739. *
  740. * @param element
  741. * An element that is a nested sub element of the root element in
  742. * this layout
  743. * @return The Paintable which the element is a part of. Null if the element
  744. * belongs to the layout and not to a child.
  745. * @deprecated As of 7.2, call or override {@link #getComponent(Element)}
  746. * instead
  747. */
  748. @Deprecated
  749. public ComponentConnector getComponent(
  750. com.google.gwt.user.client.Element element) {
  751. return Util.getConnectorForElement(client, this, element);
  752. }
  753. /**
  754. * Returns the deepest nested child component which contains "element". The
  755. * child component is also returned if "element" is part of its caption.
  756. * <p>
  757. * For internal use only. May be removed or replaced in the future.
  758. *
  759. * @param element
  760. * An element that is a nested sub element of the root element in
  761. * this layout
  762. * @return The Paintable which the element is a part of. Null if the element
  763. * belongs to the layout and not to a child.
  764. *
  765. * @since 7.2
  766. */
  767. public ComponentConnector getComponent(Element element) {
  768. return getComponent(DOM.asOld(element));
  769. }
  770. /** For internal use only. May be removed or replaced in the future. */
  771. public void setCaption(Widget widget, VCaption caption) {
  772. VLayoutSlot slot = widgetToCell.get(widget).slot;
  773. if (caption != null) {
  774. // Logical attach.
  775. getChildren().add(caption);
  776. }
  777. // Physical attach if not null, also removes old caption
  778. slot.setCaption(caption);
  779. if (caption != null) {
  780. // Adopt.
  781. adopt(caption);
  782. }
  783. }
  784. private void togglePrefixedStyleName(String name, boolean enabled) {
  785. if (enabled) {
  786. addStyleDependentName(name);
  787. } else {
  788. removeStyleDependentName(name);
  789. }
  790. }
  791. /** For internal use only. May be removed or replaced in the future. */
  792. public void updateMarginStyleNames(MarginInfo marginInfo) {
  793. togglePrefixedStyleName("margin-top", marginInfo.hasTop());
  794. togglePrefixedStyleName("margin-right", marginInfo.hasRight());
  795. togglePrefixedStyleName("margin-bottom", marginInfo.hasBottom());
  796. togglePrefixedStyleName("margin-left", marginInfo.hasLeft());
  797. }
  798. /** For internal use only. May be removed or replaced in the future. */
  799. public void updateSpacingStyleName(boolean spacingEnabled) {
  800. String styleName = getStylePrimaryName();
  801. if (spacingEnabled) {
  802. spacingMeasureElement.addClassName(styleName + "-spacing-on");
  803. spacingMeasureElement.removeClassName(styleName + "-spacing-off");
  804. } else {
  805. spacingMeasureElement.removeClassName(styleName + "-spacing-on");
  806. spacingMeasureElement.addClassName(styleName + "-spacing-off");
  807. }
  808. }
  809. public void setSize(int rows, int cols) {
  810. rowCount = rows;
  811. columnCount = cols;
  812. if (cells == null) {
  813. cells = new Cell[cols][rows];
  814. } else if (cells.length != cols || cells[0].length != rows) {
  815. Cell[][] newCells = new Cell[cols][rows];
  816. for (int i = 0; i < cells.length; i++) {
  817. for (int j = 0; j < cells[i].length; j++) {
  818. if (i < cols && j < rows) {
  819. newCells[i][j] = cells[i][j];
  820. }
  821. }
  822. }
  823. cells = newCells;
  824. }
  825. }
  826. private int getRowCount() {
  827. return rowCount;
  828. }
  829. private int getColumnCount() {
  830. return columnCount;
  831. }
  832. @Override
  833. public boolean remove(Widget w) {
  834. boolean removed = super.remove(w);
  835. if (removed) {
  836. Cell cell = widgetToCell.remove(w);
  837. if (cell != null) {
  838. cell.slot.setCaption(null);
  839. cell.slot.getWrapperElement().removeFromParent();
  840. cell.slot = null;
  841. Style style = w.getElement().getStyle();
  842. style.clearTop();
  843. style.clearLeft();
  844. style.clearPosition();
  845. if (cells.length < cell.col && cells.length != 0
  846. && cells[0].length < cell.row
  847. && cells[cell.col][cell.row] == cell) {
  848. cells[cell.col][cell.row] = null;
  849. }
  850. }
  851. }
  852. return removed;
  853. }
  854. }