2 * This file is part of lanterna (http://code.google.com/p/lanterna/).
4 * lanterna is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 * Copyright (C) 2010-2015 Martin
19 package com
.googlecode
.lanterna
.gui2
;
21 import com
.googlecode
.lanterna
.TerminalPosition
;
22 import com
.googlecode
.lanterna
.TerminalSize
;
24 import java
.util
.List
;
27 * Simple layout manager the puts all components on a single line, either horizontally or vertically.
29 public class LinearLayout
implements LayoutManager
{
31 * This enum type will decide the alignment of a component on the counter-axis, meaning the horizontal alignment on
32 * vertical {@code LinearLayout}s and vertical alignment on horizontal {@code LinearLayout}s.
34 public enum Alignment
{
36 * The component will be placed to the left (for vertical layouts) or top (for horizontal layouts)
40 * The component will be placed horizontally centered (for vertical layouts) or vertically centered (for
45 * The component will be placed to the right (for vertical layouts) or bottom (for horizontal layouts)
49 * The component will be forced to take up all the horizontal space (for vertical layouts) or vertical space
50 * (for horizontal layouts)
55 private static class LinearLayoutData
implements LayoutData
{
56 private final Alignment alignment
;
58 public LinearLayoutData(Alignment alignment
) {
59 this.alignment
= alignment
;
64 * Creates a {@code LayoutData} for {@code LinearLayout} that assigns a component to a particular alignment on its
65 * counter-axis, meaning the horizontal alignment on vertical {@code LinearLayout}s and vertical alignment on
66 * horizontal {@code LinearLayout}s.
67 * @param alignment Alignment to store in the {@code LayoutData} object
68 * @return {@code LayoutData} object created for {@code LinearLayout}s with the specified alignment
71 public static LayoutData
createLayoutData(Alignment alignment
) {
72 return new LinearLayoutData(alignment
);
75 private final Direction direction
;
77 private boolean changed
;
80 * Default constructor, creates a vertical {@code LinearLayout}
82 public LinearLayout() {
83 this(Direction
.VERTICAL
);
87 * Standard constructor that creates a {@code LinearLayout} with a specified direction to position the components on
88 * @param direction Direction for this {@code Direction}
90 public LinearLayout(Direction direction
) {
91 this.direction
= direction
;
92 this.spacing
= direction
== Direction
.HORIZONTAL ?
1 : 0;
97 * Sets the amount of empty space to put in between components. For horizontal layouts, this is number of columns
98 * (by default 1) and for vertical layouts this is number of rows (by default 0).
99 * @param spacing Spacing between components, either in number of columns or rows depending on the direction
102 public LinearLayout
setSpacing(int spacing
) {
103 this.spacing
= spacing
;
109 * Returns the amount of empty space to put in between components. For horizontal layouts, this is number of columns
110 * (by default 1) and for vertical layouts this is number of rows (by default 0).
111 * @return Spacing between components, either in number of columns or rows depending on the direction
113 public int getSpacing() {
118 public TerminalSize
getPreferredSize(List
<Component
> components
) {
119 if(direction
== Direction
.VERTICAL
) {
120 return getPreferredSizeVertically(components
);
123 return getPreferredSizeHorizontally(components
);
127 private TerminalSize
getPreferredSizeVertically(List
<Component
> components
) {
130 for(Component component
: components
) {
131 TerminalSize preferredSize
= component
.getPreferredSize();
132 if(maxWidth
< preferredSize
.getColumns()) {
133 maxWidth
= preferredSize
.getColumns();
135 height
+= preferredSize
.getRows();
137 height
+= spacing
* (components
.size() - 1);
138 return new TerminalSize(maxWidth
, height
);
141 private TerminalSize
getPreferredSizeHorizontally(List
<Component
> components
) {
144 for(Component component
: components
) {
145 TerminalSize preferredSize
= component
.getPreferredSize();
146 if(maxHeight
< preferredSize
.getRows()) {
147 maxHeight
= preferredSize
.getRows();
149 width
+= preferredSize
.getColumns();
151 width
+= spacing
* (components
.size() - 1);
152 return new TerminalSize(width
, maxHeight
);
156 public boolean hasChanged() {
161 public void doLayout(TerminalSize area
, List
<Component
> components
) {
162 if(direction
== Direction
.VERTICAL
) {
163 doVerticalLayout(area
, components
);
166 doHorizontalLayout(area
, components
);
168 this.changed
= false;
171 private void doVerticalLayout(TerminalSize area
, List
<Component
> components
) {
172 int remainingVerticalSpace
= area
.getRows();
173 int availableHorizontalSpace
= area
.getColumns();
174 for(Component component
: components
) {
175 if(remainingVerticalSpace
<= 0) {
176 component
.setPosition(TerminalPosition
.TOP_LEFT_CORNER
);
177 component
.setSize(TerminalSize
.ZERO
);
180 LinearLayoutData layoutData
= (LinearLayoutData
)component
.getLayoutData();
181 Alignment alignment
= Alignment
.Beginning
;
182 if(layoutData
!= null) {
183 alignment
= layoutData
.alignment
;
186 TerminalSize preferredSize
= component
.getPreferredSize();
187 TerminalSize decidedSize
= new TerminalSize(
188 Math
.min(availableHorizontalSpace
, preferredSize
.getColumns()),
189 Math
.min(remainingVerticalSpace
, preferredSize
.getRows()));
190 if(alignment
== Alignment
.Fill
) {
191 decidedSize
= decidedSize
.withColumns(availableHorizontalSpace
);
192 alignment
= Alignment
.Beginning
;
195 TerminalPosition position
= component
.getPosition();
196 position
= position
.withRow(area
.getRows() - remainingVerticalSpace
);
199 position
= position
.withColumn(availableHorizontalSpace
- decidedSize
.getColumns());
202 position
= position
.withColumn((availableHorizontalSpace
- decidedSize
.getColumns()) / 2);
206 position
= position
.withColumn(0);
209 component
.setPosition(position
);
210 component
.setSize(component
.getSize().with(decidedSize
));
211 remainingVerticalSpace
-= decidedSize
.getRows() + spacing
;
216 private void doHorizontalLayout(TerminalSize area
, List
<Component
> components
) {
217 int remainingHorizontalSpace
= area
.getColumns();
218 int availableVerticalSpace
= area
.getRows();
219 for(Component component
: components
) {
220 if(remainingHorizontalSpace
<= 0) {
221 component
.setPosition(TerminalPosition
.TOP_LEFT_CORNER
);
222 component
.setSize(TerminalSize
.ZERO
);
225 LinearLayoutData layoutData
= (LinearLayoutData
)component
.getLayoutData();
226 Alignment alignment
= Alignment
.Beginning
;
227 if(layoutData
!= null) {
228 alignment
= layoutData
.alignment
;
230 TerminalSize preferredSize
= component
.getPreferredSize();
231 TerminalSize decidedSize
= new TerminalSize(
232 Math
.min(remainingHorizontalSpace
, preferredSize
.getColumns()),
233 Math
.min(availableVerticalSpace
, preferredSize
.getRows()));
234 if(alignment
== Alignment
.Fill
) {
235 decidedSize
= decidedSize
.withRows(availableVerticalSpace
);
236 alignment
= Alignment
.Beginning
;
239 TerminalPosition position
= component
.getPosition();
240 position
= position
.withColumn(area
.getColumns() - remainingHorizontalSpace
);
243 position
= position
.withRow(availableVerticalSpace
- decidedSize
.getRows());
246 position
= position
.withRow((availableVerticalSpace
- decidedSize
.getRows()) / 2);
250 position
= position
.withRow(0);
253 component
.setPosition(position
);
254 component
.setSize(component
.getSize().with(decidedSize
));
255 remainingHorizontalSpace
-= decidedSize
.getColumns() + spacing
;