001package squidpony.squidgrid.gui.gdx;
002
003import com.badlogic.gdx.Input.Keys;
004import com.badlogic.gdx.graphics.Color;
005import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
006import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
007import com.badlogic.gdx.scenes.scene2d.Actor;
008
009/**
010 * @author smelC
011 */
012public class UIUtil {
013
014        /**
015         * Draws margins around an actor.
016         * 
017         * @param renderer_
018         *            The renderer to use. If {@code null} a new one will be
019         *            allocated.
020         * @param a
021         * @param margin
022         *            The size of the margin to draw.
023         * @param c
024         *            The margins' colors.
025         */
026        public static void drawMarginsAround(ShapeRenderer renderer_, Actor a, float margin, Color color,
027                        CornerStyle cornerStyle) {
028                drawMarginsAround(renderer_, a.getX(), a.getY(), a.getWidth(), a.getHeight(), margin, color,
029                                cornerStyle, 1f, 1f);
030        }
031
032        /**
033         * Draws margins around a rectangle
034         * 
035         * @param botLeftX
036         *            The rectangle's bottom left.
037         * @param botLeftY
038         *            The rectangle's bottom left.
039         * @param width
040         *            The rectangle's width.
041         * @param height
042         *            The rectangle's height.
043         * @param xmargin
044         *            The size of the left margin and the size of the right margin.
045         * @param ymargin
046         *            The size of the bottom margin and the size of the top margin.
047         * @param c
048         *            The margins' colors.
049         */
050        public static void drawMarginsAround(float botLeftX, float botLeftY, int width, int height, int xmargin,
051                        int ymargin, Color c) {
052                if (xmargin == 0 && ymargin == 0)
053                        return;
054
055                final ShapeRenderer renderer = new ShapeRenderer();
056                renderer.begin(ShapeType.Filled);
057                renderer.setColor(c);
058
059                if (0 < xmargin) {
060                        /* The left rectangle */
061                        renderer.rect(botLeftX - xmargin, botLeftY - ymargin, xmargin, height + (ymargin * 2));
062                        /* The right rectangle */
063                        renderer.rect(botLeftX + width, botLeftY - ymargin, xmargin, height + (ymargin * 2));
064                }
065                if (0 < ymargin) {
066                        /* The bottom rectangle */
067                        renderer.rect(botLeftX, botLeftY - ymargin, width, ymargin);
068                        /* The top rectangle */
069                        renderer.rect(botLeftX, botLeftY + height, width, ymargin);
070                }
071
072                renderer.end();
073                renderer.dispose();
074        }
075
076        /**
077         * @param renderer_
078         *            The renderer to use. If {@code null} a new one will be
079         *            allocated.
080         * @param botLeftX
081         *            The bottom left x cell of the rectangle to draw around.
082         * @param botLeftY
083         *            The bottom left y cell of the rectangle to draw around.
084         * @param width
085         *            The width of the button considered.
086         * @param height
087         *            The width of the button considered.
088         * @param margin
089         *            The size of the margin to draw.
090         * @param color
091         *            The color to draw
092         * @param cornerStyle
093         *            The style with which to draw the margins
094         */
095        public static void drawMarginsAround(ShapeRenderer renderer_, float botLeftX, float botLeftY, float width,
096                        float height, float margin, Color color, CornerStyle cornerStyle) {
097                drawMarginsAround(renderer_, botLeftX, botLeftY, width, height, margin, color, cornerStyle, 1f, 1f);
098        }
099
100        /**
101         * @param renderer_
102         *            The renderer to use. If {@code null} a new one will be
103         *            allocated.
104         * @param botLeftX
105         *            The bottom left x cell of the rectangle to draw around.
106         * @param botLeftY
107         *            The bottom left y cell of the rectangle to draw around.
108         * @param width
109         *            The width of the button considered.
110         * @param height
111         *            The width of the button considered.
112         * @param margin
113         *            The size of the margin to draw.
114         * @param color
115         *            The color to draw
116         * @param cornerStyle
117         *            The style with which to draw the margins
118         * @param zoomX
119         *            A multiplier for the world x-size of non-ShapeRenderer
120         *            objects, that needs to be reversed for this
121         * @param zoomY
122         *            A multiplier for the world y-size of non-ShapeRenderer
123         *            objects, that needs to be reversed for this
124         */
125        public static void drawMarginsAround(ShapeRenderer renderer_, float botLeftX, float botLeftY, float width,
126                        float height, float margin, Color color, CornerStyle cornerStyle, float zoomX, float zoomY) {
127                if (margin == 0 || color == null)
128                        /* Nothing to do */
129                        return;
130
131                botLeftY += 1;
132
133                final boolean reset;
134                final ShapeRenderer renderer = renderer_ == null ? new ShapeRenderer() : renderer_;
135                /*
136                 * No matter the state of the given ShapeRenderer, we'll be fine, thanks
137                 * to this:
138                 */
139                if (!renderer.isDrawing()) {
140                        reset = true;
141                        renderer.begin(ShapeType.Filled);
142                } else
143                        reset = false;
144                renderer.scale(1f / zoomX, 1f / zoomY, 1f);
145                renderer.setColor(color);
146
147                if (cornerStyle == CornerStyle.ROUNDED || cornerStyle == CornerStyle.MISSING) {
148                        /* Left margin */
149                        renderer.rect(botLeftX - margin, botLeftY, margin, height);
150                        /* Right margin */
151                        renderer.rect(botLeftX + width, botLeftY, margin, height);
152                } else {
153                        /* Left margin */
154                        renderer.rect(botLeftX - margin, botLeftY - margin, margin, height + (margin * 2));
155                        /* Right margin */
156                        renderer.rect(botLeftX + width, botLeftY - margin, margin, height + (margin * 2));
157                }
158                /* Bottom margin */
159                renderer.rect(botLeftX, botLeftY - margin, width, margin);
160                /* Top margin */
161                renderer.rect(botLeftX, botLeftY + height, width, margin);
162
163                if (cornerStyle == CornerStyle.ROUNDED) {
164                        /* Bottom left */
165                        renderer.arc(botLeftX, botLeftY, margin, 180, 90);
166                        /* Top left */
167                        renderer.arc(botLeftX, botLeftY + height, margin, 90, 90);
168                        /* Top right */
169                        renderer.arc(botLeftX + width, botLeftY + height, margin, 0, 90);
170                        /* Bottom Right */
171                        renderer.arc(botLeftX + width, botLeftY, margin, 270, 90);
172                }
173
174                if (reset)
175                        renderer.end();
176
177                if (renderer_ == null)
178                        /* I allocated it, I must dispose it */
179                        renderer.dispose();
180        }
181
182        /**
183         * Draws a rectangle using a {@link ShapeRenderer}.
184         * 
185         * @parem sRender_ The renderer to use. If {@code null} a new one will be
186         *        allocated.
187         * @param botLeftX
188         *            The bottom left x of the rectangle.
189         * @param botLeftY
190         *            The bottom left y of the rectangle.
191         * @param width
192         *            The rectangle's width
193         * @param height
194         *            The rectangle's height
195         * @param st
196         *            The style to use
197         * @param color
198         *            The rectangle's color
199         */
200        public static void drawRectangle(/* @Nullable */ShapeRenderer sRender_, float botLeftX, float botLeftY,
201                        float width, float height, ShapeType st, Color color) {
202                final ShapeRenderer sRender = sRender_ == null ? new ShapeRenderer() : sRender_;
203                final boolean reset;
204                /*
205                 * No matter the state of the given ShapeRenderer, we'll be fine, thanks
206                 * to this:
207                 */
208                if (!sRender.isDrawing()) {
209                        reset = true;
210                        sRender.begin(st);
211                } else
212                        reset = false;
213                sRender.setColor(color);
214                sRender.rect(botLeftX, botLeftY, width, height);
215                if (reset)
216                        sRender.end();
217                if (sRender != sRender_)
218                        /* I allocated it */
219                        sRender.dispose();
220        }
221
222        /**
223         * Draws a rectangle using a {@link ShapeRenderer}, allocating a new one for
224         * the occasion.
225         * 
226         * @param botLeftX
227         *            The bottom left x of the rectangle.
228         * @param botLeftY
229         *            The bottom left y of the rectangle.
230         * @param width
231         *            The rectangle's width
232         * @param height
233         *            The rectangle's height
234         * @param st
235         *            The style to use
236         * @param color
237         *            The rectangle's color
238         */
239        public static void drawRectangle(float botLeftX, float botLeftY, float width, float height, ShapeType st,
240                        Color color) {
241                drawRectangle(null, botLeftX, botLeftY, width, height, st, color);
242        }
243
244        /**
245         * @author smelC
246         */
247        public static enum CornerStyle {
248                SQUARE,
249                /**
250                 * Here's an example of this style:
251                 * 
252                 * <br>
253                 * 
254                 * <img src="http://i.imgur.com/AQgWeic.png"/>.
255                 */
256                ROUNDED,
257                /**
258                 * A NES-like style (to my taste..). Try it, I can't explain it with
259                 * sentences. Here's an example:
260                 * 
261                 * <br>
262                 * 
263                 * <img src="http://i.imgur.com/PQSvT0t.png"/>
264                 */
265                MISSING,
266        }
267
268        /**
269         * A vertical move triggered by keyboard keys.
270         * 
271         * @author smelC
272         */
273        public static enum YMoveKind {
274                /** The kind corresponding to arrow up */
275                UP,
276                /** The kind corresponding to arrow down */
277                DOWN,
278                /** The kind corresponding to page down */
279                PAGE_DOWN,
280                /** The kind corresponding to page up */
281                PAGE_UP;
282
283                /**
284                 * @return {@code true} if {@code this} is downward.
285                 */
286                public boolean isDown() {
287                        switch (this) {
288                        case DOWN:
289                        case PAGE_DOWN:
290                                return true;
291                        case PAGE_UP:
292                        case UP:
293                                return false;
294                        }
295                        throw new IllegalStateException("Unmatched " + getClass().getSimpleName() + ": " + this);
296                }
297
298                /**
299                 * @param keycode
300                 * @param vim
301                 *            Whether to recognize vim shortcuts (j/k).
302                 * @return The move kind corresponding to {@code keycode}, or
303                 *         {@code null} if none.
304                 */
305                public static YMoveKind of(int keycode, boolean vim) {
306                        if (keycode == Keys.UP || keycode == Keys.DPAD_UP || keycode == Keys.NUMPAD_8)
307                                return UP;
308                        else if (keycode == Keys.DOWN || keycode == Keys.DPAD_DOWN || keycode == Keys.NUMPAD_2)
309                                return DOWN;
310                        else if (keycode == Keys.PAGE_UP)
311                                return PAGE_UP;
312                        else if (keycode == Keys.PAGE_DOWN)
313                                return PAGE_DOWN;
314                        else if (vim) {
315                                if (keycode == Keys.J)
316                                        return DOWN;
317                                else if (keycode == Keys.K)
318                                        return UP;
319                                else
320                                        return null;
321                        } else
322                                return null;
323                }
324        }
325
326}