GWTPにおけるブラウザ画面サイズ変更通知

GWTPプレゼンタの作成でプレゼンタを作成し、その中身にブラウザ画面サイズ変更通知を行う方法を記述する。

RequiresResize

画面サイズ変更通知を受けるには、RequiresResizeを実装するウィジェットであることが必要になる。ただし、当然だがこのインターフェースを実装しようがしまいが、画面サイズが変更されたときのウィジェットの配置やサイズは変更される。このインターフェースは、あくまでも「画面サイズが変更された」を通知するもの。

そして、RequiresResizeにも記述があるが、RequiresResizeを実装したウィジェットを格納する先は、ProvidesResizeインターフェースを実装していなければならない。

この仕組みでは、ウィジェット階層構造の下位にサイズ変更を通知するには、その間のウィジェットがすべてRequiresResize, ProvidesResizeでなければならないように思えるが、そんなことは無いようだ。例えば、以下のような構造があったとする。

Root
 +- A
   +- B
     +- C 

この場合にCにサイズ変更を伝えるには、A,B,CすべてRequiresResize、A,BがProvidesResizeであるという必要は無い。Bがそうでない場合には、Aがサイズ変更通知を受けたときに、AからCにそれを伝えられればよい。つまり、CのonResizeを呼び出してしまうということである。

プレゼンタがルートであるとき

以下はプレゼンタがルートのときの例である。RevealTypeとしてRootlayoutを使うことに注意。

public class LoginPresenter extends Presenter<LoginPresenter.MyView, LoginPresenter.MyProxy> {

  public interface MyView extends View {}
  public static class MyViewImpl extends ViewImpl implements LoginPresenter.MyView {
    @Inject
    public MyViewImpl(LoginPanel loginPanel) {
      initWidget(loginPanel.asWidget());
    }
  }

  @ProxyCodeSplit
  @NameToken(NameTokens.login)
  public interface MyProxy extends ProxyPlace<LoginPresenter> {}

  @Inject
  public LoginPresenter(final EventBus eventBus, final MyView view, final MyProxy proxy) {
    super(eventBus, view, proxy, RevealType.RootLayout);
  }
}

LoginPanelとしては以下になる。これでブラウザ画面変更時にそのサイズを取得することができる。

@Singleton
public class LoginPanel extends Composite implements RequiresResize {
  public LoginPanel() {
    initWidget(new Label("test string"));
  }
  @Override
  public void onResize() {
    GWT.log("resized " + this.getOffsetWidth() + "," + this.getOffsetHeight());
  }
}

注意点としては、Composite + RequiresResizeではなく、ResizeCompositeというウィジェットもあるのだが、以下の通り、格納するものがRequiresResizeに限定されるため不便である。

public abstract class ResizeComposite extends Composite implements
    RequiresResize {

  @Override
  protected void initWidget(Widget widget) {
    assert widget instanceof RequiresResize :
      "ResizeComposite requires that its wrapped widget implement RequiresResize";
    super.initWidget(widget);
  }

  public void onResize() {
    ((RequiresResize) getWidget()).onResize();
  }
}

このため、ResizeCompositeは使わず、Composite + RequiresResizeとした方がよい。

プレゼンタがNestedであるとき

この場合のオフィシャルな方法はどうなるのか、少々検索してもわからなかった。しかし、以下のようにしてサイズ変更を通知することはできる。

まず、親となるプレゼンタ。

public class ParentPresenter extends
    Presenter<ParentPresenter.MyView, ParentPresenter.MyProxy> {
  public static final NestedSlot TYPE_SetMainContent = new NestedSlot();
  public interface MyView extends View {}
  public static class MyViewImpl extends ViewImpl implements ParentPresenter.MyView {
    private ParentPanel parentPanel;    
    @Inject
    public MyViewImpl(ParentPanel parentPanel) {
      this.parentPanel = parentPanel;
      initWidget(parentPanel.asWidget());      
    }
    @Override
    public Widget asWidget() {
      return parentPanel.asWidget();
    }
    @Override
    public void setInSlot(Object slot, IsWidget content) {
      if (slot == ParentPresenter.TYPE_SetMainContent) {
        parentPanel.setContent(content.asWidget());
      } else {
        super.setInSlot(slot, content);
      }
    }
  }
  @ProxyCodeSplit
  @NameToken(BaseNameTokens.parent)
  public interface MyProxy extends ProxyPlace<ParentPresenter> {
  }
  @Inject
  public ParentPresenter(final EventBus eventBus, final MyView view, final MyProxy proxy) {
    super(eventBus, view, proxy, RevealType.RootLayout);    
  }
}

そのパネル

@Singleton
public class ParentPanel extends Composite implements RequiresResize {  
  Widget content;
  public ParentPanel() {
    initWidget(...);
  }
  public void setContent(Widget content) {    
    this.content = content;
    ...
  }

  @Override
  public void onResize() {    
    if (content == null) return;    
    if (!(content instanceof RequiresResize)) return;
    ((RequiresResize)content).onResize();
  }
}

ネストされるプレゼンタに共通する処理

public class ChildPresenter<V extends View, Proxy_ extends Proxy<?>> extends Presenter <V, Proxy_>{
  @Inject
  public ChildPresenter(final EventBus eventBus, final V view, Proxy_ proxy) {
    super(eventBus, view, proxy, ParentPresenter.TYPE_SetMainContent);
  }
}

ネストされるプレゼンタ

public class StocktakeInputPresenter extends
    ChildPresenter<StocktakeInputPresenter.MyView, StocktakeInputPresenter.MyProxy> {
  public interface MyView extends View {}
  public static class MyViewImpl extends ViewImpl implements StocktakeInputPresenter.MyView { 
    private StocktakeInputPanel stocktakePanel;    
    @Inject
    public MyViewImpl(StocktakeInputPanel panel) {
      this.stocktakePanel = panel;
      initWidget(panel.asWidget());
    }
  }

  @ProxyCodeSplit
  @NameToken(NameTokens.stocktakeInput)
  public interface MyProxy extends ProxyPlace<StocktakeInputPresenter> {
  }

  @Inject
  public StocktakeInputPresenter(final EventBus eventBus, final MyView view,
      MyProxy proxy) {
    super(eventBus, view, proxy);
  }
}

ネストされるプレゼンタのパネル

public class StocktakeInputPanel extends Composite implements RequiresResize {
  public Widget asWidget() {
    return this;
  }

  @Override
  public void onResize() {
    GWT.log("" + this.getOffsetWidth() + "," + this.getOffsetHeight());
  }
}