3
3
import games .strategy .engine .data .GameData ;
4
4
import games .strategy .engine .data .GamePlayer ;
5
5
import games .strategy .engine .data .Resource ;
6
- import games .strategy .engine .data .ResourceCollection ;
7
6
import games .strategy .engine .data .Territory ;
8
7
import games .strategy .engine .data .TerritoryEffect ;
9
8
import games .strategy .triplea .Constants ;
10
9
import games .strategy .triplea .attachments .TerritoryAttachment ;
10
+ import games .strategy .triplea .util .UnitCategory ;
11
+ import games .strategy .triplea .util .UnitSeparator ;
11
12
import java .awt .BorderLayout ;
12
13
import java .awt .Dimension ;
14
+ import java .awt .Font ;
13
15
import java .awt .GridBagLayout ;
14
16
import java .awt .Image ;
17
+ import java .util .Collection ;
15
18
import java .util .List ;
16
19
import java .util .Optional ;
20
+ import javax .annotation .Nullable ;
17
21
import javax .swing .BorderFactory ;
22
+ import javax .swing .Box ;
23
+ import javax .swing .BoxLayout ;
18
24
import javax .swing .ImageIcon ;
19
25
import javax .swing .JLabel ;
20
26
import javax .swing .JPanel ;
27
+ import javax .swing .JSeparator ;
21
28
import javax .swing .SwingConstants ;
29
+ import javax .swing .border .Border ;
22
30
import javax .swing .border .EtchedBorder ;
23
31
import org .triplea .java .collections .IntegerMap ;
24
32
import org .triplea .swing .SwingComponents ;
@@ -42,26 +50,24 @@ public class BottomBar extends JPanel {
42
50
public BottomBar (final UiContext uiContext , final GameData data , final boolean usingDiceServer ) {
43
51
this .uiContext = uiContext ;
44
52
this .data = data ;
45
- setLayout ( new BorderLayout () );
53
+ this . resourceBar = new ResourceBar ( data , uiContext );
46
54
47
- resourceBar = new ResourceBar ( data , uiContext );
55
+ setLayout ( new BorderLayout () );
48
56
add (createCenterPanel (), BorderLayout .CENTER );
49
57
add (createStepPanel (usingDiceServer ), BorderLayout .EAST );
50
58
}
51
59
52
60
private JPanel createCenterPanel () {
53
61
final JPanel centerPanel = new JPanel ();
54
62
centerPanel .setLayout (new GridBagLayout ());
55
- centerPanel .setBorder (BorderFactory .createEmptyBorder ());
56
63
final var gridBuilder =
57
64
new GridBagConstraintsBuilder (0 , 0 ).weightY (1 ).fill (GridBagConstraintsFill .BOTH );
58
65
59
66
centerPanel .add (
60
67
resourceBar , gridBuilder .weightX (0 ).anchor (GridBagConstraintsAnchor .WEST ).build ());
61
68
62
- territoryInfo .setLayout (new GridBagLayout ());
63
- territoryInfo .setBorder (new EtchedBorder (EtchedBorder .RAISED ));
64
69
territoryInfo .setPreferredSize (new Dimension (0 , 0 ));
70
+ territoryInfo .setBorder (new EtchedBorder (EtchedBorder .RAISED ));
65
71
centerPanel .add (
66
72
territoryInfo ,
67
73
gridBuilder .gridX (1 ).weightX (1 ).anchor (GridBagConstraintsAnchor .CENTER ).build ());
@@ -102,65 +108,104 @@ public void setStatus(final String msg, final Optional<Image> image) {
102
108
}
103
109
}
104
110
105
- public void setTerritory (final Territory territory ) {
111
+ public void setTerritory (final @ Nullable Territory territory ) {
106
112
territoryInfo .removeAll ();
107
113
108
- final JLabel nameLabel = new JLabel ();
109
- if (territory != null ) {
110
- nameLabel .setText ("<html><b>" + territory .getName ());
111
- }
112
-
113
- final var gridBuilder = new GridBagConstraintsBuilder (0 , 0 );
114
- // If territory is null or doesn't have an attachment then just display the name or "none"
115
- if (territory == null || TerritoryAttachment .get (territory ) == null ) {
116
- territoryInfo .add (nameLabel , gridBuilder .build ());
114
+ if (territory == null ) {
117
115
SwingComponents .redraw (territoryInfo );
118
116
return ;
119
117
}
120
118
121
- // Display territory effects, territory name, and resources
119
+ // Box layout with horizontal glue on both sides achieves the following desirable properties:
120
+ // 1. If the content is narrower than the available space, it will be centered.
121
+ // 2. If the content is wider than the available space, then the beginning will be shown,
122
+ // which is the more important information (territory name, income, etc).
123
+ // 3. Elements are vertically centered.
124
+ territoryInfo .setLayout (new BoxLayout (territoryInfo , BoxLayout .LINE_AXIS ));
125
+ territoryInfo .add (Box .createHorizontalGlue ());
126
+
122
127
final TerritoryAttachment ta = TerritoryAttachment .get (territory );
123
- final List < TerritoryEffect > territoryEffects = ta . getTerritoryEffect ();
124
- int count = 0 ;
128
+
129
+ // Display territory effects, territory name, resources and units.
125
130
final StringBuilder territoryEffectText = new StringBuilder ();
126
- for (final TerritoryEffect territoryEffect : territoryEffects ) {
131
+ final List <TerritoryEffect > territoryEffects = ta != null ? ta .getTerritoryEffect () : List .of ();
132
+ for (final TerritoryEffect effect : territoryEffects ) {
127
133
try {
128
- final JLabel territoryEffectLabel = new JLabel ();
129
- territoryEffectLabel .setToolTipText (territoryEffect .getName ());
130
- territoryEffectLabel .setIcon (
131
- uiContext .getTerritoryEffectImageFactory ().getIcon (territoryEffect .getName ()));
132
- territoryEffectLabel .setBorder (BorderFactory .createEmptyBorder (0 , 0 , 0 , 10 ));
133
- territoryInfo .add (territoryEffectLabel , gridBuilder .gridX (count ++).build ());
134
+ final JLabel label = new JLabel ();
135
+ label .setToolTipText (effect .getName ());
136
+ label .setIcon (uiContext .getTerritoryEffectImageFactory ().getIcon (effect .getName ()));
137
+ territoryInfo .add (label );
138
+ territoryInfo .add (Box .createHorizontalStrut (6 ));
134
139
} catch (final IllegalStateException e ) {
135
- territoryEffectText .append (territoryEffect .getName ()).append (", " );
140
+ territoryEffectText .append (effect .getName ()).append (", " );
136
141
}
137
142
}
138
143
139
- territoryInfo .add (nameLabel , gridBuilder . gridX ( count ++). build ( ));
144
+ territoryInfo .add (createTerritoryNameLabel ( territory . getName () ));
140
145
141
146
if (territoryEffectText .length () > 0 ) {
142
147
territoryEffectText .setLength (territoryEffectText .length () - 2 );
143
- final JLabel territoryEffectTextLabel = new JLabel ();
144
- territoryEffectTextLabel .setText (" (" + territoryEffectText + ")" );
145
- territoryInfo .add (territoryEffectTextLabel , gridBuilder .gridX (count ++).build ());
148
+ final JLabel territoryEffectTextLabel = new JLabel (" (" + territoryEffectText + ")" );
149
+ territoryInfo .add (territoryEffectTextLabel );
150
+ }
151
+
152
+ Optional .ofNullable (ta ).ifPresent (this ::addTerritoryResourceDetails );
153
+
154
+ if (uiContext .isShowUnitsInStatusBar ()) {
155
+ final Collection <UnitCategory > units = UnitSeparator .categorize (territory .getUnits ());
156
+ if (!units .isEmpty ()) {
157
+ JSeparator separator = new JSeparator (JSeparator .VERTICAL );
158
+ separator .setMaximumSize (new Dimension (40 , getHeight ()));
159
+ separator .setPreferredSize (separator .getMaximumSize ());
160
+ territoryInfo .add (separator );
161
+ territoryInfo .add (createUnitBar (units ));
162
+ }
146
163
}
147
164
165
+ territoryInfo .add (Box .createHorizontalGlue ());
166
+ SwingComponents .redraw (territoryInfo );
167
+ }
168
+
169
+ private JLabel createTerritoryNameLabel (String territoryName ) {
170
+ final JLabel nameLabel = new JLabel (territoryName );
171
+ nameLabel .setFont (nameLabel .getFont ().deriveFont (Font .BOLD ));
172
+ // Ensure the text position is always the same, regardless of other components, by padding to
173
+ // fill available height.
174
+ final int labelHeight = nameLabel .getPreferredSize ().height ;
175
+ nameLabel .setBorder (createBorderToFillAvailableHeight (labelHeight , getHeight ()));
176
+ return nameLabel ;
177
+ }
178
+
179
+ private Border createBorderToFillAvailableHeight (int componentHeight , int availableHeight ) {
180
+ int extraVerticalSpace = Math .max (availableHeight - componentHeight , 0 );
181
+ int topPad = extraVerticalSpace / 2 ;
182
+ int bottomPad = extraVerticalSpace - topPad ; // Might != topPad if extraVerticalSpace is odd.
183
+ return BorderFactory .createEmptyBorder (topPad , 0 , bottomPad , 0 );
184
+ }
185
+
186
+ private void addTerritoryResourceDetails (TerritoryAttachment ta ) {
148
187
final IntegerMap <Resource > resources = new IntegerMap <>();
149
188
final int production = ta .getProduction ();
150
189
if (production > 0 ) {
151
190
resources .add (new Resource (Constants .PUS , data ), production );
152
191
}
153
- final ResourceCollection resourceCollection = ta .getResources ();
154
- if (resourceCollection != null ) {
155
- resources .add (resourceCollection .getResourcesCopy ());
156
- }
192
+ Optional .ofNullable (ta .getResources ()).ifPresent (r -> resources .add (r .getResourcesCopy ()));
157
193
for (final Resource resource : resources .keySet ()) {
158
- final JLabel resourceLabel =
159
- uiContext .getResourceImageFactory ().getLabel (resource , resources );
160
- resourceLabel .setBorder (BorderFactory .createEmptyBorder (0 , 10 , 0 , 0 ));
161
- territoryInfo .add (resourceLabel , gridBuilder .gridX (count ++).build ());
194
+ territoryInfo .add (Box .createHorizontalStrut (6 ));
195
+ territoryInfo .add (uiContext .getResourceImageFactory ().getLabel (resource , resources ));
162
196
}
163
- SwingComponents .redraw (territoryInfo );
197
+ }
198
+
199
+ private SimpleUnitPanel createUnitBar (Collection <UnitCategory > units ) {
200
+ final var unitBar = new SimpleUnitPanel (uiContext , SimpleUnitPanel .Style .SMALL_ICONS_ROW );
201
+ unitBar .setScaleFactor (0.5 );
202
+ unitBar .setShowCountsForSingleUnits (false );
203
+ unitBar .setUnitsFromCategories (units );
204
+ // Constrain the preferred size to the available size so that unit images that may not fully fit
205
+ // don't cause layout issues.
206
+ final int unitsWidth = unitBar .getPreferredSize ().width ;
207
+ unitBar .setPreferredSize (new Dimension (unitsWidth , getHeight ()));
208
+ return unitBar ;
164
209
}
165
210
166
211
public void gameDataChanged () {
0 commit comments