How to respond to URLs with GWT's built-in MVP-framework? - gwt

I'm building a very simple calendar app to get familiar with the MVP-framework introduced with the 2.1 version of GWT.
What I want to achieve is being able to switch between a list of scheduled appointments and a list of the avialable time.
I have created the a CalendarPlace, CalendarActivity, CalendarView and CalendarViewImpl.
I know that to navigate to a different place i would call PlaceController.goTo(Place), so in my calendar app I would call:
clientFactory.getPlaceController.goTo(new CalendarPlace("freeTime");
The URL would be index.html#CalendarPlace:freeTime for the list of free time or
clientFactory.getPlaceController.goTo(new CalendarPlace("appointments");
for the list of scheduled appointments. The URL would be index.html#CalendarPlace:appointments
But the question is where do I respond to the different tokens? I guess the CalendarPlace would be the right place, but how would I do that?
Here is my source code(I took most of the boilerplate from the tutorial here:
CalendarPlace:
public class CalendarPlace extends Place {
private String calendarName;
public CalendarPlace(String token) {
this.calendarName = token;
}
public String getCalendarName() {
return calendarName;
}
public static class Tokenizer implements PlaceTokenizer<CalendarPlace> {
#Override
public CalendarPlace getPlace(String token) {
return new CalendarPlace(token);
}
#Override
public String getToken(CalendarPlace place) {
return place.getCalendarName();
}
}
}
CalendarActivity:
public class CalendarActivity extends AbstractActivity
implements
CalendarView.Presenter {
private ClientFactory clientFactory;
private String name;
public CalendarActivity(CalendarPlace place, ClientFactory clientFactory) {
this.name = place.getCalendarName();
this.clientFactory = clientFactory;
}
#Override
public void goTo(Place place) {
clientFactory.getPlaceController().goTo(place);
}
#Override
public void start(AcceptsOneWidget containerWidget, EventBus eventBus) {
CalendarView calendarView = clientFactory.getCalendarView();
calendarView.setName(name);
calendarView.setPresenter(this);
containerWidget.setWidget(calendarView.asWidget());
}
}
CalendarViewImpl:
public class CalendarViewImpl extends Composite implements CalendarView {
private VerticalPanel content;
private String name;
private Presenter presenter;
private OptionBox optionBox;
public CalendarViewImpl() {
//optionBox is used for navigation
//optionBox is where I call PlaceController.goTo() from
optionBox=new OptionBox();
RootPanel.get("bluebar").add(optionBox);
content=new VerticalPanel();
this.initWidget(content);
}
#Override
public void setPresenter(Presenter listener) {
this.presenter=listener;
}
#Override
public void setName(String calendarName) {
this.name = calendarName;
}
public void displayFreeTime() {
//called from somewhere to display the free time
}
public void getAppointments() {
//called from somewhere to display the appointments
}
}

In your CalendarActivity constructor you have access to the place, and therefore the token. Tuck it aside, and then in your start() method you can use it. Activities are meant to be lightweight objects, created for each new navigation.

Related

GWTP displays default place on application start even if url say to go to other place

I wonder how to change gwtp behaviour.
When I start gwt app (enter app url in browser) it always displays for me default place. But when I enter url as follow: localhost/app#settings gwtp should open place Settings, but unfortunatelly it displays me Default place.
Url in web browser address points to Settings but the view is from default place.
I would like to gwtp display for me the view from url.
Here is my configuration:
public class UiModule extends AbstractGinModule {
#Override
protected void configure() {
bind(AppView.Binder.class).in(Singleton.class);
bind(Footer.Binder.class).in(Singleton.class);
bind(GatekeeperProtectedMenuPanel.Binder.class).in(Singleton.class);
install(new GinFactoryModuleBuilder().build(MenuEntryFactory.class));
}
public class ClientModule extends AbstractPresenterModule {
#Override
protected void configure() {
bind(RestyGwtConfig.class).asEagerSingleton();
install(new DefaultModule.Builder()//
.defaultPlace(Routing.HOME.url)//
.errorPlace(Routing.ERROR.url)//
.unauthorizedPlace(Routing.LOGIN.url)//
.tokenFormatter(RouteTokenFormatter.class).build());
install(new AppModule());
install(new GinFactoryModuleBuilder().build(AssistedInjectionFactory.class));
bind(CurrentUser.class).in(Singleton.class);
bind(IsAdminGatekeeper.class).in(Singleton.class);
bind(UserLoginGatekeeper.class).in(Singleton.class);
// Load and inject CSS resources
bind(ResourceLoader.class).asEagerSingleton();
}
}
public class AppModule extends AbstractPresenterModule {
#Override
protected void configure() {
install(new UiModule());
// Application Presenters
bindPresenter(AppPresenter.class, AppPresenter.MyView.class, AppView.class, AppPresenter.MyProxy.class);
bindPresenter(HomePresenter.class, HomePresenter.MyView.class, HomeView.class, HomePresenter.MyProxy.class);
bindPresenter(ErrorPresenter.class, ErrorPresenter.MyView.class, ErrorView.class, ErrorPresenter.MyProxy.class);
bindPresenter(TestPresenter.class, TestPresenter.MyView.class, TestView.class, TestPresenter.MyProxy.class);
bindPresenter(PagePresenter.class, PagePresenter.MyView.class, PageView.class, PagePresenter.MyProxy.class);
bindPresenter(SettingsPresenter.class, SettingsPresenter.MyView.class, SettingsView.class, SettingsPresenter.MyProxy.class);
bindPresenter(FilesPresenter.class, FilesPresenter.MyView.class, FilesView.class, FilesPresenter.MyProxy.class);
bindPresenter(AdminAreaPresenter.class, AdminAreaPresenter.MyView.class, AdminAreaView.class, AdminAreaPresenter.MyProxy.class);
bindPresenter(LoginPresenter.class, LoginPresenter.MyView.class, LoginView.class, LoginPresenter.MyProxy.class);
}
}
This happens when I have GateKeeper on place's presenter.
Here is code:
public class UserLoginGatekeeper extends UserLoginModel implements Gatekeeper {
private final CurrentUser currentUser;
#Inject
UserLoginGatekeeper(CurrentUser currentUser) {
this.currentUser = currentUser;
}
#Override
public boolean canReveal() {
return currentUser.isLoggedIn();
}
}
In my main app presenter I execute asynhronous call to server to check is user login. If so I set client variable currentUser.setLoggedIn(true);. Base on this Gatekeeper allow access to restricted part of app.
I think the problem is that my asynhronous call is triggered to late. And GWTP redirect to default place.
Here is my app presenter code:
public class AppPresenter extends TabContainerPresenter<AppPresenter.MyView, AppPresenter.MyProxy> implements AppUiHandlers, CurrentUserChangedHandler, AsyncCallStartHandler, AsyncCallFailHandler,
AsyncCallSucceedHandler {
#ProxyStandard
public interface MyProxy extends Proxy<AppPresenter> {
}
public interface MyView extends TabView, HasUiHandlers<AppUiHandlers> {
void refreshTabs();
void setTopMessage(String string);
void setLoginButtonVisbility(boolean isVisible);
}
#RequestTabs
public static final Type<RequestTabsHandler> SLOT_REQUEST_TABS = new Type<>();
#ChangeTab
public static final Type<ChangeTabHandler> SLOT_CHANGE_TAB = new Type<>();
public static final NestedSlot SLOT_TAB_CONTENT = new NestedSlot();
private static final LoginService service = GWT.create(LoginService.class);
private final PlaceManager placeManager;
private final CurrentUser currentUser;
#Inject
AppPresenter(EventBus eventBus, MyView view, MyProxy proxy, PlaceManager placeManager, CurrentUser currentUser) {
super(eventBus, view, proxy, SLOT_TAB_CONTENT, SLOT_REQUEST_TABS, SLOT_CHANGE_TAB, RevealType.Root);
this.placeManager = placeManager;
this.currentUser = currentUser;
getView().setUiHandlers(this);
onStart();
}
protected void onStart() {
service.isCurrentUserLoggedIn(new MethodCallback<Boolean>() {
#Override
public void onFailure(Method method, Throwable exception) {
MaterialToast.fireToast("Fail to check is current user logged in " + method + " " + exception.getLocalizedMessage());
}
#Override
public void onSuccess(Method method, Boolean response) {
currentUser.setLoggedIn(response);
getView().setLoginButtonVisbility(response);
}
});
};
#ProxyEvent
#Override
public void onCurrentUserChanged(CurrentUserChangedEvent event) {
getView().refreshTabs();
}
#ProxyEvent
#Override
public void onAsyncCallStart(AsyncCallStartEvent event) {
getView().setTopMessage("Loading...");
}
#ProxyEvent
#Override
public void onAsyncCallFail(AsyncCallFailEvent event) {
getView().setTopMessage("Oops, something went wrong...");
}
#ProxyEvent
#Override
public void onAsyncCallSucceed(AsyncCallSucceedEvent event) {
getView().setTopMessage(null);
}
#Override
public void onLogoutButtonClick() {
service.logout(new MethodCallback<Void>() {
#Override
public void onFailure(Method method, Throwable exception) {
MaterialToast.fireToast("Fail to logout " + method + " " + exception.getLocalizedMessage());
}
#Override
public void onSuccess(Method method, Void response) {
MaterialToast.fireToast("You have been Succefully logout");
PlaceRequest request = new PlaceRequest.Builder(placeManager.getCurrentPlaceRequest()).nameToken(Routing.Url.login).build();
placeManager.revealPlace(request);
currentUser.setLoggedIn(false);
getView().setLoginButtonVisbility(false);
}
});
}
}
Working solution:
/**
*
*/
package pl.korbeldaniel.cms.client.gin;
import gwt.material.design.client.ui.MaterialToast;
import org.fusesource.restygwt.client.Method;
import org.fusesource.restygwt.client.MethodCallback;
import pl.korbeldaniel.cms.client.place.Routing;
import pl.korbeldaniel.cms.client.security.CurrentUser;
import pl.korbeldaniel.cms.client.service.LoginService;
import com.google.gwt.core.shared.GWT;
import com.google.inject.Inject;
import com.gwtplatform.mvp.client.Bootstrapper;
import com.gwtplatform.mvp.client.proxy.PlaceManager;
import com.gwtplatform.mvp.shared.proxy.PlaceRequest;
/**
* #author korbeldaniel
*
*/
public class MyBootstrapper implements Bootstrapper {
private final PlaceManager placeManager;
private final CurrentUser currentUser;
private static final LoginService service = GWT.create(LoginService.class);
#Inject
public MyBootstrapper(PlaceManager placeManager, CurrentUser currentUser) {
this.placeManager = placeManager;
this.currentUser = currentUser;
}
#Override
public void onBootstrap() {
GWT.log("OnBootstrap");
service.isCurrentUserLoggedIn(new MethodCallback<Boolean>() {
#Override
public void onFailure(Method method, Throwable exception) {
MaterialToast.fireToast("Fail to check is current user logged in " + method + " " + exception.getLocalizedMessage());
placeManager.revealErrorPlace("Fail to check is current user logged in " + method + " " + exception.getLocalizedMessage());
}
#Override
public void onSuccess(Method method, Boolean response) {
// MaterialToast.fireToast("1Current user is logged in: " +
// response);
currentUser.setLoggedIn(response);
if (response == true) {
placeManager.revealCurrentPlace();
} else {
placeManager.revealPlace(new PlaceRequest.Builder().nameToken(Routing.Url.login).build());
}
}
});
};
}
Yeah, your backend call is asynchronous and most likely the UserLoginGatekeeper code will run before the backend call returns and the user gets redirected to the default page.
There are two solutions:
Use a dynamically generated host page (index.html) and set a javascript variable to the user details by the backend. You can read out the userdetails in a custom Bootstraper implementation and set the CurrentUser.
If you don't want to use a dynamically generated host page, you can also move the backend call isCurrentUserLoggedIn intot he custom Bootstrapper implementation and in the onSuccess callback reveal the first page (like in the above linked GWTP documentation)

Why canReveal() of GateKeeper was called before the EventHandler was called (GWT)?

I got a Header presenter which is the nested presenter. The Customer presenter is the child of Header presenter (ie the Customer presenter was put into a slot of Header presenter).
So I want to use MyGateKeeper to manage login page. The HeaderPresenter that will fire PassUserInfoEvent.
public class MyGateKeeper implements Gatekeeper{
private String loginedUserID="";
private final EventBus eventBus;
#Inject
public MyGateKeeper (final EventBus eventBus){
this.eventBus = eventBus;
this.eventBus.addHandler(PassUserInfoEvent.getType(), new PassUserInfoHandler(){
#Override
public void onPassUserInfo(PassUserInfoEvent event) {
// TODO Auto-generated method stub
String userID=event.getUserID();
loginedUserID=userID;
}
});
}
#Override
public boolean canReveal(){
System.out.println(loginedUserID+"Test");
if(!loginedUserID.equals("")){
System.out.println(loginedUserMeaningID+"cxcxc");
return true;
}
else{
return false;
}
}
}
In the CustomerPresenter:
#ProxyCodeSplit
#NameToken(NameTokens.cust)
#UseGatekeeper(MyGateKeeper.class)
public interface MyProxy extends ProxyPlace<CustomerPresenter> {
}
However after run, it does not show the Gui even I loggined. I tested & found that canReveal() in MyGateKeeper was called before PassUserInfoHandler() was called, so canReveal never return true;
How to fix this problem?
The usual pattern to accomplish this is to bind a CurrentUser class in Singleton:
bind(CurrentUser.class).in(Singleton.class);
and inject it into your GateKeeper. Inside your GateKeeper's canReveal method, you'll check that currentUser.isLoggedIn() :
private final CurrentUser currentUser;
#Inject
MyGateKeeper(CurrentUser currentUser) {
this.currentUser = currentUser;
}
#Override
public boolean canReveal() {
return currentUser.isLoggedIn();
}
You should initialize the CurrentUser.isLoggedIn field inside your Bootstrapper (see https://github.com/ArcBees/GWTP/wiki/Bootstrapping-or-Application-Initialization) by calling your server. Here's an example using GWTP's RestDispatch:
public class BootstrapperImpl implements Bootstrapper {
private final String unauthorizedPlace;
private final CurrentUser currentUser;
private final PlaceManager placeManager;
private final RestDispatch restDispatch;
private final UserResource userResource;
#Inject
BootstrapperImpl(
#UnauthorizedPlace String unauthorizedPlace,
CurrentUser currentUser,
PlaceManager placeManager,
RestDispatch restDispatch,
UserResource userResource) {
this.unauthorizedPlace = unauthorizedPlace;
this.currentUser = currentUser;
this.placeManager = placeManager;
this.restDispatch = restDispatch;
this.userResource = userResource;
}
#Override
public void onBootstrap() {
checkIfUserIsLoggedIn();
}
private void checkIfUserIsLoggedIn() {
restDispatch.execute(userResource.isCurrentUserLoggedIn(), new AbstractAsyncCallback<Boolean>() {
#Override
public void onSuccess(Boolean isCurrentUserLoggedIn) {
navigate(isCurrentUserLoggedIn);
}
});
}
private void navigate(Boolean isCurrentUserLoggedIn) {
currentUser.setLoggedIn(isCurrentUserLoggedIn);
if (isCurrentUserLoggedIn) {
placeManager.revealCurrentPlace();
} else {
placeManager.revealPlace(new PlaceRequest.Builder().nameToken(unauthorizedPlace).build());
}
}
}

Problems references the WebEngine

I'm trying to make a reference of WebEngine
public class ClientArea implements Initializable {
public WebEngine engine;
#Override
public void initialize(URL url, ResourceBundle rb) {
engine = browser.getEngine();
engine.load("about:blank");
}
}
for a class in the same file (ClientArea.java)
private static class SearchBox extends Region {
private WebEngine engine; // private or public
private TextField insertU;
private Button refreshButton;
private Button stopButton;
private Button backButton;
private Button forwardButton;
public Button voltarButton;
public SearchBox() {
}
}
but that does not work, what would it be?
May be i don't understand your question. You want to access from a nested class (SearchBox) a field (engine) of the top level class (ClientArea).
It is easy if the nested class is not static. In this case, the inner class keeps a reference (ClientArea.this) to the outer class and could access to its fields and methods.
public class ClientArea implements Initializable {
public WebEngine engine;
#Override
public void initialize(URL url, ResourceBundle rb) {
engine = browser.getEngine();
engine.load("about:blank");
}
private class SearchBox extends Region {
public SearchBox() {
System.err.println(engine);
}
private ClientArea getClientArea() {
return ClientArea.this;
}
private WebEngine getWebEngine() {
return engine;
}
}
}
Look at this answer on the difference between the static nested class ad the inner class.
The java tutorial on the nested classes.

GWT 2.1 Places example without Activities

does anyone have any examples of how to using Places without using activities for history management. I knocked something up quickly and can see the url changing with browser-back and browser-forward clicks but the display doesn't go anywhere.
I'm using a DecoratedTabPanel and have a SelectionHandler that fires off getPlaceController().goTo(place).
Any ideas would be useful.
Here is a simple piece of code that I've made to demonstrate what you expected. It's based on the GWT and MVP Development document (GWT and MVP)
In this example you navigate between two tabs. On selection, a new history item is created (without any activity). As long as you use browser buttons to go back/forward the page will be updated correctly.
I have defined one place, one activity and its view. I've adjusted AppActivityMapper, AppActivityManager and ClientFactory to my needs. The code is lightweight and doesn't need comments to be understood. I've only put some explanations when it was needed, but if it's not clear do not hesitate to ask.
ExampleView.java
public interface ExampleView extends IsWidget {
void selectTab(int index);
}
ExampleViewImpl.java
public class ExampleViewImpl extends Composite implements ExampleView, SelectionHandler<Integer> {
private DecoratedTabPanel panel;
public ExampleViewImpl() {
panel = new DecoratedTabPanel();
initComposite();
initWidget(panel);
}
private void initComposite() {
panel.add(new HTML("Content 1"), "Tab 1");
panel.add(new HTML("Content 2"), "Tab 2");
panel.selectTab(0);
panel.addSelectionHandler(this);
}
#Override
public void selectTab(int index) {
if (index >=0 && index < panel.getWidgetCount()) {
if (index != panel.getTabBar().getSelectedTab()) {
panel.selectTab(index);
}
}
}
#Override
public void onSelection(SelectionEvent<Integer> event) {
// Fire an history event corresponding to the tab selected
switch (event.getSelectedItem()) {
case 0:
History.newItem("thetabplace:1");
break;
case 1:
History.newItem("thetabplace:2");
break;
}
}
}
ClientFactory.java
public class ClientFactory {
private final EventBus eventBus = new SimpleEventBus();
private final PlaceController placeController = new PlaceController(eventBus);
private final ExampleViewImpl example = new ExampleViewImpl();
public EventBus getEventBus() {
return this.eventBus;
}
public PlaceController getPlaceController() {
return this.placeController;
}
public ExampleViewImpl getExampleView() {
return example;
}
}
ExampleActivity.java
public class ExampleActivity extends AbstractActivity {
private ExampleView view;
private ClientFactory factory;
public ExampleActivity(ExamplePlace place, ClientFactory factory) {
// Get the factory reference
this.factory = factory;
// Get the reference to the view
view = this.factory.getExampleView();
// Select the tab corresponding to the token value
if (place.getToken() != null) {
// By default the first tab is selected
if (place.getToken().equals("") || place.getToken().equals("1")) {
view.selectTab(0);
} else if (place.getToken().equals("2")) {
view.selectTab(1);
}
}
}
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
// Attach this view to the application container
panel.setWidget(view);
}
}
ExamplePlace.java
/**
* Just an very basic place
*/
public class ExamplePlace extends Place {
// The token corresponding to an action
private String token;
// This place should use a token to identify a view behavior
public ExamplePlace(String token) {
this.token = token;
}
// Return the current token
public String getToken() {
return token;
}
// Custom prefix to break the default name : ExamplePlace
// So that the history token will be thetabplace:token
// and not any more : ExamplePlace:token
#Prefix(value="thetabplace")
public static class Tokenizer implements PlaceTokenizer<ExamplePlace> {
#Override
public String getToken(ExamplePlace place) {
return place.getToken();
}
#Override
public ExamplePlace getPlace(String token) {
return new ExamplePlace(token);
}
}
}
AppActivityMapper.java
public class AppActivityMapper implements ActivityMapper {
private ClientFactory clientFactory;
public AppActivityMapper(ClientFactory clientFactory) {
super();
this.clientFactory = clientFactory;
}
#Override
public Activity getActivity(Place place) {
if (place instanceof ExamplePlace) {
return new ExampleActivity((ExamplePlace) place, clientFactory);
}
return null;
}
}
AppPlaceHistoryMapper.java
#WithTokenizers({ExamplePlace.Tokenizer.class})
public interface AppPlaceHistoryMapper extends PlaceHistoryMapper
{
}
All together
private Place defaultPlace = new ExamplePlace("1");
private SimplePanel appWidget = new SimplePanel();
public void onModuleLoad() {
ClientFactory clientFactory = new ClientFactory();
EventBus eventBus = clientFactory.getEventBus();
PlaceController placeController = clientFactory.getPlaceController();
// Start ActivityManager for the main widget with our ActivityMapper
ActivityMapper activityMapper = new AppActivityMapper(clientFactory);
ActivityManager activityManager = new ActivityManager(activityMapper, eventBus);
activityManager.setDisplay(appWidget);
// Start PlaceHistoryHandler with our PlaceHistoryMapper
AppPlaceHistoryMapper historyMapper= GWT.create(AppPlaceHistoryMapper.class);
PlaceHistoryHandler historyHandler = new PlaceHistoryHandler(historyMapper);
historyHandler.register(placeController, eventBus, defaultPlace);
RootPanel.get().add(appWidget);
// Goes to the place represented on URL else default place
historyHandler.handleCurrentHistory();
}

GIN & GWT: Binding Presentation layer with View

I'm trying to bind a GWT view with its presentation layer, but it doesn't seem to be doing anything.
It's a Spring Roo GWT generated project and I'm trying to use the scaffold given as far as possible.
The view is a simple button (R.ui.xml) and the rest of the view is defined in R.java:
public class R extends Composite implements RPresenter.Display {
interface MyUiBinder extends UiBinder<Widget, R> {}
private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);
#UiField Button myButton;
private ClickHandler buttonClickHandler = null;
public R(){
initWidget(uiBinder.createAndBindUi(this));
}
#UiHandler("myButton")
void onButtonClick(ClickEvent event){
GWT.log('Button clicked');
if (buttonClickHandler != null){
GWT.log("buttonClickHandler event triggered");
buttonClickHandler.onClick(event);
}
}
#Override
public void setButtonClickHandler(ClickHandler buttonClickHandler) {
GWT.log("setButtonClickHandler");
this.buttonClickHandler = buttonClickHandler;
}
}
The presenter:
public class RPresenter {
public interface Display extends IsWidget {
void setButtonClickHandler(ClickHandler buttonClickHandler);
}
private final Display display;
private final EventBus eventBus;
#Inject
public RPresenter(EventBus eventBus, Display display){
this.display = display;
this.eventBus = eventBus;
bind();
}
private void bind(){
display.setButtonClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
GWT.log("onClick event triggered");
}
});
}
public void go(HasWidgets container){
container.add(display.asWidget());
}
}
And for my GIN module I use the generated ScaffoldModule in the ...client.scaffold.ioc package:
public class ScaffoldModule extends AbstractGinModule {
#Override
protected void configure() {
GWT.log("ScaffoldModule configure");
bind(EventBus.class).to(SimpleEventBus.class).in(Singleton.class);
bind(ApplicationRequestFactory.class).toProvider(RequestFactoryProvider.class).in(Singleton.class);
bind(PlaceController.class).toProvider(PlaceControllerProvider.class).in(Singleton.class);
//bind(RPresenter.Display.class).to(R.class).in(Singleton.class);
bind(RPresenter.Display.class).to(R.class);
}
static class PlaceControllerProvider implements Provider<PlaceController> {
private final EventBus eventBus;
#Inject
public PlaceControllerProvider(EventBus eventBus) {
this.eventBus = eventBus;
}
public PlaceController get() {
return new PlaceController(eventBus);
}
}
static class RequestFactoryProvider implements Provider<ApplicationRequestFactory> {
private final EventBus eventBus;
#Inject
public RequestFactoryProvider(EventBus eventBus) {
this.eventBus = eventBus;
}
public ApplicationRequestFactory get() {
ApplicationRequestFactory requestFactory = GWT.create(ApplicationRequestFactory.class);
requestFactory.initialize(eventBus);
return requestFactory;
}
}
}
In the GWT development mode console, the "ScaffoldModule configure" never displays, yet the generated scaffold seems to binding just fine as the events get passed along from component to component without a hitch, unless the binding is magically happening somewhere else and that is dead code.
When I put my bind(RPresenter.Display.class).to(R.class) in, it doesn't seem to do the binding. The only output I get in the GWT console is "Button clicked" which is called in the view and then nothing further. I'm clearly missing something, any ideas?
The call to GWT.log() will not output anything from an AbstractGinModule - classes that extend AbstractGinModule (ScaffoldModule in your situation) are used by gin at compile time to decide which concrete implementations to use for injected interfaces. From the rest of your description (i.e. that the UI shows up in the application) it appears that your dependency injection is working correctly.