-
Notifications
You must be signed in to change notification settings - Fork 436
/
GridPane.java
2835 lines (2564 loc) · 116 KB
/
GridPane.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (c) 2011, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.scene.layout;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import com.sun.javafx.collections.TrackableObservableList;
import javafx.css.StyleableBooleanProperty;
import javafx.css.StyleableDoubleProperty;
import javafx.css.StyleableObjectProperty;
import javafx.css.CssMetaData;
import javafx.css.converter.BooleanConverter;
import javafx.css.converter.EnumConverter;
import javafx.css.converter.SizeConverter;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import javafx.beans.Observable;
import javafx.css.Styleable;
import javafx.css.StyleableProperty;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.util.Callback;
/**
* GridPane lays out its children within a flexible grid of rows and columns.
* If a border and/or padding is set, then its content will be laid out within
* those insets.
* <p>
* A child may be placed anywhere within the grid and may span multiple
* rows/columns. Children may freely overlap within rows/columns and their
* stacking order will be defined by the order of the gridpane's children list
* (0th node in back, last node in front).
* <p>
* GridPane may be styled with backgrounds and borders using CSS. See
* {@link javafx.scene.layout.Region Region} superclass for details.</p>
*
* <h2>Grid Constraints</h2>
* <p>
* A child's placement within the grid is defined by it's layout constraints:
* </p>
*
* <table border="1">
* <caption>Grid Constraint Table</caption>
* <tr><th scope="col">Constraint</th><th scope="col">Type</th><th scope="col">Description</th></tr>
* <tr><th scope="row">columnIndex</th><td>integer</td><td>column where child's layout area starts.</td></tr>
* <tr><th scope="row">rowIndex</th><td>integer</td><td>row where child's layout area starts.</td></tr>
* <tr><th scope="row">columnSpan</th><td>integer</td><td>the number of columns the child's layout area spans horizontally.</td></tr>
* <tr><th scope="row">rowSpan</th><td>integer</td><td>the number of rows the child's layout area spans vertically.</td></tr>
* </table>
* <p>
* If the row/column indices are not explicitly set, then the child will be placed
* in the first row/column. If row/column spans are not set, they will default to 1.
* A child's placement constraints can be changed dynamically and the gridpane
* will update accordingly.
* <p>
* The total number of rows/columns does not need to be specified up front as the
* gridpane will automatically expand/contract the grid to accommodate the content.
* <p>
* To use the GridPane, an application needs to set the layout constraints on
* the children and add those children to the gridpane instance.
* Constraints are set on the children using static setter methods on the GridPane
* class:
* <pre><code> GridPane gridpane = new GridPane();
*
* // Set one constraint at a time...
* // Places the button at the first row and second column
* Button button = new Button();
* <b>GridPane.setRowIndex(button, 0);
* GridPane.setColumnIndex(button, 1);</b>
*
* // or convenience methods set more than one constraint at once...
* Label label = new Label();
* <b>GridPane.setConstraints(label, 2, 0);</b> // column=2 row=0
*
* // don't forget to add children to gridpane
* <b>gridpane.getChildren().addAll(button, label);</b>
* </code></pre>
*
* Applications may also use convenience methods which combine the steps of
* setting the constraints and adding the children:
* <pre><code>
* GridPane gridpane = new GridPane();
* <b>gridpane.add(new Button(), 1, 0);</b> // column=1 row=0
* <b>gridpane.add(new Label(), 2, 0);</b> // column=2 row=0
* </code></pre>
*
*
* <h2>Row/Column Sizing</h2>
*
* By default, rows and columns will be sized to fit their content;
* a column will be wide enough to accommodate the widest child, a
* row tall enough to fit the tallest child. However, if an application needs
* to explicitly control the size of rows or columns, it may do so by adding
* RowConstraints and ColumnConstraints objects to specify those metrics.
* For example, to create a grid with two fixed-width columns:
* <pre><code>
* GridPane gridpane = new GridPane();
* <b>gridpane.getColumnConstraints().add(new ColumnConstraints(100));</b> // column 0 is 100 wide
* <b>gridpane.getColumnConstraints().add(new ColumnConstraints(200));</b> // column 1 is 200 wide
* </code></pre>
* By default the gridpane will resize rows/columns to their preferred sizes (either
* computed from content or fixed), even if the gridpane is resized larger than
* its preferred size. If an application needs a particular row or column to
* grow if there is extra space, it may set its grow priority on the RowConstraints
* or ColumnConstraints object. For example:
* <pre><code>
* GridPane gridpane = new GridPane();
* ColumnConstraints column1 = new ColumnConstraints(100,100,Double.MAX_VALUE);
* <b>column1.setHgrow(Priority.ALWAYS);</b>
* ColumnConstraints column2 = new ColumnConstraints(100);
* gridpane.getColumnConstraints().addAll(column1, column2); // first column gets any extra width
* </code></pre>
* <p>
* Note: Nodes spanning multiple rows/columns will be also size to the preferred sizes.
* The affected rows/columns are resized by the following priority: grow priorities, last row.
* This is with respect to row/column constraints.
*
* <h2>Percentage Sizing</h2>
*
* Alternatively, RowConstraints and ColumnConstraints allow the size to be specified
* as a percentage of gridpane's available space:
* <pre><code>
* GridPane gridpane = new GridPane();
* ColumnConstraints column1 = new ColumnConstraints();
* <b>column1.setPercentWidth(50);</b>
* ColumnConstraints column2 = new ColumnConstraints();
* <b>column2.setPercentWidth(50);</b>
* gridpane.getColumnConstraints().addAll(column1, column2); // each get 50% of width
* </code></pre>
* If a percentage value is set on a row/column, then that value takes precedent and the
* row/column's min, pref, max, and grow constraints will be ignored.
* <p>
* Note that if the sum of the widthPercent (or heightPercent) values total greater than 100, the values will
* be treated as weights. e.g. if 3 columns are each given a widthPercent of 50,
* then each will be allocated 1/3 of the gridpane's available width (50/(50+50+50)).
*
* <h2>Mixing Size Types</h2>
*
* An application may freely mix the size-types of rows/columns (computed from content, fixed,
* or percentage). The percentage rows/columns will always be allocated space first
* based on their percentage of the gridpane's available space (size minus insets and gaps).
* The remaining space will be allocated to rows/columns given their minimum, preferred,
* and maximum sizes and grow priorities.
*
* <h2>Resizable Range</h2>
* <p>
* A gridpane's parent will resize the gridpane within the gridpane's resizable range
* during layout. By default the gridpane computes this range based on its content
* and row/column constraints as outlined in the table below.
* </p>
*
* <table border="1">
* <caption>GridPane Resize Table</caption>
* <tr><td></td><th scope="col">width</th><th scope="col">height</th></tr>
* <tr><th scope="row">minimum</th>
* <td>left/right insets plus the sum of each column's min width.</td>
* <td>top/bottom insets plus the sum of each row's min height.</td></tr>
* <tr><th scope="row">preferred</th>
* <td>left/right insets plus the sum of each column's pref width.</td>
* <td>top/bottom insets plus the sum of each row's pref height.</td></tr>
* <tr><th scope="row">maximum</th>
* <td>Double.MAX_VALUE</td><td>Double.MAX_VALUE</td></tr>
* </table>
* <p>
* A gridpane's unbounded maximum width and height are an indication to the parent that
* it may be resized beyond its preferred size to fill whatever space is assigned
* to it.
* <p>
* GridPane provides properties for setting the size range directly. These
* properties default to the sentinel value USE_COMPUTED_SIZE, however the
* application may set them to other values as needed:
* <pre><code> <b>gridpane.setPrefSize(300, 300);</b>
* // never size the gridpane larger than its preferred size:
* <b>gridpane.setMaxSize(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE);</b>
* </code></pre>
* Applications may restore the computed values by setting these properties back
* to USE_COMPUTED_SIZE.
* <p>
* GridPane does not clip its content by default, so it is possible that children's
* bounds may extend outside its own bounds if a child's min size prevents it from
* being fit within it space.</p>
*
* <h2>Optional Layout Constraints</h2>
*
* <p>
* An application may set additional constraints on children to customize how the
* child is sized and positioned within the layout area established by it's row/column
* indices/spans:
* </p>
*
* <table border="1">
* <caption>GridPane Constraint Table</caption>
* <tr><th scope="col">Constraint</th><th scope="col">Type</th><th scope="col">Description</th></tr>
* <tr><th scope="row">halignment</th><td>javafx.geometry.HPos</td><td>The horizontal alignment of the child within its layout area.</td></tr>
* <tr><th scope="row">valignment</th><td>javafx.geometry.VPos</td><td>The vertical alignment of the child within its layout area.</td></tr>
* <tr><th scope="row">hgrow</th><td>javafx.scene.layout.Priority</td><td>The horizontal grow priority of the child.</td></tr>
* <tr><th scope="row">vgrow</th><td>javafx.scene.layout.Priority</td><td>The vertical grow priority of the child.</td></tr>
* <tr><th scope="row">margin</th><td>javafx.geometry.Insets</td><td>Margin space around the outside of the child.</td></tr>
* </table>
* <p>
* By default the alignment of a child within its layout area is defined by the
* alignment set for the row and column. If an individual alignment constraint is
* set on a child, that alignment will override the row/column alignment only
* for that child. Alignment of other children in the same row or column will
* not be affected.
* <p>
* Grow priorities, on the other hand, can only be applied to entire rows or columns.
* Therefore, if a grow priority constraint is set on a single child, it will be
* used to compute the default grow priority of the encompassing row/column. If
* a grow priority is set directly on a RowConstraint or ColumnConstraint object,
* it will override the value computed from content.
*
*
* @since JavaFX 2.0
*/
public class GridPane extends Pane {
/**
* Sentinel value which may be set on a child's row/column span constraint to
* indicate that it should span the remaining rows/columns.
*/
public static final int REMAINING = Integer.MAX_VALUE;
/* ******************************************************************
* BEGIN static methods
********************************************************************/
private static final String MARGIN_CONSTRAINT = "gridpane-margin";
private static final String HALIGNMENT_CONSTRAINT = "gridpane-halignment";
private static final String VALIGNMENT_CONSTRAINT = "gridpane-valignment";
private static final String HGROW_CONSTRAINT = "gridpane-hgrow";
private static final String VGROW_CONSTRAINT = "gridpane-vgrow";
private static final String ROW_INDEX_CONSTRAINT = "gridpane-row";
private static final String COLUMN_INDEX_CONSTRAINT = "gridpane-column";
private static final String ROW_SPAN_CONSTRAINT = "gridpane-row-span";
private static final String COLUMN_SPAN_CONSTRAINT = "gridpane-column-span";
private static final String FILL_WIDTH_CONSTRAINT = "gridpane-fill-width";
private static final String FILL_HEIGHT_CONSTRAINT = "gridpane-fill-height";
/**
* Sets the row index for the child when contained by a gridpane
* so that it will be positioned starting in that row of the gridpane.
* If a gridpane child has no row index set, it will be positioned in the
* first row.
* Setting the value to null will remove the constraint.
* @param child the child node of a gridpane
* @param value the row index of the child
*/
public static void setRowIndex(Node child, Integer value) {
if (value != null && value < 0) {
throw new IllegalArgumentException("rowIndex must be greater or equal to 0, but was "+value);
}
setConstraint(child, ROW_INDEX_CONSTRAINT, value);
}
/**
* Returns the child's row index constraint if set.
* @param child the child node of a gridpane
* @return the row index for the child or null if no row index was set
*/
public static Integer getRowIndex(Node child) {
return (Integer)getConstraint(child, ROW_INDEX_CONSTRAINT);
}
/**
* Sets the column index for the child when contained by a gridpane
* so that it will be positioned starting in that column of the gridpane.
* If a gridpane child has no column index set, it will be positioned in
* the first column.
* Setting the value to null will remove the constraint.
* @param child the child node of a gridpane
* @param value the column index of the child
*/
public static void setColumnIndex(Node child, Integer value) {
if (value != null && value < 0) {
throw new IllegalArgumentException("columnIndex must be greater or equal to 0, but was "+value);
}
setConstraint(child, COLUMN_INDEX_CONSTRAINT, value);
}
/**
* Returns the child's column index constraint if set.
* @param child the child node of a gridpane
* @return the column index for the child or null if no column index was set
*/
public static Integer getColumnIndex(Node child) {
return (Integer)getConstraint(child, COLUMN_INDEX_CONSTRAINT);
}
/**
* Sets the row span for the child when contained by a gridpane
* so that it will span that number of rows vertically. This may be
* set to REMAINING, which will cause the span to extend across all the remaining
* rows.
* <p>
* If a gridpane child has no row span set, it will default to spanning one row.
* Setting the value to null will remove the constraint.
* @param child the child node of a gridpane
* @param value the row span of the child
*/
public static void setRowSpan(Node child, Integer value) {
if (value != null && value < 1) {
throw new IllegalArgumentException("rowSpan must be greater or equal to 1, but was "+value);
}
setConstraint(child, ROW_SPAN_CONSTRAINT, value);
}
/**
* Returns the child's row-span constraint if set.
* @param child the child node of a gridpane
* @return the row span for the child or null if no row span was set
*/
public static Integer getRowSpan(Node child) {
return (Integer)getConstraint(child, ROW_SPAN_CONSTRAINT);
}
/**
* Sets the column span for the child when contained by a gridpane
* so that it will span that number of columns horizontally. This may be
* set to REMAINING, which will cause the span to extend across all the remaining
* columns.
* <p>
* If a gridpane child has no column span set, it will default to spanning one column.
* Setting the value to null will remove the constraint.
* @param child the child node of a gridpane
* @param value the column span of the child
*/
public static void setColumnSpan(Node child, Integer value) {
if (value != null && value < 1) {
throw new IllegalArgumentException("columnSpan must be greater or equal to 1, but was "+value);
}
setConstraint(child, COLUMN_SPAN_CONSTRAINT, value);
}
/**
* Returns the child's column-span constraint if set.
* @param child the child node of a gridpane
* @return the column span for the child or null if no column span was set
*/
public static Integer getColumnSpan(Node child) {
return (Integer)getConstraint(child, COLUMN_SPAN_CONSTRAINT);
}
/**
* Sets the margin for the child when contained by a gridpane.
* If set, the gridpane will lay it out with the margin space around it.
* Setting the value to null will remove the constraint.
* @param child the child node of a gridpane
* @param value the margin of space around the child
*/
public static void setMargin(Node child, Insets value) {
setConstraint(child, MARGIN_CONSTRAINT, value);
}
/**
* Returns the child's margin constraint if set.
* @param child the child node of a gridpane
* @return the margin for the child or null if no margin was set
*/
public static Insets getMargin(Node child) {
return (Insets)getConstraint(child, MARGIN_CONSTRAINT);
}
private double getBaselineComplementForChild(Node child) {
if (isNodePositionedByBaseline(child)) {
return rowMinBaselineComplement[getNodeRowIndex(child)];
}
return -1;
}
private static final Callback<Node, Insets> marginAccessor = n -> getMargin(n);
/**
* Sets the horizontal alignment for the child when contained by a gridpane.
* If set, will override the gridpane's default horizontal alignment.
* Setting the value to null will remove the constraint.
* @param child the child node of a gridpane
* @param value the hozizontal alignment for the child
*/
public static void setHalignment(Node child, HPos value) {
setConstraint(child, HALIGNMENT_CONSTRAINT, value);
}
/**
* Returns the child's halignment constraint if set.
* @param child the child node of a gridpane
* @return the horizontal alignment for the child or null if no alignment was set
*/
public static HPos getHalignment(Node child) {
return (HPos)getConstraint(child, HALIGNMENT_CONSTRAINT);
}
/**
* Sets the vertical alignment for the child when contained by a gridpane.
* If set, will override the gridpane's default vertical alignment.
* Setting the value to null will remove the constraint.
* @param child the child node of a gridpane
* @param value the vertical alignment for the child
*/
public static void setValignment(Node child, VPos value) {
setConstraint(child, VALIGNMENT_CONSTRAINT, value);
}
/**
* Returns the child's valignment constraint if set.
* @param child the child node of a gridpane
* @return the vertical alignment for the child or null if no alignment was set
*/
public static VPos getValignment(Node child) {
return (VPos)getConstraint(child, VALIGNMENT_CONSTRAINT);
}
/**
* Sets the horizontal grow priority for the child when contained by a gridpane.
* If set, the gridpane will use the priority to allocate the child additional
* horizontal space if the gridpane is resized larger than it's preferred width.
* Setting the value to null will remove the constraint.
* @param child the child of a gridpane
* @param value the horizontal grow priority for the child
*/
public static void setHgrow(Node child, Priority value) {
setConstraint(child, HGROW_CONSTRAINT, value);
}
/**
* Returns the child's hgrow constraint if set.
* @param child the child node of a gridpane
* @return the horizontal grow priority for the child or null if no priority was set
*/
public static Priority getHgrow(Node child) {
return (Priority)getConstraint(child, HGROW_CONSTRAINT);
}
/**
* Sets the vertical grow priority for the child when contained by a gridpane.
* If set, the gridpane will use the priority to allocate the child additional
* vertical space if the gridpane is resized larger than it's preferred height.
* Setting the value to null will remove the constraint.
* @param child the child of a gridpane
* @param value the vertical grow priority for the child
*/
public static void setVgrow(Node child, Priority value) {
setConstraint(child, VGROW_CONSTRAINT, value);
}
/**
* Returns the child's vgrow constraint if set.
* @param child the child node of a gridpane
* @return the vertical grow priority for the child or null if no priority was set
*/
public static Priority getVgrow(Node child) {
return (Priority)getConstraint(child, VGROW_CONSTRAINT);
}
/**
* Sets the horizontal fill policy for the child when contained by a gridpane.
* If set, the gridpane will use the policy to determine whether node
* should be expanded to fill the column or resized to its preferred width.
* Setting the value to null will remove the constraint.
* If not value is specified for the node nor for the column, the default value is true.
* @param child the child node of a gridpane
* @param value the horizontal fill policy or null for unset
* @since JavaFX 8.0
*/
public static void setFillWidth(Node child, Boolean value) {
setConstraint(child, FILL_WIDTH_CONSTRAINT, value);
}
/**
* Returns the child's horizontal fill policy if set
* @param child the child node of a gridpane
* @return the horizontal fill policy for the child or null if no policy was set
* @since JavaFX 8.0
*/
public static Boolean isFillWidth(Node child) {
return (Boolean) getConstraint(child, FILL_WIDTH_CONSTRAINT);
}
/**
* Sets the vertical fill policy for the child when contained by a gridpane.
* If set, the gridpane will use the policy to determine whether node
* should be expanded to fill the row or resized to its preferred height.
* Setting the value to null will remove the constraint.
* If not value is specified for the node nor for the row, the default value is true.
* @param child the child node of a gridpane
* @param value the vertical fill policy or null for unset
* @since JavaFX 8.0
*/
public static void setFillHeight(Node child, Boolean value) {
setConstraint(child, FILL_HEIGHT_CONSTRAINT, value);
}
/**
* Returns the child's vertical fill policy if set
* @param child the child node of a gridpane
* @return the vertical fill policy for the child or null if no policy was set
* @since JavaFX 8.0
*/
public static Boolean isFillHeight(Node child) {
return (Boolean) getConstraint(child, FILL_HEIGHT_CONSTRAINT);
}
/**
* Sets the column,row indeces for the child when contained in a gridpane.
* @param child the child node of a gridpane
* @param columnIndex the column index position for the child
* @param rowIndex the row index position for the child
*/
public static void setConstraints(Node child, int columnIndex, int rowIndex) {
setRowIndex(child, rowIndex);
setColumnIndex(child, columnIndex);
}
/**
* Sets the column, row, column-span, and row-span value for the child when
* contained in a gridpane.
* @param child the child node of a gridpane
* @param columnIndex the column index position for the child
* @param rowIndex the row index position for the child
* @param columnspan the number of columns the child should span
* @param rowspan the number of rows the child should span
*/
public static void setConstraints(Node child, int columnIndex, int rowIndex, int columnspan, int rowspan) {
setRowIndex(child, rowIndex);
setColumnIndex(child, columnIndex);
setRowSpan(child, rowspan);
setColumnSpan(child, columnspan);
}
/**
* Sets the grid position, spans, and alignment for the child when contained in a gridpane.
* @param child the child node of a gridpane
* @param columnIndex the column index position for the child
* @param rowIndex the row index position for the child
* @param columnspan the number of columns the child should span
* @param rowspan the number of rows the child should span
* @param halignment the horizontal alignment of the child
* @param valignment the vertical alignment of the child
*/
public static void setConstraints(Node child, int columnIndex, int rowIndex, int columnspan, int rowspan,
HPos halignment, VPos valignment) {
setRowIndex(child, rowIndex);
setColumnIndex(child, columnIndex);
setRowSpan(child, rowspan);
setColumnSpan(child, columnspan);
setHalignment(child, halignment);
setValignment(child, valignment);
}
/**
* Sets the grid position, spans, and alignment for the child when contained in a gridpane.
* @param child the child node of a gridpane
* @param columnIndex the column index position for the child
* @param rowIndex the row index position for the child
* @param columnspan the number of columns the child should span
* @param rowspan the number of rows the child should span
* @param halignment the horizontal alignment of the child
* @param valignment the vertical alignment of the child
* @param hgrow the horizontal grow priority of the child
* @param vgrow the vertical grow priority of the child
*/
public static void setConstraints(Node child, int columnIndex, int rowIndex, int columnspan, int rowspan,
HPos halignment, VPos valignment, Priority hgrow, Priority vgrow) {
setRowIndex(child, rowIndex);
setColumnIndex(child, columnIndex);
setRowSpan(child, rowspan);
setColumnSpan(child, columnspan);
setHalignment(child, halignment);
setValignment(child, valignment);
setHgrow(child, hgrow);
setVgrow(child, vgrow);
}
/**
* Sets the grid position, spans, alignment, grow priorities, and margin for
* the child when contained in a gridpane.
* @param child the child node of a gridpane
* @param columnIndex the column index position for the child
* @param rowIndex the row index position for the child
* @param columnspan the number of columns the child should span
* @param rowspan the number of rows the child should span
* @param halignment the horizontal alignment of the child
* @param valignment the vertical alignment of the child
* @param hgrow the horizontal grow priority of the child
* @param vgrow the vertical grow priority of the child
* @param margin the margin of space around the child
*/
public static void setConstraints(Node child, int columnIndex, int rowIndex, int columnspan, int rowspan,
HPos halignment, VPos valignment, Priority hgrow, Priority vgrow, Insets margin) {
setRowIndex(child, rowIndex);
setColumnIndex(child, columnIndex);
setRowSpan(child, rowspan);
setColumnSpan(child, columnspan);
setHalignment(child, halignment);
setValignment(child, valignment);
setHgrow(child, hgrow);
setVgrow(child, vgrow);
setMargin(child, margin);
}
/**
* Removes all gridpane constraints from the child node.
* @param child the child node
*/
public static void clearConstraints(Node child) {
setRowIndex(child, null);
setColumnIndex(child, null);
setRowSpan(child, null);
setColumnSpan(child, null);
setHalignment(child, null);
setValignment(child, null);
setHgrow(child, null);
setVgrow(child, null);
setMargin(child, null);
}
private static final Color GRID_LINE_COLOR = Color.rgb(30, 30, 30);
private static final double GRID_LINE_DASH = 3;
static void createRow(int rowIndex, int columnIndex, Node... nodes) {
for (int i = 0; i < nodes.length; i++) {
setConstraints(nodes[i], columnIndex + i, rowIndex);
}
}
static void createColumn(int columnIndex, int rowIndex, Node... nodes) {
for (int i = 0; i < nodes.length; i++) {
setConstraints(nodes[i], columnIndex, rowIndex + i);
}
}
static int getNodeRowIndex(Node node) {
Integer rowIndex = getRowIndex(node);
return rowIndex != null? rowIndex : 0;
}
private static int getNodeRowSpan(Node node) {
Integer rowspan = getRowSpan(node);
return rowspan != null? rowspan : 1;
}
static int getNodeRowEnd(Node node) {
int rowSpan = getNodeRowSpan(node);
return rowSpan != REMAINING? getNodeRowIndex(node) + rowSpan - 1 : REMAINING;
}
static int getNodeColumnIndex(Node node) {
Integer columnIndex = getColumnIndex(node);
return columnIndex != null? columnIndex : 0;
}
private static int getNodeColumnSpan(Node node) {
Integer colspan = getColumnSpan(node);
return colspan != null? colspan : 1;
}
static int getNodeColumnEnd(Node node) {
int columnSpan = getNodeColumnSpan(node);
return columnSpan != REMAINING? getNodeColumnIndex(node) + columnSpan - 1 : REMAINING;
}
private static Priority getNodeHgrow(Node node) {
Priority hgrow = getHgrow(node);
return hgrow != null? hgrow : Priority.NEVER;
}
private static Priority getNodeVgrow(Node node) {
Priority vgrow = getVgrow(node);
return vgrow != null? vgrow : Priority.NEVER;
}
private static Priority[] createPriorityArray(int length, Priority value) {
Priority[] array = new Priority[length];
Arrays.fill(array, value);
return array;
}
/* ******************************************************************
* END static methods
********************************************************************/
/**
* Creates a GridPane layout with hgap/vgap = 0 and TOP_LEFT alignment.
*/
public GridPane() {
super();
getChildren().addListener((Observable o) -> requestLayout());
}
/**
* Creates a {@code GridPane} layout with the given {@link #hgapProperty() hgap} and {@link #vgapProperty() vgap}.
*
* @param hgap the size of the horizontal gaps between columns
* @param vgap the size of the vertical gaps between rows
*
* @since 21
*/
public GridPane(double hgap, double vgap) {
this();
setHgap(hgap);
setVgap(vgap);
}
/**
* The width of the horizontal gaps between columns.
* @return the width of the horizontal gaps between columns
*/
public final DoubleProperty hgapProperty() {
if (hgap == null) {
hgap = new StyleableDoubleProperty(0) {
@Override
public void invalidated() {
requestLayout();
}
@Override
public CssMetaData<GridPane, Number> getCssMetaData() {
return StyleableProperties.HGAP;
}
@Override
public Object getBean() {
return GridPane.this;
}
@Override
public String getName() {
return "hgap";
}
};
}
return hgap;
}
private DoubleProperty hgap;
public final void setHgap(double value) { hgapProperty().set(value); }
public final double getHgap() { return hgap == null ? 0 : hgap.get(); }
/**
* The height of the vertical gaps between rows.
* @return the height of the vertical gaps between rows
*/
public final DoubleProperty vgapProperty() {
if (vgap == null) {
vgap = new StyleableDoubleProperty(0) {
@Override
public void invalidated() {
requestLayout();
}
@Override
public CssMetaData<GridPane, Number> getCssMetaData() {
return StyleableProperties.VGAP;
}
@Override
public Object getBean() {
return GridPane.this;
}
@Override
public String getName() {
return "vgap";
}
};
}
return vgap;
}
private DoubleProperty vgap;
public final void setVgap(double value) { vgapProperty().set(value); }
public final double getVgap() { return vgap == null ? 0 : vgap.get(); }
/**
* The alignment of the grid within the gridpane's width and height.
* @return the alignment of the grid within the gridpane's width and height
*/
public final ObjectProperty<Pos> alignmentProperty() {
if (alignment == null) {
alignment = new StyleableObjectProperty<Pos>(Pos.TOP_LEFT) {
@Override
public void invalidated() {
requestLayout();
}
@Override
public CssMetaData<GridPane, Pos> getCssMetaData() {
return StyleableProperties.ALIGNMENT;
}
@Override
public Object getBean() {
return GridPane.this;
}
@Override
public String getName() {
return "alignment";
}
};
}
return alignment;
}
private ObjectProperty<Pos> alignment;
public final void setAlignment(Pos value) {
alignmentProperty().set(value);
}
public final Pos getAlignment() {
return alignment == null ? Pos.TOP_LEFT : alignment.get();
}
private Pos getAlignmentInternal() {
Pos localPos = getAlignment();
return localPos == null ? Pos.TOP_LEFT : localPos;
}
/**
* For debug purposes only: controls whether lines are displayed to show the gridpane's rows and columns.
* Default is <code>false</code>.
* @return true if lines are displayed to show the gridpane's rows and columns
*/
public final BooleanProperty gridLinesVisibleProperty() {
if (gridLinesVisible == null) {
gridLinesVisible = new StyleableBooleanProperty() {
@Override
protected void invalidated() {
if (get()) {
gridLines = new Group();
gridLines.setManaged(false);
getChildren().add(gridLines);
} else {
getChildren().remove(gridLines);
gridLines = null;
}
requestLayout();
}
@Override
public CssMetaData<GridPane, Boolean> getCssMetaData() {
return StyleableProperties.GRID_LINES_VISIBLE;
}
@Override
public Object getBean() {
return GridPane.this;
}
@Override
public String getName() {
return "gridLinesVisible";
}
};
}
return gridLinesVisible;
}
private BooleanProperty gridLinesVisible;
public final void setGridLinesVisible(boolean value) { gridLinesVisibleProperty().set(value); }
public final boolean isGridLinesVisible() { return gridLinesVisible == null ? false : gridLinesVisible.get(); }
/**
* RowConstraints instances can be added to explicitly control individual row
* sizing and layout behavior.
* If not set, row sizing and layout behavior will be computed based on content.
*
*/
private final ObservableList<RowConstraints> rowConstraints = new TrackableObservableList<>() {
@Override
protected void onChanged(Change<RowConstraints> c) {
while (c.next()) {
for (RowConstraints constraints : c.getRemoved()) {
if (constraints != null && !rowConstraints.contains(constraints)) {
constraints.remove(GridPane.this);
}
}
for (RowConstraints constraints : c.getAddedSubList()) {
if (constraints != null) {
constraints.add(GridPane.this);
}
}
}
requestLayout();
}
};
/**
* Returns list of row constraints. Row constraints can be added to
* explicitly control individual row sizing and layout behavior.
* If not set, row sizing and layout behavior is computed based on content.
*
* Index in the ObservableList denotes the row number, so the row constraint for the first row
* is at the position of 0.
* @return the list of row constraints
*/
public final ObservableList<RowConstraints> getRowConstraints() { return rowConstraints; }
/**
* ColumnConstraints instances can be added to explicitly control individual column
* sizing and layout behavior.
* If not set, column sizing and layout behavior will be computed based on content.
*/
private final ObservableList<ColumnConstraints> columnConstraints = new TrackableObservableList<>() {
@Override
protected void onChanged(Change<ColumnConstraints> c) {
while(c.next()) {
for (ColumnConstraints constraints : c.getRemoved()) {
if (constraints != null && !columnConstraints.contains(constraints)) {
constraints.remove(GridPane.this);
}
}
for (ColumnConstraints constraints : c.getAddedSubList()) {
if (constraints != null) {
constraints.add(GridPane.this);
}
}
}
requestLayout();
}
};
/**
* Returns list of column constraints. Column constraints can be added to
* explicitly control individual column sizing and layout behavior.
* If not set, column sizing and layout behavior is computed based on content.
*
* Index in the ObservableList denotes the column number, so the column constraint for the first column
* is at the position of 0.
* @return the list of column constraints
*/
public final ObservableList<ColumnConstraints> getColumnConstraints() { return columnConstraints; }
/**
* Adds a child to the gridpane at the specified [column, row] position.
* This convenience method will set the gridpane column and row constraints
* on the child.
* @param child the node being added to the gridpane
* @param columnIndex the column index position for the child within the gridpane, counting from 0
* @param rowIndex the row index position for the child within the gridpane, counting from 0
*/
public void add(Node child, int columnIndex, int rowIndex) {
setConstraints(child, columnIndex, rowIndex);
getChildren().add(child);
}
/**
* Adds a child to the gridpane at the specified [column, row] position and spans.
* This convenience method will set the gridpane column, row, and span constraints
* on the child.
* @param child the node being added to the gridpane
* @param columnIndex the column index position for the child within the gridpane, counting from 0
* @param rowIndex the row index position for the child within the gridpane, counting from 0
* @param colspan the number of columns the child's layout area should span
* @param rowspan the number of rows the child's layout area should span
*/