001package squidpony.squidgrid.gui.gdx; 002 003import com.badlogic.gdx.Gdx; 004import com.badlogic.gdx.scenes.scene2d.Group; 005 006import squidpony.IColorCenter; 007import squidpony.SquidTags; 008import squidpony.panel.IColoredString; 009import squidpony.panel.ICombinedPanel; 010import squidpony.panel.ISquidPanel; 011 012/** 013 * An implementation of {@link ICombinedPanel} that extends libGDX's Group. If 014 * you're a new user or need only a foreground and background, it's likely what 015 * you should use. 016 * 017 * this is a concrete implementation of {@link ICombinedPanel} that you should 018 * use if you're concretely in need of a panel to display/write to, without 019 * doing fancy GUI stuff. Because it extends libGDX's {@link Group}, it offers a 020 * lot of features. 021 * 022 * @see SquidLayers for a more advanced Group that supports multiple layers. 023 * @author smelC 024 * 025 * @param <T> 026 * The type of colors. 027 */ 028public class GroupCombinedPanel<T> extends Group implements ICombinedPanel<T> { 029 030 protected/* @Nullable */ISquidPanel<T> bg; 031 protected/* @Nullable */ISquidPanel<T> fg; 032 033 /** 034 * @param bg 035 * The backing background panel. Typically a {@link SquidPanel}. 036 * @param fg 037 * The backing foreground panel. Typically a {@link SquidPanel}. 038 * @throws IllegalStateException 039 * In various cases of errors regarding sizes of panels. 040 */ 041 public GroupCombinedPanel(ISquidPanel<T> bg, ISquidPanel<T> fg) { 042 if (bg != null && fg != null) 043 setPanels(bg, fg); 044 } 045 046 /** 047 * Constructor that defer providing the backing panels. Useful for 048 * subclasses that compute their size after being constructed. Use 049 * {@link #setPanels(ISquidPanel, ISquidPanel)} to set the panels (required 050 * before calling any {@code put} method). 051 * 052 * <p> 053 * Width and height are computed using the provided panels. 054 * </p> 055 */ 056 public GroupCombinedPanel() { 057 } 058 059 /** 060 * Sets the backing panels. 061 * 062 * @param bg 063 * Typically a {@link SquidPanel}. 064 * @param fg 065 * Typically a {@link SquidPanel}. 066 * @throws IllegalStateException 067 * In various cases of errors regarding sizes of panels. 068 */ 069 public final void setPanels(ISquidPanel<T> bg, ISquidPanel<T> fg) { 070 if (bg == null || fg == null) { 071 Gdx.app.log(SquidTags.LAYOUT, 072 "You should not call this method with a null panel. Avoiding an NPE, but you should fix that."); 073 return; 074 } 075 076 if (this.bg != null) 077 throw new IllegalStateException("Cannot change the background panel"); 078 this.bg = bg; 079 080 if (this.fg != null) 081 throw new IllegalStateException("Cannot change the foreground panel"); 082 this.fg = fg; 083 084 final int bgw = bg.gridWidth(); 085 final int bgh = bg.gridHeight(); 086 087 if (bgw != fg.gridWidth()) 088 throw new IllegalStateException("Cannot build a combined panel with backers of different widths"); 089 if (bgh != fg.gridHeight()) 090 throw new IllegalStateException( 091 "Cannot build a combined panel with backers of different heights"); 092 093 addActors(); 094 } 095 096 @Override 097 public void putFG(int x, int y, char c) { 098 checkFG(); 099 fg.put(x, y, c); 100 } 101 102 @Override 103 public void putFG(int x, int y, char c, T color) { 104 checkFG(); 105 fg.put(x, y, c, color); 106 } 107 108 @Override 109 public void putFG(int x, int y, String string, T foreground) { 110 checkFG(); 111 fg.put(x, y, string, foreground); 112 } 113 114 @Override 115 public void putFG(int x, int y, IColoredString<T> cs) { 116 checkFG(); 117 fg.put(x, y, cs); 118 } 119 120 @Override 121 public void putBG(int x, int y, T color) { 122 checkBG(); 123 bg.put(x, y, color); 124 } 125 126 @Override 127 public void put(int x, int y, char c, T background, T foreground) { 128 checkFG(); 129 checkBG(); 130 bg.put(x, y, background); 131 fg.put(x, y, c, foreground); 132 } 133 134 @Override 135 public void put(int x, int y, T background, IColoredString<T> cs) { 136 checkFG(); 137 checkBG(); 138 final int w = getGridWidth(); 139 final int l = cs.length(); 140 for (int i = x; i < l && i < w; i++) { 141 bg.put(i, y, background); 142 } 143 fg.put(x, y, cs); 144 } 145 146 @Override 147 public void put(int x, int y, String s, T background, T foreground) { 148 checkFG(); 149 checkBG(); 150 final int w = getGridWidth(); 151 final int l = s.length(); 152 for (int i = x; i < l && i < w; i++) { 153 bg.put(i, y, background); 154 } 155 fg.put(x, y, s, foreground); 156 } 157 158 /** 159 * Writes {@code string} at the bottom right. If {@code string} is wider 160 * than {@code this}, its end will be stripped. 161 * 162 * @param string 163 */ 164 public void putBottomRight(IColoredString<? extends T> string) { 165 final int width = bg.gridWidth(); 166 final int len = string.length(); 167 final int x = len < width ? width - len : 0; 168 fg.put(x, bg.gridHeight() - 1, string); 169 } 170 171 /** 172 * Writes {@code string} at the bottom left. If {@code string} is wider than 173 * {@code this}, its end will be stripped. 174 * 175 * @param string 176 */ 177 public void putBottomLeft(IColoredString<? extends T> string) { 178 fg.put(0, bg.gridHeight() - 1, string); 179 } 180 181 @Override 182 public void fill(What what, T color) { 183 final int gridWidth = getGridWidth(); 184 final int gridHeight = getGridHeight(); 185 186 final boolean bfg = what.hasFG(); 187 final boolean bbg = what.hasBG(); 188 189 for (int x = 0; x < gridWidth; x++) { 190 for (int y = 0; y < gridHeight; y++) { 191 if (bfg) 192 putFG(x, y, ' ', color); 193 if (bbg) 194 putBG(x, y, color); 195 } 196 } 197 } 198 199 @Override 200 public boolean hasActiveAnimations() { 201 return (bg != null && bg.hasActiveAnimations()) || (fg != null && fg.hasActiveAnimations()); 202 } 203 204 @Override 205 public void setColorCenter(IColorCenter<T> icc) { 206 bg.setColorCenter(icc); 207 fg.setColorCenter(icc); 208 } 209 210 /** 211 * @return The backer's width 212 * @throws IllegalStateException 213 * If backers aren't set yet. 214 */ 215 public int getGridWidth() { 216 if (bg == null) 217 throw new NullPointerException("The background panel must be set before requesting the width"); 218 return bg.gridWidth(); 219 } 220 221 /** 222 * @return The backer's height 223 * @throws IllegalStateException 224 * If backers aren't set yet. 225 */ 226 public int getGridHeight() { 227 if (bg == null) 228 throw new NullPointerException("The background panel must be set before requesting the height"); 229 return bg.gridHeight(); 230 } 231 232 /** 233 * @return The width of cells in the backing panel. 234 */ 235 public int cellWidth() { 236 return bg.cellWidth(); 237 } 238 239 /** 240 * @return The height of cells in the backing panel. 241 */ 242 public int cellHeight() { 243 return bg.cellHeight(); 244 } 245 246 protected void addActors() { 247 addActor((SquidPanel) bg.getBacker()); 248 addActor((SquidPanel) fg.getBacker()); 249 } 250 251 protected void checkFG() { 252 if (fg == null) 253 throw new NullPointerException("The foreground panel must be set before writing to it"); 254 } 255 256 protected void checkBG() { 257 if (bg == null) 258 throw new NullPointerException("The background panel must be set before writing to it"); 259 } 260 261 @Override 262 public String toString() { 263 return this.getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); 264 } 265 266}