Fix UTF8 bug, create first executable JAR file
[jvcard.git] / src / com / googlecode / lanterna / gui2 / LinearLayout.java
... / ...
CommitLineData
1/*
2 * This file is part of lanterna (http://code.google.com/p/lanterna/).
3 *
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.
8 *
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.
13 *
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/>.
16 *
17 * Copyright (C) 2010-2015 Martin
18 */
19package com.googlecode.lanterna.gui2;
20
21import com.googlecode.lanterna.TerminalPosition;
22import com.googlecode.lanterna.TerminalSize;
23
24import java.util.List;
25
26/**
27 * Simple layout manager the puts all components on a single line, either horizontally or vertically.
28 */
29public class LinearLayout implements LayoutManager {
30 /**
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.
33 */
34 public enum Alignment {
35 /**
36 * The component will be placed to the left (for vertical layouts) or top (for horizontal layouts)
37 */
38 Beginning,
39 /**
40 * The component will be placed horizontally centered (for vertical layouts) or vertically centered (for
41 * horizontal layouts)
42 */
43 Center,
44 /**
45 * The component will be placed to the right (for vertical layouts) or bottom (for horizontal layouts)
46 */
47 End,
48 /**
49 * The component will be forced to take up all the horizontal space (for vertical layouts) or vertical space
50 * (for horizontal layouts)
51 */
52 Fill,
53 }
54
55 private static class LinearLayoutData implements LayoutData {
56 private final Alignment alignment;
57
58 public LinearLayoutData(Alignment alignment) {
59 this.alignment = alignment;
60 }
61 }
62
63 /**
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
69 * @see Alignment
70 */
71 public static LayoutData createLayoutData(Alignment alignment) {
72 return new LinearLayoutData(alignment);
73 }
74
75 private final Direction direction;
76 private int spacing;
77 private boolean changed;
78
79 /**
80 * Default constructor, creates a vertical {@code LinearLayout}
81 */
82 public LinearLayout() {
83 this(Direction.VERTICAL);
84 }
85
86 /**
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}
89 */
90 public LinearLayout(Direction direction) {
91 this.direction = direction;
92 this.spacing = direction == Direction.HORIZONTAL ? 1 : 0;
93 this.changed = true;
94 }
95
96 /**
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
100 * @return Itself
101 */
102 public LinearLayout setSpacing(int spacing) {
103 this.spacing = spacing;
104 this.changed = true;
105 return this;
106 }
107
108 /**
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
112 */
113 public int getSpacing() {
114 return spacing;
115 }
116
117 @Override
118 public TerminalSize getPreferredSize(List<Component> components) {
119 if(direction == Direction.VERTICAL) {
120 return getPreferredSizeVertically(components);
121 }
122 else {
123 return getPreferredSizeHorizontally(components);
124 }
125 }
126
127 private TerminalSize getPreferredSizeVertically(List<Component> components) {
128 int maxWidth = 0;
129 int height = 0;
130 for(Component component: components) {
131 TerminalSize preferredSize = component.getPreferredSize();
132 if(maxWidth < preferredSize.getColumns()) {
133 maxWidth = preferredSize.getColumns();
134 }
135 height += preferredSize.getRows();
136 }
137 height += spacing * (components.size() - 1);
138 return new TerminalSize(maxWidth, height);
139 }
140
141 private TerminalSize getPreferredSizeHorizontally(List<Component> components) {
142 int maxHeight = 0;
143 int width = 0;
144 for(Component component: components) {
145 TerminalSize preferredSize = component.getPreferredSize();
146 if(maxHeight < preferredSize.getRows()) {
147 maxHeight = preferredSize.getRows();
148 }
149 width += preferredSize.getColumns();
150 }
151 width += spacing * (components.size() - 1);
152 return new TerminalSize(width, maxHeight);
153 }
154
155 @Override
156 public boolean hasChanged() {
157 return changed;
158 }
159
160 @Override
161 public void doLayout(TerminalSize area, List<Component> components) {
162 if(direction == Direction.VERTICAL) {
163 doVerticalLayout(area, components);
164 }
165 else {
166 doHorizontalLayout(area, components);
167 }
168 this.changed = false;
169 }
170
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);
178 }
179 else {
180 LinearLayoutData layoutData = (LinearLayoutData)component.getLayoutData();
181 Alignment alignment = Alignment.Beginning;
182 if(layoutData != null) {
183 alignment = layoutData.alignment;
184 }
185
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;
193 }
194
195 TerminalPosition position = component.getPosition();
196 position = position.withRow(area.getRows() - remainingVerticalSpace);
197 switch(alignment) {
198 case End:
199 position = position.withColumn(availableHorizontalSpace - decidedSize.getColumns());
200 break;
201 case Center:
202 position = position.withColumn((availableHorizontalSpace - decidedSize.getColumns()) / 2);
203 break;
204 case Beginning:
205 default:
206 position = position.withColumn(0);
207 break;
208 }
209 component.setPosition(position);
210 component.setSize(component.getSize().with(decidedSize));
211 remainingVerticalSpace -= decidedSize.getRows() + spacing;
212 }
213 }
214 }
215
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);
223 }
224 else {
225 LinearLayoutData layoutData = (LinearLayoutData)component.getLayoutData();
226 Alignment alignment = Alignment.Beginning;
227 if(layoutData != null) {
228 alignment = layoutData.alignment;
229 }
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;
237 }
238
239 TerminalPosition position = component.getPosition();
240 position = position.withColumn(area.getColumns() - remainingHorizontalSpace);
241 switch(alignment) {
242 case End:
243 position = position.withRow(availableVerticalSpace - decidedSize.getRows());
244 break;
245 case Center:
246 position = position.withRow((availableVerticalSpace - decidedSize.getRows()) / 2);
247 break;
248 case Beginning:
249 default:
250 position = position.withRow(0);
251 break;
252 }
253 component.setPosition(position);
254 component.setSize(component.getSize().with(decidedSize));
255 remainingHorizontalSpace -= decidedSize.getColumns() + spacing;
256 }
257 }
258 }
259}