Created
December 19, 2013 16:06
-
-
Save eneveu/8041699 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.headexplodes.gwt.link; | |
import com.google.common.annotations.VisibleForTesting; | |
import com.google.gwt.dom.client.NativeEvent; | |
import com.google.gwt.event.dom.client.ClickEvent; | |
import com.google.gwt.event.dom.client.ClickHandler; | |
import com.google.gwt.inject.client.binder.GinBinder; | |
import com.google.gwt.place.shared.Place; | |
import com.google.gwt.place.shared.PlaceChangeRequestEvent; | |
import com.google.gwt.place.shared.PlaceController; | |
import com.google.gwt.place.shared.PlaceHistoryMapper; | |
import com.google.gwt.user.client.ui.Anchor; | |
import com.google.gwt.user.client.ui.Hyperlink; | |
import com.google.inject.Provider; | |
import javax.annotation.Nonnull; | |
import javax.annotation.Nullable; | |
import javax.inject.Inject; | |
import static com.google.common.base.Preconditions.checkArgument; | |
import static com.google.common.base.Preconditions.checkNotNull; | |
/** | |
* This widget lets us link to another Place within our application. Using an {@link Anchor} or an {@link Hyperlink} | |
* would cause problems with GWT's history management and more specifically with aborted {@link PlaceChangeRequestEvent}s. | |
* <p/> | |
* See Thomas Broyer's explanations in | |
* <a href="https://groups.google.com/forum/#!msg/google-web-toolkit/IpQQyeI1-Zc">this thread</a> | |
* and <a href="http://stackoverflow.com/a/7835854/142983">this StackOverflow answer</a> for more details. | |
* <p/> | |
* We set the {@code href} of the anchor to make sure browsers style it correctly (mouse pointer), but also to support | |
* opening the link in another tab / window. | |
*/ | |
public class PlaceHyperlink extends Anchor { | |
/** | |
* These providers are injected using {@link GinBinder#requestStaticInjection(Class[])}. This lets us use | |
* PlaceHyperlinks in UIBinder code without needing the {@code @UiField(provided = true)} to construct them. | |
*/ | |
@Inject | |
private static Provider<PlaceController> placeControllerProvider; | |
@Inject | |
private static Provider<PlaceHistoryMapper> placeHistoryMapperProvider; | |
@Nonnull | |
private final PlaceController placeController; | |
@Nonnull | |
private final PlaceHistoryMapper placeHistoryMapper; | |
@Nullable | |
private Place targetPlace; | |
public PlaceHyperlink() { | |
this(placeControllerProvider.get(), placeHistoryMapperProvider.get(), null); | |
} | |
public PlaceHyperlink(@Nonnull Place targetPlace) { | |
this(placeControllerProvider.get(), placeHistoryMapperProvider.get(), checkNotNull(targetPlace)); | |
} | |
@VisibleForTesting | |
public PlaceHyperlink(@Nonnull PlaceController placeController, | |
@Nonnull PlaceHistoryMapper placeHistoryMapper, | |
@Nullable Place targetPlace) { | |
this.placeController = checkNotNull(placeController); | |
this.placeHistoryMapper = checkNotNull(placeHistoryMapper); | |
this.targetPlace = targetPlace; | |
if (targetPlace != null) { | |
setHref(targetPlace); | |
} | |
configureClickHandler(); | |
} | |
public void setPlace(@Nonnull Place targetPlace) { | |
this.targetPlace = checkNotNull(targetPlace); | |
setHref(targetPlace); | |
} | |
private void setHref(@Nonnull Place targetPlace) { | |
String placeToken = placeHistoryMapper.getToken(targetPlace); | |
checkArgument(placeToken != null, "No token mapped for place %s", targetPlace); | |
setHref("#" + placeToken); | |
} | |
private void configureClickHandler() { | |
addClickHandler(new ClickHandler() { | |
@Override | |
public void onClick(ClickEvent event) { | |
// we only want to intercept simple left clicks, to avoid preventing standard browser behaviors | |
// (e.g. we don't want to prevent the user from opening the link in another tab using ctrl + click) | |
if (isLeftClick(event) && !isModifierKeyDown(event)) { | |
placeController.goTo(targetPlace); | |
event.preventDefault(); | |
} | |
} | |
}); | |
} | |
private static boolean isLeftClick(ClickEvent event) { | |
return event.getNativeButton() == NativeEvent.BUTTON_LEFT; | |
} | |
private static boolean isModifierKeyDown(ClickEvent event) { | |
return event.isAltKeyDown() | |
|| event.isControlKeyDown() | |
|| event.isMetaKeyDown() | |
|| event.isShiftKeyDown(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment