com.badlogic.gdx.backends.gwt.GwtApplication Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gdx-backend-gwt Show documentation
Show all versions of gdx-backend-gwt Show documentation
Android/Desktop/iOS/HTML5 game development framework.
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.backends.gwt;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.ApplicationLogger;
import com.badlogic.gdx.Audio;
import com.badlogic.gdx.Files;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Graphics;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.LifecycleListener;
import com.badlogic.gdx.Net;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.backends.gwt.preloader.Preloader;
import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderCallback;
import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderState;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Clipboard;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.TimeUtils;
import com.google.gwt.animation.client.AnimationScheduler;
import com.google.gwt.animation.client.AnimationScheduler.AnimationCallback;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.CanvasElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** Implementation of an {@link Application} based on GWT. Clients have to override {@link #getConfig()} and
* {@link #createApplicationListener()}. Clients can override the default loading screen via
* {@link #getPreloaderCallback()} and implement any loading screen drawing via GWT widgets.
* @author mzechner */
public abstract class GwtApplication implements EntryPoint, Application {
private ApplicationListener listener;
GwtApplicationConfiguration config;
GwtGraphics graphics;
private GwtAudio audio;
private GwtInput input;
private GwtNet net;
private Panel root = null;
protected TextArea log = null;
private int logLevel = LOG_ERROR;
private ApplicationLogger applicationLogger;
private Array runnables = new Array();
private Array runnablesHelper = new Array();
private Array lifecycleListeners = new Array();
private int lastWidth;
private int lastHeight;
Preloader preloader;
private static AgentInfo agentInfo;
private ObjectMap prefs = new ObjectMap();
private Clipboard clipboard;
LoadingListener loadingListener;
/** @return the configuration for the {@link GwtApplication}. */
public abstract GwtApplicationConfiguration getConfig ();
public String getPreloaderBaseURL()
{
return GWT.getHostPageBaseURL() + "assets/";
}
@Override
public ApplicationListener getApplicationListener() {
return listener;
}
public abstract ApplicationListener createApplicationListener();
@Override
public void onModuleLoad () {
GwtApplication.agentInfo = computeAgentInfo();
this.listener = createApplicationListener();
this.config = getConfig();
setApplicationLogger(new GwtApplicationLogger(this.config.log));
if (config.rootPanel != null) {
this.root = config.rootPanel;
} else {
Element element = Document.get().getElementById("embed-" + GWT.getModuleName());
int width ;
int height;
if (!config.isFixedSizeApplication()) {
// resizable application
width = Window.getClientWidth() - config.padHorizontal;
height = Window.getClientHeight() - config.padVertical;
// resizeable application does not need to take the native screen density into
// account here - the panel's size is set to logical pixels
Window.enableScrolling(false);
Window.setMargin("0");
Window.addResizeHandler(new ResizeListener());
} else {
// fixed size application
width = config.width;
height = config.height;
if (config.usePhysicalPixels) {
double density = GwtGraphics.getNativeScreenDensity();
width = (int) (width / density);
height = (int) (height / density);
}
}
if (element == null) {
VerticalPanel panel = new VerticalPanel();
panel.setWidth("" + width + "px");
panel.setHeight("" + height + "px");
panel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
panel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
RootPanel.get().add(panel);
RootPanel.get().setWidth("" + width + "px");
RootPanel.get().setHeight("" + height + "px");
this.root = panel;
} else {
VerticalPanel panel = new VerticalPanel();
panel.setWidth("" + width + "px");
panel.setHeight("" + height + "px");
panel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
panel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
element.appendChild(panel.getElement());
root = panel;
}
}
preloadAssets();
}
void preloadAssets() {
final PreloaderCallback callback = getPreloaderCallback();
preloader = createPreloader();
preloader.preload("assets.txt", new PreloaderCallback() {
@Override
public void error (String file) {
callback.error(file);
}
@Override
public void update (PreloaderState state) {
callback.update(state);
if (state.hasEnded()) {
getRootPanel().clear();
if(loadingListener != null)
loadingListener.beforeSetup();
setupLoop();
addEventListeners();
if(loadingListener != null)
loadingListener.afterSetup();
}
}
});
}
/**
* Override this method to return a custom widget informing the that their browser lacks support of WebGL.
*
* @return Widget to display when WebGL is not supported.
*/
public Widget getNoWebGLSupportWidget() {
return new Label("Sorry, your browser doesn't seem to support WebGL");
}
void setupLoop () {
Gdx.app = this;
// setup modules
try {
graphics = new GwtGraphics(root, config);
} catch (Throwable e) {
root.clear();
root.add(getNoWebGLSupportWidget());
return;
}
lastWidth = graphics.getWidth();
lastHeight = graphics.getHeight();
Gdx.graphics = graphics;
Gdx.gl20 = graphics.getGL20();
Gdx.gl = Gdx.gl20;
if(config.disableAudio) {
audio = null;
} else {
audio = createAudio();
}
Gdx.audio = audio;
Gdx.files = new GwtFiles(preloader);
this.input = createInput(graphics.canvas, this.config);
Gdx.input = this.input;
this.net = new GwtNet(config);
Gdx.net = this.net;
this.clipboard = new GwtClipboard();
updateLogLabelSize();
// tell listener about app creation
try {
listener.create();
listener.resize(graphics.getWidth(), graphics.getHeight());
} catch (Throwable t) {
error("GwtApplication", "exception: " + t.getMessage(), t);
t.printStackTrace();
throw new RuntimeException(t);
}
AnimationScheduler.get().requestAnimationFrame(new AnimationCallback() {
@Override
public void execute (double timestamp) {
try {
mainLoop();
} catch (Throwable t) {
error("GwtApplication", "exception: " + t.getMessage(), t);
throw new RuntimeException(t);
}
AnimationScheduler.get().requestAnimationFrame(this, graphics.canvas);
}
}, graphics.canvas);
}
void mainLoop() {
graphics.update();
if (Gdx.graphics.getWidth() != lastWidth || Gdx.graphics.getHeight() != lastHeight) {
lastWidth = graphics.getWidth();
lastHeight = graphics.getHeight();
Gdx.gl.glViewport(0, 0, lastWidth, lastHeight);
GwtApplication.this.listener.resize(lastWidth, lastHeight);
}
runnablesHelper.addAll(runnables);
runnables.clear();
for (int i = 0; i < runnablesHelper.size; i++) {
runnablesHelper.get(i).run();
}
runnablesHelper.clear();
graphics.frameId++;
listener.render();
input.reset();
}
public Panel getRootPanel () {
return root;
}
long loadStart = TimeUtils.nanoTime();
public Preloader createPreloader() {
return new Preloader(getPreloaderBaseURL());
}
/**
* This procedure creates the preloader panel and returns a preloader callback to update it.
*
* You can override it to construct your own preloader animation. You can adjust the progress bar
* colors to your needs by overriding {@link #adjustMeterPanel(Panel, Style)}.
*
* Example to use an own image (width should be around 300px) placed in webapp folder:
*
* public PreloaderCallback getPreloaderCallback () {
* return createPreloaderPanel(GWT.getHostPageBaseURL() + "logo_preload.png");
* }
*
* @return PreloaderCallback to use for preload()
*/
public PreloaderCallback getPreloaderCallback () {
return createPreloaderPanel(GWT.getModuleBaseURL() + "logo.png");
}
protected PreloaderCallback createPreloaderPanel(String logoUrl) {
final Panel preloaderPanel = new VerticalPanel();
preloaderPanel.setStyleName("gdx-preloader");
final Image logo = new Image(logoUrl);
logo.setStyleName("logo");
preloaderPanel.add(logo);
final Panel meterPanel = new SimplePanel();
final InlineHTML meter = new InlineHTML();
final Style meterStyle = meter.getElement().getStyle();
meterStyle.setWidth(0, Unit.PCT);
adjustMeterPanel(meterPanel, meterStyle);
meterPanel.add(meter);
preloaderPanel.add(meterPanel);
getRootPanel().add(preloaderPanel);
return new PreloaderCallback() {
@Override
public void error (String file) {
System.out.println("error: " + file);
}
@Override
public void update (PreloaderState state) {
meterStyle.setWidth(100f * state.getProgress(), Unit.PCT);
}
};
}
/**
* called by {@link #createPreloaderPanel(String)} for overriding purpose.
* override this method to adjust the styles of the loading progress bar. Example for changing
* the bars padding and color:
*
* meterPanel.setStyleName("gdx-meter");
* meterPanel.addStyleName("nostripes");
* Style meterPanelStyle = meterPanel.getElement().getStyle();
* meterPanelStyle.setProperty("backgroundColor", "#ff0000");
* meterPanelStyle.setProperty("padding", "0px");
* meterStyle.setProperty("backgroundColor", "#ffffff");
* meterStyle.setProperty("backgroundImage", "none");
*
*/
protected void adjustMeterPanel(Panel meterPanel, Style meterStyle) {
meterPanel.setStyleName("gdx-meter");
meterPanel.addStyleName("red");
}
@Override
public Graphics getGraphics () {
return graphics;
}
@Override
public Audio getAudio () {
return audio;
}
@Override
public Input getInput () {
return Gdx.input;
}
@Override
public Files getFiles () {
return Gdx.files;
}
@Override
public Net getNet() {
return Gdx.net;
}
private void updateLogLabelSize () {
if (log != null) {
if (graphics != null) {
log.setSize(graphics.getWidth() + "px", "200px");
} else {
log.setSize("400px", "200px"); // Should not happen at this point, use dummy value
}
}
}
@Override
public void log (String tag, String message) {
if (logLevel >= LOG_INFO) getApplicationLogger().log(tag, message);
}
@Override
public void log (String tag, String message, Throwable exception) {
if (logLevel >= LOG_INFO) getApplicationLogger().log(tag, message, exception);
}
@Override
public void error (String tag, String message) {
if (logLevel >= LOG_ERROR) getApplicationLogger().error(tag, message);
}
@Override
public void error (String tag, String message, Throwable exception) {
if (logLevel >= LOG_ERROR) getApplicationLogger().error(tag, message, exception);
}
@Override
public void debug (String tag, String message) {
if (logLevel >= LOG_DEBUG) getApplicationLogger().debug(tag, message);
}
@Override
public void debug (String tag, String message, Throwable exception) {
if (logLevel >= LOG_DEBUG) getApplicationLogger().debug(tag, message, exception);
}
@Override
public void setLogLevel (int logLevel) {
this.logLevel = logLevel;
}
@Override
public int getLogLevel() {
return logLevel;
}
@Override
public void setApplicationLogger (ApplicationLogger applicationLogger) {
this.applicationLogger = applicationLogger;
}
@Override
public ApplicationLogger getApplicationLogger () {
return applicationLogger;
}
@Override
public ApplicationType getType () {
return ApplicationType.WebGL;
}
@Override
public int getVersion () {
return 0;
}
@Override
public long getJavaHeap () {
return 0;
}
@Override
public long getNativeHeap () {
return 0;
}
@Override
public Preferences getPreferences (String name) {
Preferences pref = prefs.get(name);
if (pref == null) {
pref = new GwtPreferences(name);
prefs.put(name, pref);
}
return pref;
}
@Override
public Clipboard getClipboard () {
return clipboard;
}
@Override
public void postRunnable (Runnable runnable) {
runnables.add(runnable);
}
@Override
public void exit () {
}
protected GwtAudio createAudio () {
return new DefaultGwtAudio();
}
protected GwtInput createInput(CanvasElement canvas, GwtApplicationConfiguration config) {
return new DefaultGwtInput(canvas, config);
}
/**
* @return {@code true} if application runs on a mobile device
*/
public static boolean isMobileDevice() {
// RegEx pattern from detectmobilebrowsers.com (public domain)
String pattern = "(android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec" +
"|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)" +
"i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)" +
"|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(Window.Navigator.getUserAgent().toLowerCase());
return m.matches();
}
/** Contains precomputed information on the user-agent. Useful for dealing with browser and OS behavioral differences. Kindly
* borrowed from PlayN */
public static AgentInfo agentInfo () {
return agentInfo;
}
/** kindly borrowed from PlayN **/
private static native AgentInfo computeAgentInfo () /*-{
var userAgent = navigator.userAgent.toLowerCase();
return {
// browser type flags
isFirefox : userAgent.indexOf("firefox") != -1,
isChrome : userAgent.indexOf("chrome") != -1,
isSafari : userAgent.indexOf("safari") != -1,
isOpera : userAgent.indexOf("opera") != -1,
isIE : userAgent.indexOf("msie") != -1 || userAgent.indexOf("trident") != -1,
// OS type flags
isMacOS : userAgent.indexOf("mac") != -1,
isLinux : userAgent.indexOf("linux") != -1,
isWindows : userAgent.indexOf("win") != -1
};
}-*/;
/** Returned by {@link #agentInfo}. Kindly borrowed from PlayN. */
public static class AgentInfo extends JavaScriptObject {
public final native boolean isFirefox () /*-{
return this.isFirefox;
}-*/;
public final native boolean isChrome () /*-{
return this.isChrome;
}-*/;
public final native boolean isSafari () /*-{
return this.isSafari;
}-*/;
public final native boolean isOpera () /*-{
return this.isOpera;
}-*/;
public final native boolean isIE () /*-{
return this.isIE;
}-*/;
public final native boolean isMacOS () /*-{
return this.isMacOS;
}-*/;
public final native boolean isLinux () /*-{
return this.isLinux;
}-*/;
public final native boolean isWindows () /*-{
return this.isWindows;
}-*/;
protected AgentInfo () {
}
}
public String getBaseUrl () {
return preloader.baseUrl;
}
public Preloader getPreloader () {
return preloader;
}
public CanvasElement getCanvasElement(){
return graphics.canvas;
}
public LoadingListener getLoadingListener () {
return loadingListener;
}
public void setLoadingListener (LoadingListener loadingListener) {
this.loadingListener = loadingListener;
}
@Override
public void addLifecycleListener (LifecycleListener listener) {
synchronized(lifecycleListeners) {
lifecycleListeners.add(listener);
}
}
@Override
public void removeLifecycleListener (LifecycleListener listener) {
synchronized(lifecycleListeners) {
lifecycleListeners.removeValue(listener, true);
}
}
native static public void consoleLog(String message) /*-{
console.log( "GWT: " + message );
}-*/;
private native void addEventListeners() /*-{
var self = this;
var eventName = null;
if ("hidden" in $doc) {
eventName = "visibilitychange"
} else if ("webkitHidden" in $doc) {
eventName = "webkitvisibilitychange"
} else if ("mozHidden" in $doc) {
eventName = "mozvisibilitychange"
} else if ("msHidden" in $doc) {
eventName = "msvisibilitychange"
}
if (eventName !== null) {
$doc.addEventListener(eventName, function(e) {
[email protected]::onVisibilityChange(Z)($doc['hidden'] !== true);
});
}
}-*/;
private void onVisibilityChange (boolean visible) {
if (visible) {
for (LifecycleListener listener : lifecycleListeners) {
listener.resume();
}
listener.resume();
} else {
for (LifecycleListener listener : lifecycleListeners) {
listener.pause();
}
listener.pause();
}
}
/**
* LoadingListener interface main purpose is to do some things before or after {@link GwtApplication#setupLoop()}
*/
public interface LoadingListener{
/**
* Method called before the setup
*/
public void beforeSetup();
/**
* Method called after the setup
*/
public void afterSetup();
}
/**
* ResizeListener called when browser window is resized
*/
class ResizeListener implements ResizeHandler {
@Override
public void onResize(ResizeEvent event) {
int width = event.getWidth() - config.padHorizontal;
int height = event.getHeight() - config.padVertical;
// ignore 0, 0
if (width == 0 || height == 0) {
return;
}
getRootPanel().setWidth("" + width + "px");
getRootPanel().setHeight("" + height + "px");
// while preloading and before setupLoop() is called, graphics are not initialized
if (graphics != null) {
// event calls us with logical pixel size, so if we use physical pixels internally,
// we need to convert them
if (config.usePhysicalPixels) {
double density = GwtGraphics.getNativeScreenDensity();
width = (int) (width * density);
height = (int) (height * density);
}
graphics.setCanvasSize(width, height);
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy