6cc6e85e8d5f779225f0f8355e249c494063e35d
[jvcard.git] / src / com / googlecode / lanterna / gui2 / BorderLayout.java
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 */
19 package com.googlecode.lanterna.gui2;
20
21 import com.googlecode.lanterna.TerminalPosition;
22 import com.googlecode.lanterna.TerminalSize;
23
24 import java.util.*;
25
26 /**
27 * BorderLayout imitates the BorderLayout class from AWT, allowing you to add a center component with optional
28 * components around it in top, bottom, left and right locations. The edge components will be sized at their preferred
29 * size and the center component will take up whatever remains.
30 * @author martin
31 */
32 public class BorderLayout implements LayoutManager {
33
34 /**
35 * This type is what you use as the layout data for components added to a panel using {@code BorderLayout} for its
36 * layout manager. This values specified where inside the panel the component should be added.
37 */
38 public enum Location implements LayoutData {
39 /**
40 * The component with this value as its layout data will occupy the center space, whatever is remaining after
41 * the other components (if any) have allocated their space.
42 */
43 CENTER,
44 /**
45 * The component with this value as its layout data will occupy the left side of the container, attempting to
46 * allocate the preferred width of the component and at least the preferred height, but could be more depending
47 * on the other components added.
48 */
49 LEFT,
50 /**
51 * The component with this value as its layout data will occupy the right side of the container, attempting to
52 * allocate the preferred width of the component and at least the preferred height, but could be more depending
53 * on the other components added.
54 */
55 RIGHT,
56 /**
57 * The component with this value as its layout data will occupy the top side of the container, attempting to
58 * allocate the preferred height of the component and at least the preferred width, but could be more depending
59 * on the other components added.
60 */
61 TOP,
62 /**
63 * The component with this value as its layout data will occupy the bottom side of the container, attempting to
64 * allocate the preferred height of the component and at least the preferred width, but could be more depending
65 * on the other components added.
66 */
67 BOTTOM,
68 ;
69 }
70
71 //When components don't have a location, we'll assign an available location based on this order
72 private static final List<Location> AUTO_ASSIGN_ORDER = Collections.unmodifiableList(Arrays.asList(
73 Location.CENTER,
74 Location.TOP,
75 Location.BOTTOM,
76 Location.LEFT,
77 Location.RIGHT));
78
79 @Override
80 public TerminalSize getPreferredSize(List<Component> components) {
81 EnumMap<Location, Component> layout = makeLookupMap(components);
82 int preferredHeight =
83 (layout.containsKey(Location.TOP) ? layout.get(Location.TOP).getPreferredSize().getRows() : 0)
84 +
85 Math.max(
86 layout.containsKey(Location.LEFT) ? layout.get(Location.LEFT).getPreferredSize().getRows() : 0,
87 Math.max(
88 layout.containsKey(Location.CENTER) ? layout.get(Location.CENTER).getPreferredSize().getRows() : 0,
89 layout.containsKey(Location.RIGHT) ? layout.get(Location.RIGHT).getPreferredSize().getRows() : 0))
90 +
91 (layout.containsKey(Location.BOTTOM) ? layout.get(Location.BOTTOM).getPreferredSize().getRows() : 0);
92
93 int preferredWidth =
94 Math.max(
95 (layout.containsKey(Location.LEFT) ? layout.get(Location.LEFT).getPreferredSize().getColumns() : 0) +
96 (layout.containsKey(Location.CENTER) ? layout.get(Location.CENTER).getPreferredSize().getColumns() : 0) +
97 (layout.containsKey(Location.RIGHT) ? layout.get(Location.RIGHT).getPreferredSize().getColumns() : 0),
98 Math.max(
99 layout.containsKey(Location.TOP) ? layout.get(Location.TOP).getPreferredSize().getColumns() : 0,
100 layout.containsKey(Location.BOTTOM) ? layout.get(Location.BOTTOM).getPreferredSize().getColumns() : 0));
101 return new TerminalSize(preferredWidth, preferredHeight);
102 }
103
104 @Override
105 public void doLayout(TerminalSize area, List<Component> components) {
106 EnumMap<Location, Component> layout = makeLookupMap(components);
107 int availableHorizontalSpace = area.getColumns();
108 int availableVerticalSpace = area.getRows();
109
110 //We'll need this later on
111 int topComponentHeight = 0;
112 int leftComponentWidth = 0;
113
114 //First allocate the top
115 if(layout.containsKey(Location.TOP)) {
116 Component topComponent = layout.get(Location.TOP);
117 topComponentHeight = Math.min(topComponent.getPreferredSize().getRows(), availableVerticalSpace);
118 topComponent.setPosition(TerminalPosition.TOP_LEFT_CORNER);
119 topComponent.setSize(new TerminalSize(availableHorizontalSpace, topComponentHeight));
120 availableVerticalSpace -= topComponentHeight;
121 }
122
123 //Next allocate the bottom
124 if(layout.containsKey(Location.BOTTOM)) {
125 Component bottomComponent = layout.get(Location.BOTTOM);
126 int bottomComponentHeight = Math.min(bottomComponent.getPreferredSize().getRows(), availableVerticalSpace);
127 bottomComponent.setPosition(new TerminalPosition(0, area.getRows() - bottomComponentHeight));
128 bottomComponent.setSize(new TerminalSize(availableHorizontalSpace, bottomComponentHeight));
129 availableVerticalSpace -= bottomComponentHeight;
130 }
131
132 //Now divide the remaining space between LEFT, CENTER and RIGHT
133 if(layout.containsKey(Location.LEFT)) {
134 Component leftComponent = layout.get(Location.LEFT);
135 leftComponentWidth = Math.min(leftComponent.getPreferredSize().getColumns(), availableHorizontalSpace);
136
137 /*
138 if(leftComponentWidth == availableHorizontalSpace ){
139 if(layout.containsKey(Location.RIGHT))
140 leftComponentWidth--;
141 if(layout.containsKey(Location.CENTER))
142 leftComponentWidth--;
143 }*/
144
145 leftComponent.setPosition(new TerminalPosition(0, topComponentHeight));
146 leftComponent.setSize(new TerminalSize(leftComponentWidth, availableVerticalSpace));
147 availableHorizontalSpace -= leftComponentWidth;
148
149 if(availableHorizontalSpace<=0)
150 availableHorizontalSpace=1;
151 }
152 if(layout.containsKey(Location.RIGHT)) {
153 Component rightComponent = layout.get(Location.RIGHT);
154 int rightComponentWidth = Math.min(rightComponent.getPreferredSize().getColumns(), availableHorizontalSpace);
155
156 /*
157 if(rightComponentWidth == availableHorizontalSpace ){
158 if(layout.containsKey(Location.CENTER))
159 rightComponentWidth--;
160 }*/
161
162 rightComponent.setPosition(new TerminalPosition(area.getColumns() - rightComponentWidth, topComponentHeight));
163 rightComponent.setSize(new TerminalSize(rightComponentWidth, availableVerticalSpace));
164 availableHorizontalSpace -= rightComponentWidth;
165
166 if(availableHorizontalSpace<=0)
167 availableHorizontalSpace=1;
168 }
169 if(layout.containsKey(Location.CENTER)) {
170 Component centerComponent = layout.get(Location.CENTER);
171 centerComponent.setPosition(new TerminalPosition(leftComponentWidth, topComponentHeight));
172 centerComponent.setSize(new TerminalSize(availableHorizontalSpace, availableVerticalSpace));
173 }
174
175 //Set the remaining components to 0x0
176 for(Component component: components) {
177 if(!layout.values().contains(component)) {
178 component.setPosition(TerminalPosition.TOP_LEFT_CORNER);
179 component.setSize(TerminalSize.ZERO);
180 }
181 }
182 }
183
184 private EnumMap<Location, Component> makeLookupMap(List<Component> components) {
185 EnumMap<Location, Component> map = new EnumMap<BorderLayout.Location, Component>(Location.class);
186 List<Component> unassignedComponents = new ArrayList<Component>();
187 for(Component component: components) {
188 if(component.getLayoutData() instanceof Location) {
189 map.put((Location)component.getLayoutData(), component);
190 }
191 else {
192 unassignedComponents.add(component);
193 }
194 }
195 //Try to assign components to available locations
196 for(Component component: unassignedComponents) {
197 for(Location location: AUTO_ASSIGN_ORDER) {
198 if(!map.containsKey(location)) {
199 map.put(location, component);
200 break;
201 }
202 }
203 }
204 return map;
205 }
206
207 @Override
208 public boolean hasChanged() {
209 //No internal state
210 return false;
211 }
212 }