Package org.waveprotocol.wave.client.widget.toolbar

Source Code of org.waveprotocol.wave.client.widget.toolbar.ToplevelToolbarWidget$Binder

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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 org.waveprotocol.wave.client.widget.toolbar;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.StyleInjector;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.resources.client.ImageResource.ImageOptions;
import com.google.gwt.resources.client.ImageResource.RepeatStyle;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;

import org.waveprotocol.wave.client.widget.overflowpanel.OverflowPanelUpdater;
import org.waveprotocol.wave.client.widget.overflowpanel.OverflowPanelUpdater.OverflowPanel;
import org.waveprotocol.wave.client.widget.toolbar.buttons.AbstractToolbarButton;
import org.waveprotocol.wave.client.widget.toolbar.buttons.HorizontalToolbarButtonWidget;
import org.waveprotocol.wave.client.widget.toolbar.buttons.ToolbarButtonUiProxy;
import org.waveprotocol.wave.client.widget.toolbar.buttons.ToolbarButtonView;
import org.waveprotocol.wave.client.widget.toolbar.buttons.ToolbarButtonView.State;
import org.waveprotocol.wave.client.widget.toolbar.buttons.ToolbarClickButton;
import org.waveprotocol.wave.client.widget.toolbar.buttons.ToolbarToggleButton;
import org.waveprotocol.wave.client.widget.toolbar.buttons.VerticalToolbarButtonWidget;
import org.waveprotocol.wave.model.util.CollectionUtils;

import java.util.List;

/**
* A {@link ToolbarView} to be used as a top-level widget.
*
* @author kalman@google.com (Benjamin Kalman)
*/
public final class ToplevelToolbarWidget extends Composite
    implements
    GroupingToolbar.View,
    OverflowPanel,
    SubmenuItem.Parent {

  interface Resources extends ClientBundle {
    @Source("ToplevelToolbarWidget.css")
    Css css();

    @Source("button_fill.png")
    @ImageOptions(repeatStyle = RepeatStyle.Horizontal)
    ImageResource fillImage();

    @Source("toolbar_more_button.png")
    ImageResource overflowButtonIcon();
  }

  interface Css extends CssResource {
    String toolbar();
    String overflowButton();
    String overflowButtonIcon();
  }

  /**
   * An item in the toolbar.
   */
  private static final class Item {
    /** The item's widget in the toplevel toolbar. */
    public final HorizontalToolbarButtonWidget onToplevel;

    /** The item's widget in the overflow toolbar. */
    public final VerticalToolbarButtonWidget onOverflow;

    /**
     * The proxy of the item, always delegates to either {@link #onToplevel} or
     * {@link #onOverflow} depending on the placement of the item.
     */
    public final ToolbarButtonUiProxy proxy;

    /**
     * The item as an {@link AbstractToolbarButton}, the component of the item
     * returned to callers that add buttons and submenus. Maintained in order to
     * change parents as the item overflows (and un-overflows), and to remove
     * the item.
     */
    public AbstractToolbarButton asAbstractButton = null;

    public Item(HorizontalToolbarButtonWidget onToplevel, VerticalToolbarButtonWidget onOverflow,
        ToolbarButtonUiProxy proxy) {
      this.onToplevel = onToplevel;
      this.onOverflow = onOverflow;
      this.proxy = proxy;
    }
  }

  interface Binder extends UiBinder<Widget, ToplevelToolbarWidget> {}
  private static final Binder BINDER = GWT.create(Binder.class);

  @UiField(provided = true)
  static final Resources res = GWT.create(Resources.class);
  static {
    StyleInjector.inject(res.css().getText(), true);
  }

  @UiField FlowPanel self;
  @UiField SimplePanel overflowButton;

  /** The overflow submenu. */
  private final SubmenuToolbarWidget overflowSubmenu =
      new SubmenuToolbarWidget(new ToolbarToggleButton(new HorizontalToolbarButtonWidget()));

  /** The logic for controlling which items show in the overflow submenu. */
  private final OverflowPanelUpdater overflowLogic;

  /** Items in the menu. */
  private final List<Item> items = CollectionUtils.newArrayList();

  public ToplevelToolbarWidget() {
    initWidget(BINDER.createAndBindUi(this));
    overflowButton.setWidget(overflowSubmenu.hackGetWidget());
    overflowSubmenu.addDebugClass("more");
    // Build the "..." icon.
    Element icon = DOM.createDiv();
    icon.setClassName(res.css().overflowButtonIcon());
    overflowSubmenu.setVisualElement(icon);
    overflowSubmenu.setShowDivider(true);
    // Attach overflow logic.
    overflowLogic = new OverflowPanelUpdater(this);
  }

  //
  // For vanilla ToolbarView
  //

  @Override
  public ToolbarClickButton addClickButton() {
    ToolbarClickButton button = insertClickButton(items.size());
    showDividerIfNotFirst(button);
    return button;
  }

  @Override
  public ToolbarToggleButton addToggleButton() {
    ToolbarToggleButton button = insertToggleButton(items.size());
    showDividerIfNotFirst(button);
    return button;
  }

  @Override
  public SubmenuToolbarView addSubmenu() {
    SubmenuToolbarView submenu = insertSubmenu(items.size());
    showDividerIfNotFirst(submenu);
    return submenu;
  }

  private void showDividerIfNotFirst(ToolbarButtonView button) {
    if (button.hackGetWidget() != items.get(0).onToplevel) {
      button.setShowDivider(true);
    }
  }

  @Override
  public ToolbarView addGroup() {
    return new GroupingToolbar(this, addFakeItem());
  }

  /**
   * Adds a fake item that isn't rendered but still has an entry in items
   * (so that it plays correctly with overflowing, etc).
   */
  private ToolbarButtonView addFakeItem() {
    // NOTE: simplest way to add a fake item is to add an invisible button.
    ToolbarButtonView fakeButton = addClickButton();
    fakeButton.setState(State.INVISIBLE);
    return fakeButton;
  }

  @Override
  public void clearItems() {
    self.clear();
    items.clear();
  }

  //
  // For GroupingToolbar.View
  //

  @Override
  public ToolbarClickButton insertClickButton(int beforeIndex) {
    Item item = insertItem(beforeIndex);
    ToolbarClickButton button = new ToolbarClickButton(item.proxy);
    item.asAbstractButton = button;
    return button;
  }

  @Override
  public ToolbarToggleButton insertToggleButton(int beforeIndex) {
    Item item = insertItem(beforeIndex);
    ToolbarToggleButton button = new ToolbarToggleButton(item.proxy);
    item.asAbstractButton = button;
    return button;
  }

  @Override
  public SubmenuToolbarView insertSubmenu(int beforeIndex) {
    Item item = insertItem(beforeIndex);
    SubmenuToolbarWidget submenu = new SubmenuToolbarWidget(new ToolbarToggleButton(item.proxy));
    submenu.setShowDropdownArrow(true);
    item.asAbstractButton = submenu;
    return submenu;
  }

  @Override
  public int indexOf(ToolbarButtonView button) {
    for (int i = 0; i < items.size(); i++) {
      if (items.get(i).asAbstractButton == button) {
        return i;
      }
    }
    throw new IllegalArgumentException("button is not in this toolbar");
  }

  /**
   * Adds a new item to the toolbar, handling its placement in both the toplevel
   * and overflow toolbars.
   */
  private Item insertItem(int beforeIndex) {
    // The widget for the toplevel toolbar.
    HorizontalToolbarButtonWidget toplevelButton = new HorizontalToolbarButtonWidget();
    self.insert(toplevelButton, beforeIndex);
    overflowLogic.updateStateEventually();

    // The widget for the overflow toolbar.  Construct manually and use
    // hackAddWidget so that the ToolbarButtonViewProxy can manage the state
    // of the submenus correctly.
    VerticalToolbarButtonWidget overflowButton = new VerticalToolbarButtonWidget();
    overflowSubmenu.hackInsertWidget(overflowButton, beforeIndex);

    // Return the item, initially proxying the toplevel button.
    Item item = new Item(toplevelButton, overflowButton, new ToolbarButtonUiProxy(toplevelButton));
    items.add(beforeIndex, item);
    return item;
  }

  //
  // Resizing
  //

  public void onResizeDone() {
    overflowLogic.updateStateEventually();
  }

  //
  // OverflowPanel
  //

  @Override
  public boolean hasOverflowed(int index) {
    return items.get(index).onToplevel.getElement().getOffsetTop() > 0;
  }

  @Override
  public boolean isVisible(int index) {
    return items.get(index).proxy.hackGetState() != State.INVISIBLE;
  }

  @Override
  public void moveToOverflowBucket(int index) {
    Item item = items.get(index);
    // The item is in the overflow menu now, so parent is the overflow submenu.
    item.asAbstractButton.setParent(overflowSubmenu);
    // ... and so is the proxy.
    item.proxy.setDelegate(item.onOverflow);
    // Even though there is overflow: hidden, explicitly hide the toplevel
    // buttons so that fast resize events don't look strange (e.g. wrong icon
    // placement).
    item.onToplevel.setState(State.INVISIBLE);
    // Force a state change event to possibly enable the overflow submenu.
    overflowSubmenu.onChildStateChanged(item.asAbstractButton, item.proxy.hackGetState());
  }

  @Override
  public void onBeginOverflowLayout() {
    // Reset all the items to the toplevel and hide the overflow submenu;
    // very difficult to calculate overflow without a consistent state.
    for (Item item : items) {
      // The item is in the toplevel now, so the toplevel is the parent.
      item.asAbstractButton.setParent(this);
      // ... and the proxy is on the toplevel.
      item.proxy.setDelegate(item.onToplevel);
      // Item now invisible on the overflow menu.  This is done to all
      // items of course, so by the end the overflow submenu will think that
      // all buttons are disabled.
      item.onOverflow.setState(State.INVISIBLE);
    }
    overflowSubmenu.setState(State.INVISIBLE);
  }

  @Override
  public void onEndOverflowLayout() {
    // Hide the divider of the first overflowed button, if any.  Since this is
    // set on the button itself and not the proxy, the divider state will be
    // reset to the correct value next time there is an overflow.
    for (Item item : items) {
      if (item.proxy.getDelegate() == item.onOverflow) {
        item.onOverflow.setShowDivider(false);
        // TODO(kalman): Don't hide all the dividers.
        //break;
      }
    }
  }

  @Override
  public void showMoreButton() {
    // This is called before moving any items to the overflow panel; doing so
    // will result in the submenu enabling itself so long as the button states
    // are kept up to date.  So, for now, just make it visible and disabled.
    overflowSubmenu.setState(State.DISABLED);
  }

  @Override
  public int getWidgetCount() {
    return items.size();
  }

  //
  // SubmenuItem.Parent
  //

  @Override
  public void onChildStateChanged(SubmenuItem item, State newState) {
    overflowLogic.updateStateEventually();
  }

  @Override
  public void onActionPerformed() {
  }
}
TOP

Related Classes of org.waveprotocol.wave.client.widget.toolbar.ToplevelToolbarWidget$Binder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.