• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.badlogic.gdx.backends.gwt.widgets;
18 
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.Map;
22 
23 import com.google.gwt.event.logical.shared.ResizeEvent;
24 import com.google.gwt.event.logical.shared.ResizeHandler;
25 import com.google.gwt.event.shared.HandlerRegistration;
26 import com.google.gwt.user.client.Timer;
27 import com.google.gwt.user.client.Window;
28 import com.google.gwt.user.client.WindowResizeListener;
29 
30 /** A collection of {@link ResizableWidget} that periodically checks the outer dimensions of a widget and redraws it as necessary.
31  * Every {@link ResizableWidgetCollection} uses a timer, so consider the cost when adding one.
32  *
33  * Typically, a {@link ResizableWidgetCollection} is only needed if you expect your widgets to resize based on window resizing or
34  * other events. Fixed sized Widgets do not need to be added to a {@link ResizableWidgetCollection} as they cannot be resized. */
35 public class ResizableWidgetCollection implements ResizeHandler, Iterable<ResizableWidget> {
36 	/** Information about a widgets size. */
37 	static class ResizableWidgetInfo {
38 
39 		private ResizableWidget widget;
40 		private int curOffsetHeight = 0;
41 		private int curOffsetWidth = 0;
42 		private int curClientHeight = 0;
43 		private int curClientWidth = 0;
44 
45 		/** Constructor.
46 		 *
47 		 * @param widget the widget that will be monitored */
ResizableWidgetInfo(ResizableWidget widget)48 		public ResizableWidgetInfo (ResizableWidget widget) {
49 			this.widget = widget;
50 			updateSizes();
51 		}
52 
getClientHeight()53 		public int getClientHeight () {
54 			return curClientHeight;
55 		}
56 
getClientWidth()57 		public int getClientWidth () {
58 			return curClientWidth;
59 		}
60 
getOffsetHeight()61 		public int getOffsetHeight () {
62 			return curOffsetHeight;
63 		}
64 
getOffsetWidth()65 		public int getOffsetWidth () {
66 			return curOffsetWidth;
67 		}
68 
69 		/** Update the current sizes.
70 		 *
71 		 * @return true if the sizes changed, false if not. */
updateSizes()72 		public boolean updateSizes () {
73 			int offsetWidth = widget.getElement().getOffsetWidth();
74 			int offsetHeight = widget.getElement().getOffsetHeight();
75 			int clientWidth = widget.getElement().getClientWidth();
76 			int clientHeight = widget.getElement().getClientHeight();
77 			if (offsetWidth != curOffsetWidth || offsetHeight != curOffsetHeight || clientWidth != curClientWidth
78 				|| clientHeight != curClientHeight) {
79 				this.curOffsetWidth = offsetWidth;
80 				this.curOffsetHeight = offsetHeight;
81 				this.curClientWidth = clientWidth;
82 				this.curClientHeight = clientHeight;
83 				return true;
84 			}
85 
86 			return false;
87 		}
88 	}
89 
90 	/** The default delay between resize checks in milliseconds. */
91 	private static final int DEFAULT_RESIZE_CHECK_DELAY = 400;
92 
93 	/** A static {@link ResizableWidgetCollection} that can be used in most cases. */
94 	private static ResizableWidgetCollection staticCollection = null;
95 
96 	/** Get the globally accessible {@link ResizableWidgetCollection}. In most cases, the global collection can be used for all
97 	 * {@link ResizableWidget}s.
98 	 *
99 	 * @return the global {@link ResizableWidgetCollection} */
get()100 	public static ResizableWidgetCollection get () {
101 		if (staticCollection == null) {
102 			staticCollection = new ResizableWidgetCollection();
103 		}
104 		return staticCollection;
105 	}
106 
107 	/** The timer used to periodically compare the dimensions of elements to their old dimensions. */
108 	private Timer resizeCheckTimer = new Timer() {
109 		@Override
110 		public void run () {
111 			// Ignore changes that result from window resize events
112 			if (windowHeight != Window.getClientHeight() || windowWidth != Window.getClientWidth()) {
113 				windowHeight = Window.getClientHeight();
114 				windowWidth = Window.getClientWidth();
115 				schedule(resizeCheckDelay);
116 				return;
117 			}
118 
119 			// Look for elements that have new dimensions
120 			checkWidgetSize();
121 
122 			// Start checking again
123 			if (resizeCheckingEnabled) {
124 				schedule(resizeCheckDelay);
125 			}
126 		}
127 	};
128 
129 	/** A hash map of the resizable widgets this collection is checking. */
130 	private Map<ResizableWidget, ResizableWidgetInfo> widgets = new HashMap<ResizableWidget, ResizableWidgetInfo>();
131 
132 	/** The current window height. */
133 	int windowHeight = 0;
134 
135 	/** The current window width. */
136 	int windowWidth = 0;
137 
138 	/** The hook used to remove the window handler. */
139 	private HandlerRegistration windowHandler;
140 
141 	/** The delay between resize checks. */
142 	int resizeCheckDelay = DEFAULT_RESIZE_CHECK_DELAY;
143 
144 	/** A boolean indicating that resize checking should run. */
145 	boolean resizeCheckingEnabled;
146 
147 	/** Create a ResizableWidget. */
ResizableWidgetCollection()148 	public ResizableWidgetCollection () {
149 		this(DEFAULT_RESIZE_CHECK_DELAY);
150 	}
151 
152 	/** Constructor.
153 	 *
154 	 * @param resizeCheckingEnabled false to disable resize checking */
ResizableWidgetCollection(boolean resizeCheckingEnabled)155 	public ResizableWidgetCollection (boolean resizeCheckingEnabled) {
156 		this(DEFAULT_RESIZE_CHECK_DELAY, resizeCheckingEnabled);
157 	}
158 
159 	/** Constructor.
160 	 *
161 	 * @param resizeCheckDelay the delay between checks in milliseconds */
ResizableWidgetCollection(int resizeCheckDelay)162 	public ResizableWidgetCollection (int resizeCheckDelay) {
163 		this(resizeCheckDelay, true);
164 	}
165 
166 	/** Constructor. */
ResizableWidgetCollection(int resizeCheckDelay, boolean resizeCheckingEnabled)167 	protected ResizableWidgetCollection (int resizeCheckDelay, boolean resizeCheckingEnabled) {
168 		setResizeCheckDelay(resizeCheckDelay);
169 		setResizeCheckingEnabled(resizeCheckingEnabled);
170 	}
171 
172 	/** Add a resizable widget to the collection.
173 	 *
174 	 * @param widget the resizable widget to add */
add(ResizableWidget widget)175 	public void add (ResizableWidget widget) {
176 		widgets.put(widget, new ResizableWidgetInfo(widget));
177 	}
178 
179 	/** Check to see if any Widgets have been resized and call their handlers appropriately. */
checkWidgetSize()180 	public void checkWidgetSize () {
181 		for (Map.Entry<ResizableWidget, ResizableWidgetInfo> entry : widgets.entrySet()) {
182 			ResizableWidget widget = entry.getKey();
183 			ResizableWidgetInfo info = entry.getValue();
184 
185 			// Call the onResize method only if the widget is attached
186 			if (info.updateSizes()) {
187 				// Check that the offset width and height are greater than 0.
188 				if (info.getOffsetWidth() > 0 && info.getOffsetHeight() > 0 && widget.isAttached()) {
189 					// Send the client dimensions, which is the space available for
190 					// rendering.
191 					widget.onResize(info.getOffsetWidth(), info.getOffsetHeight());
192 				}
193 			}
194 		}
195 	}
196 
197 	/** Get the delay between resize checks in milliseconds.
198 	 *
199 	 * @return the resize check delay */
getResizeCheckDelay()200 	public int getResizeCheckDelay () {
201 		return resizeCheckDelay;
202 	}
203 
204 	/** Check whether or not resize checking is enabled.
205 	 *
206 	 * @return true is resize checking is enabled */
isResizeCheckingEnabled()207 	public boolean isResizeCheckingEnabled () {
208 		return resizeCheckingEnabled;
209 	}
210 
iterator()211 	public Iterator<ResizableWidget> iterator () {
212 		return widgets.keySet().iterator();
213 	}
214 
215 	/** Remove a {@link ResizableWidget} from the collection.
216 	 *
217 	 * @param widget the widget to remove */
remove(ResizableWidget widget)218 	public void remove (ResizableWidget widget) {
219 		widgets.remove(widget);
220 	}
221 
222 	/** Set the delay between resize checks in milliseconds.
223 	 *
224 	 * @param resizeCheckDelay the new delay */
setResizeCheckDelay(int resizeCheckDelay)225 	public void setResizeCheckDelay (int resizeCheckDelay) {
226 		this.resizeCheckDelay = resizeCheckDelay;
227 	}
228 
229 	/** Set whether or not resize checking is enabled. If disabled, elements will still be resized on window events, but the timer
230 	 * will not check their dimensions periodically.
231 	 *
232 	 * @param enabled true to enable the resize checking timer */
setResizeCheckingEnabled(boolean enabled)233 	public void setResizeCheckingEnabled (boolean enabled) {
234 		if (enabled && !resizeCheckingEnabled) {
235 			resizeCheckingEnabled = true;
236 			if (windowHandler == null) {
237 				windowHandler = Window.addResizeHandler(this);
238 			}
239 			resizeCheckTimer.schedule(resizeCheckDelay);
240 		} else if (!enabled && resizeCheckingEnabled) {
241 			resizeCheckingEnabled = false;
242 			if (windowHandler != null) {
243 				windowHandler.removeHandler();
244 				windowHandler = null;
245 			}
246 			resizeCheckTimer.cancel();
247 		}
248 	}
249 
250 	/** Inform the {@link ResizableWidgetCollection} that the size of a widget has changed and already been redrawn. This will
251 	 * prevent the widget from being redrawn on the next loop.
252 	 *
253 	 * @param widget the widget's size that changed */
updateWidgetSize(ResizableWidget widget)254 	public void updateWidgetSize (ResizableWidget widget) {
255 		if (!widget.isAttached()) {
256 			return;
257 		}
258 
259 		ResizableWidgetInfo info = widgets.get(widget);
260 		if (info != null) {
261 			info.updateSizes();
262 		}
263 	}
264 
265 	/** Called when the browser window is resized.
266 	 *
267 	 */
268 	@Override
onResize(ResizeEvent event)269 	public void onResize (ResizeEvent event) {
270 		checkWidgetSize();
271 	}
272 
273 }
274