• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.scenes.scene2d.ui;
18 
19 import com.badlogic.gdx.graphics.Camera;
20 import com.badlogic.gdx.graphics.Color;
21 import com.badlogic.gdx.graphics.OrthographicCamera;
22 import com.badlogic.gdx.graphics.g2d.Batch;
23 import com.badlogic.gdx.graphics.g2d.BitmapFont;
24 import com.badlogic.gdx.math.Vector2;
25 import com.badlogic.gdx.scenes.scene2d.Actor;
26 import com.badlogic.gdx.scenes.scene2d.InputEvent;
27 import com.badlogic.gdx.scenes.scene2d.InputListener;
28 import com.badlogic.gdx.scenes.scene2d.Stage;
29 import com.badlogic.gdx.scenes.scene2d.Touchable;
30 import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle;
31 import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
32 import com.badlogic.gdx.utils.Align;
33 
34 /** A table that can be dragged and act as a modal window. The top padding is used as the window's title height.
35  * <p>
36  * The preferred size of a window is the preferred size of the title text and the children as laid out by the table. After adding
37  * children to the window, it can be convenient to call {@link #pack()} to size the window to the size of the children.
38  * @author Nathan Sweet */
39 public class Window extends Table {
40 	static private final Vector2 tmpPosition = new Vector2();
41 	static private final Vector2 tmpSize = new Vector2();
42 	static private final int MOVE = 1 << 5;
43 
44 	private WindowStyle style;
45 	boolean isMovable = true, isModal, isResizable;
46 	int resizeBorder = 8;
47 	boolean dragging;
48 	boolean keepWithinStage = true;
49 	Label titleLabel;
50 	Table titleTable;
51 	boolean drawTitleTable;
52 
Window(String title, Skin skin)53 	public Window (String title, Skin skin) {
54 		this(title, skin.get(WindowStyle.class));
55 		setSkin(skin);
56 	}
57 
Window(String title, Skin skin, String styleName)58 	public Window (String title, Skin skin, String styleName) {
59 		this(title, skin.get(styleName, WindowStyle.class));
60 		setSkin(skin);
61 	}
62 
Window(String title, WindowStyle style)63 	public Window (String title, WindowStyle style) {
64 		if (title == null) throw new IllegalArgumentException("title cannot be null.");
65 		setTouchable(Touchable.enabled);
66 		setClip(true);
67 
68 		titleLabel = new Label(title, new LabelStyle(style.titleFont, style.titleFontColor));
69 		titleLabel.setEllipsis(true);
70 
71 		titleTable = new Table() {
72 			public void draw (Batch batch, float parentAlpha) {
73 				if (drawTitleTable) super.draw(batch, parentAlpha);
74 			}
75 		};
76 		titleTable.add(titleLabel).expandX().fillX().minWidth(0);
77 		addActor(titleTable);
78 
79 		setStyle(style);
80 		setWidth(150);
81 		setHeight(150);
82 
83 		addCaptureListener(new InputListener() {
84 			public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
85 				toFront();
86 				return false;
87 			}
88 		});
89 		addListener(new InputListener() {
90 			int edge;
91 			float startX, startY, lastX, lastY;
92 
93 			public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
94 				if (button == 0) {
95 					int border = resizeBorder;
96 					float width = getWidth(), height = getHeight();
97 					edge = 0;
98 					if (isResizable && x >= 0 && x < width && y >= 0 && y < height) {
99 						if (x < border) edge |= Align.left;
100 						if (x > width - border) edge |= Align.right;
101 						if (y < border) edge |= Align.bottom;
102 						if (y > height - border) edge |= Align.top;
103 						if (edge != 0) border += 25;
104 						if (x < border) edge |= Align.left;
105 						if (x > width - border) edge |= Align.right;
106 						if (y < border) edge |= Align.bottom;
107 						if (y > height - border) edge |= Align.top;
108 					}
109 					if (isMovable && edge == 0 && y <= height && y >= height - getPadTop() && x >= 0 && x <= width) edge = MOVE;
110 					dragging = edge != 0;
111 					startX = x;
112 					startY = y;
113 					lastX = x - width;
114 					lastY = y - height;
115 				}
116 				return edge != 0 || isModal;
117 			}
118 
119 			public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
120 				dragging = false;
121 			}
122 
123 			public void touchDragged (InputEvent event, float x, float y, int pointer) {
124 				if (!dragging) return;
125 				float width = getWidth(), height = getHeight();
126 				float windowX = getX(), windowY = getY();
127 
128 				float minWidth = getMinWidth(), maxWidth = getMaxWidth();
129 				float minHeight = getMinHeight(), maxHeight = getMaxHeight();
130 				Stage stage = getStage();
131 				boolean clampPosition = keepWithinStage && getParent() == stage.getRoot();
132 
133 				if ((edge & MOVE) != 0) {
134 					float amountX = x - startX, amountY = y - startY;
135 					windowX += amountX;
136 					windowY += amountY;
137 				}
138 				if ((edge & Align.left) != 0) {
139 					float amountX = x - startX;
140 					if (width - amountX < minWidth) amountX = -(minWidth - width);
141 					if (clampPosition && windowX + amountX < 0) amountX = -windowX;
142 					width -= amountX;
143 					windowX += amountX;
144 				}
145 				if ((edge & Align.bottom) != 0) {
146 					float amountY = y - startY;
147 					if (height - amountY < minHeight) amountY = -(minHeight - height);
148 					if (clampPosition && windowY + amountY < 0) amountY = -windowY;
149 					height -= amountY;
150 					windowY += amountY;
151 				}
152 				if ((edge & Align.right) != 0) {
153 					float amountX = x - lastX - width;
154 					if (width + amountX < minWidth) amountX = minWidth - width;
155 					if (clampPosition && windowX + width + amountX > stage.getWidth()) amountX = stage.getWidth() - windowX - width;
156 					width += amountX;
157 				}
158 				if ((edge & Align.top) != 0) {
159 					float amountY = y - lastY - height;
160 					if (height + amountY < minHeight) amountY = minHeight - height;
161 					if (clampPosition && windowY + height + amountY > stage.getHeight())
162 						amountY = stage.getHeight() - windowY - height;
163 					height += amountY;
164 				}
165 				setBounds(Math.round(windowX), Math.round(windowY), Math.round(width), Math.round(height));
166 			}
167 
168 			public boolean mouseMoved (InputEvent event, float x, float y) {
169 				return isModal;
170 			}
171 
172 			public boolean scrolled (InputEvent event, float x, float y, int amount) {
173 				return isModal;
174 			}
175 
176 			public boolean keyDown (InputEvent event, int keycode) {
177 				return isModal;
178 			}
179 
180 			public boolean keyUp (InputEvent event, int keycode) {
181 				return isModal;
182 			}
183 
184 			public boolean keyTyped (InputEvent event, char character) {
185 				return isModal;
186 			}
187 		});
188 	}
189 
setStyle(WindowStyle style)190 	public void setStyle (WindowStyle style) {
191 		if (style == null) throw new IllegalArgumentException("style cannot be null.");
192 		this.style = style;
193 		setBackground(style.background);
194 		titleLabel.setStyle(new LabelStyle(style.titleFont, style.titleFontColor));
195 		invalidateHierarchy();
196 	}
197 
198 	/** Returns the window's style. Modifying the returned style may not have an effect until {@link #setStyle(WindowStyle)} is
199 	 * called. */
getStyle()200 	public WindowStyle getStyle () {
201 		return style;
202 	}
203 
keepWithinStage()204 	void keepWithinStage () {
205 		if (!keepWithinStage) return;
206 		Stage stage = getStage();
207 		Camera camera = stage.getCamera();
208 		if (camera instanceof OrthographicCamera) {
209 			OrthographicCamera orthographicCamera = (OrthographicCamera)camera;
210 			float parentWidth = stage.getWidth();
211 			float parentHeight = stage.getHeight();
212 			if (getX(Align.right) - camera.position.x > parentWidth / 2 / orthographicCamera.zoom)
213 				setPosition(camera.position.x + parentWidth / 2 / orthographicCamera.zoom, getY(Align.right), Align.right);
214 			if (getX(Align.left) - camera.position.x < -parentWidth / 2 / orthographicCamera.zoom)
215 				setPosition(camera.position.x - parentWidth / 2 / orthographicCamera.zoom, getY(Align.left), Align.left);
216 			if (getY(Align.top) - camera.position.y > parentHeight / 2 / orthographicCamera.zoom)
217 				setPosition(getX(Align.top), camera.position.y + parentHeight / 2 / orthographicCamera.zoom, Align.top);
218 			if (getY(Align.bottom) - camera.position.y < -parentHeight / 2 / orthographicCamera.zoom)
219 				setPosition(getX(Align.bottom), camera.position.y - parentHeight / 2 / orthographicCamera.zoom, Align.bottom);
220 		} else if (getParent() == stage.getRoot()) {
221 			float parentWidth = stage.getWidth();
222 			float parentHeight = stage.getHeight();
223 			if (getX() < 0) setX(0);
224 			if (getRight() > parentWidth) setX(parentWidth - getWidth());
225 			if (getY() < 0) setY(0);
226 			if (getTop() > parentHeight) setY(parentHeight - getHeight());
227 		}
228 	}
229 
draw(Batch batch, float parentAlpha)230 	public void draw (Batch batch, float parentAlpha) {
231 		Stage stage = getStage();
232 		if (stage.getKeyboardFocus() == null) stage.setKeyboardFocus(this);
233 
234 		keepWithinStage();
235 
236 		if (style.stageBackground != null) {
237 			stageToLocalCoordinates(tmpPosition.set(0, 0));
238 			stageToLocalCoordinates(tmpSize.set(stage.getWidth(), stage.getHeight()));
239 			drawStageBackground(batch, parentAlpha, getX() + tmpPosition.x, getY() + tmpPosition.y, getX() + tmpSize.x, getY()
240 				+ tmpSize.y);
241 		}
242 
243 		super.draw(batch, parentAlpha);
244 	}
245 
drawStageBackground(Batch batch, float parentAlpha, float x, float y, float width, float height)246 	protected void drawStageBackground (Batch batch, float parentAlpha, float x, float y, float width, float height) {
247 		Color color = getColor();
248 		batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
249 		style.stageBackground.draw(batch, x, y, width, height);
250 	}
251 
drawBackground(Batch batch, float parentAlpha, float x, float y)252 	protected void drawBackground (Batch batch, float parentAlpha, float x, float y) {
253 		super.drawBackground(batch, parentAlpha, x, y);
254 
255 		// Manually draw the title table before clipping is done.
256 		titleTable.getColor().a = getColor().a;
257 		float padTop = getPadTop(), padLeft = getPadLeft();
258 		titleTable.setSize(getWidth() - padLeft - getPadRight(), padTop);
259 		titleTable.setPosition(padLeft, getHeight() - padTop);
260 		drawTitleTable = true;
261 		titleTable.draw(batch, parentAlpha);
262 		drawTitleTable = false; // Avoid drawing the title table again in drawChildren.
263 	}
264 
hit(float x, float y, boolean touchable)265 	public Actor hit (float x, float y, boolean touchable) {
266 		Actor hit = super.hit(x, y, touchable);
267 		if (hit == null && isModal && (!touchable || getTouchable() == Touchable.enabled)) return this;
268 		float height = getHeight();
269 		if (hit == null || hit == this) return hit;
270 		if (y <= height && y >= height - getPadTop() && x >= 0 && x <= getWidth()) {
271 			// Hit the title bar, don't use the hit child if it is in the Window's table.
272 			Actor current = hit;
273 			while (current.getParent() != this)
274 				current = current.getParent();
275 			if (getCell(current) != null) return this;
276 		}
277 		return hit;
278 	}
279 
isMovable()280 	public boolean isMovable () {
281 		return isMovable;
282 	}
283 
setMovable(boolean isMovable)284 	public void setMovable (boolean isMovable) {
285 		this.isMovable = isMovable;
286 	}
287 
isModal()288 	public boolean isModal () {
289 		return isModal;
290 	}
291 
setModal(boolean isModal)292 	public void setModal (boolean isModal) {
293 		this.isModal = isModal;
294 	}
295 
setKeepWithinStage(boolean keepWithinStage)296 	public void setKeepWithinStage (boolean keepWithinStage) {
297 		this.keepWithinStage = keepWithinStage;
298 	}
299 
isResizable()300 	public boolean isResizable () {
301 		return isResizable;
302 	}
303 
setResizable(boolean isResizable)304 	public void setResizable (boolean isResizable) {
305 		this.isResizable = isResizable;
306 	}
307 
setResizeBorder(int resizeBorder)308 	public void setResizeBorder (int resizeBorder) {
309 		this.resizeBorder = resizeBorder;
310 	}
311 
isDragging()312 	public boolean isDragging () {
313 		return dragging;
314 	}
315 
getPrefWidth()316 	public float getPrefWidth () {
317 		return Math.max(super.getPrefWidth(), titleLabel.getPrefWidth() + getPadLeft() + getPadRight());
318 	}
319 
getTitleTable()320 	public Table getTitleTable () {
321 		return titleTable;
322 	}
323 
getTitleLabel()324 	public Label getTitleLabel () {
325 		return titleLabel;
326 	}
327 
328 	/** The style for a window, see {@link Window}.
329 	 * @author Nathan Sweet */
330 	static public class WindowStyle {
331 		/** Optional. */
332 		public Drawable background;
333 		public BitmapFont titleFont;
334 		/** Optional. */
335 		public Color titleFontColor = new Color(1, 1, 1, 1);
336 		/** Optional. */
337 		public Drawable stageBackground;
338 
WindowStyle()339 		public WindowStyle () {
340 		}
341 
WindowStyle(BitmapFont titleFont, Color titleFontColor, Drawable background)342 		public WindowStyle (BitmapFont titleFont, Color titleFontColor, Drawable background) {
343 			this.background = background;
344 			this.titleFont = titleFont;
345 			this.titleFontColor.set(titleFontColor);
346 		}
347 
WindowStyle(WindowStyle style)348 		public WindowStyle (WindowStyle style) {
349 			this.background = style.background;
350 			this.titleFont = style.titleFont;
351 			this.titleFontColor = new Color(style.titleFontColor);
352 		}
353 	}
354 }
355