Jump to: navigation, search

Scout/HowTo/3.8/Adding toolbars to views with the SWT client

< Scout‎ | HowTo‎ | 3.8
Revision as of 07:50, 18 March 2013 by Urs.beeli.sbb.ch (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)


Scout
Wiki Home
Website
DownloadGit
Community
ForumsBlogTwitter
Bugzilla
Bugzilla


The SWT client has different views. It is possible to add toolbar buttons to the view, these will then appear in the upper right corner of the view's header (to the left of the minimise/maximise buttons). This how-to explains how such toolbars can be added to views.

Steps

Registering the toolbar classes

Each toolbar must be registered for the view it will be attached to. This is done in the SWT plugin.xml file by adding a menuContribution to the org.eclipse.ui.menus extension. The menuContribution needs a locationURI attribute which is built as follows:

toolbar:<class name of the view you want the toolbar added to>

The menuContribution also needs a dynamic element which indicates the class that will manage the toolbar.

        <menuContribution
            allPopups="false"
            locationURI="toolbar:org.eclipse.minicrm.ui.swt.views.OutlineView">
         <dynamic
               class="org.eclipse.minicrm.ui.swt.toolbars.ViewToolBarOutline"
               id="org.eclipse.minicrm.ui.swt.views.toolbar.OutlineView">
         </dynamic>
      </menuContribution>
      <menuContribution
            allPopups="false"
            locationURI="toolbar:org.eclipse.minicrm.ui.swt.views.TableView">
         <dynamic
               class="org.eclipse.minicrm.ui.swt.toolbars.ViewToolBarTable"
               id="org.eclipse.minicrm.ui.swt.views.toolbar.TableView">
         </dynamic>
      </menuContribution>

Implementing the toolbar classes

Each distinct toolbar needs its own class, but as the only difference between toolbars is what actions are added as buttons, we can use a common class that covers the necessary functionality.

  • Create a new package org.eclipse.minicrm.ui.swt.toolbars
  • Create the abstract class ViewToolBar in that package
    package org.eclipse.minicrm.ui.swt.toolbars;

    import java.util.ArrayList;

    import org.eclipse.jface.action.Action;
    import org.eclipse.jface.action.ActionContributionItem;
    import org.eclipse.jface.action.IContributionItem;
    import org.eclipse.jface.action.Separator;
    import org.eclipse.minicrm.client.ClientSession;
    import org.eclipse.minicrm.ui.swt.Activator;
    import org.eclipse.scout.rt.client.IClientSession;
    import org.eclipse.scout.rt.client.ui.action.IAction;
    import org.eclipse.scout.rt.ui.swt.ISwtEnvironment;
    import org.eclipse.scout.rt.ui.swt.action.SwtScoutAction;
    import org.eclipse.swt.widgets.ToolBar;
    import org.eclipse.ui.actions.CompoundContributionItem;

    public abstract class ViewToolBar extends CompoundContributionItem {
      private IContributionItem[] oldItems = null;
      private ToolBar m_parent = null;
      private int m_index = 0;

      public ViewToolBar() {
        super();
        ((SwtEnvironment) Activator.getDefault().getEnvironment()).addViewToolBar(this);
      }

      public ViewToolBar(String id) {
        super(id);
        ((SwtEnvironment) Activator.getDefault().getEnvironment()).addViewToolBar(this);
      }

      public void refreshToolBar() {
        if (m_parent != null) {
          fill(m_parent, m_index);
        }
      }

      @Override
      public void fill(ToolBar parent, int index) {
        m_parent = parent;
        m_index = index;

        if (index == -1) {
          index = parent.getItemCount();
        }

        disposeOldItems();
        oldItems = getContributionItems();
        for (int i = 0; i < oldItems.length; i++) {
          IContributionItem item = oldItems[i];
          int oldItemCount = parent.getItemCount();
          if (item.isVisible()) {
            item.fill(parent, index);
          }
          int newItemCount = parent.getItemCount();
          int numAdded = newItemCount - oldItemCount;
          index += numAdded;
        }
      }

      private void disposeOldItems() {
        if (oldItems != null) {
          for (int i = 0; i < oldItems.length; i++) {
            IContributionItem oldItem = oldItems[i];
            oldItem.dispose();
          }
          oldItems = null;
        }
      }

      @Override
      public void dispose() {
        disposeOldItems();
        super.dispose();
      }

      @Override
      protected IContributionItem[] getContributionItems() {
        ISwtEnvironment env = Activator.getDefault().getEnvironment();
        if (env != null && env.isInitialized()) {
          IClientSession iSession = env.getClientSession();
          if ((iSession != null) && (iSession instanceof ClientSession))
          {

            ClientSession session = (ClientSession) iSession;
            IAction[] buttons = session.getViewButtons(getView());
            if (buttons != null && buttons.length > 0) {
              return getButtonContribution(buttons, env);
            }
          }
        }
        return new IContributionItem[0];
      }

      private IContributionItem[] getButtonContribution(IAction[] buttonActions, ISwtEnvironment env) {
        ArrayList<IContributionItem> contributionItems = new ArrayList<IContributionItem>();
        for (IAction buttonAction : buttonActions) {
          if (!buttonAction.isVisible()) {
            continue;
          }
          if (buttonAction.isSeparator()
              //ignore trailing separator
              && contributionItems.size() > 0 && contributionItems.get(contributionItems.size() - 1).isSeparator()) {
            continue;
          }

          contributionItems.add(getMenuContributionItem(buttonAction, env));
        }
        return contributionItems.toArray(new IContributionItem[contributionItems.size()]);
      }

      private IContributionItem getMenuContributionItem(IAction buttonAction, ISwtEnvironment env) {
        if (!buttonAction.isVisible()) {
          return null;
        }

        if (buttonAction.isSeparator()) {
          return new Separator();
        }

        Action swtAction = new SwtScoutAction(buttonAction, env).getSwtAction();
        return new ActionContributionItem(swtAction);
      }

      abstract protected String getView();
    }      
  • This class uses the ClientSession.getViewButtons() method to retrieve the configured actions (see more about this below). It also defines an abstract method getView() which defines the key value to which configured actions are attached
  • For each distinct toolbar, we need to define a class which extends ViewToolBar, these are the classes that are attached to the views using the <dynamic> element in the plugin.xml file.

If more than one view need identical toolbars, it is possible to use the same extended class for those views

    package org.eclipse.minicrm.ui.swt.toolbars;

    public class ViewToolBarOutline extends ViewToolBar {
      @Override
      protected String getView() {
        return Strings.OutlineView;
      }
    }
    package org.eclipse.minicrm.ui.swt.toolbars;

    public class ViewToolBarTable extends ViewToolBar {
      @Override
      protected String getView() {
        return Strings.TableView;
      }
    }      
  • The key values are best added to the org.eclipse.minicrm.shared.Strings class.

Transferring the actions between client and server

Building the dynamic menu (DesktopMenuBar) is done using Desktop.getMenus() which returns all instances of AbstractMenu. Building the main toolbar (ApplicationActionBarAdvisor) is done using Desktop.getViewButtons() and Desktop.getToolButtons() which return all instances of AbstractOutlineViewButton and AbstractOutlineViewButton.

Since Scout does not support view toolbars out of the box, a little more work is needed to set up the buttons in the client and to access them from the SWT renderer. Accessing the buttons from the SWT clien is done using the getViewButtons() method, mentioned in the previous chapter. As it is easier to extend the ClientSession than the desktop itself, we are adding a container to the ClientSession to and from which buttons can be added and retrieved:

  • Add the following member to ClientSession:
private Map<String, List<IAction>> viewButtons = new HashMap<String, List<IAction>>();
  • Add the following methods to ClientSession:
      public void addViewButton(String key, IAction buttonAction) {
        if (key != null && buttonAction != null) {
          List<IAction> actions = viewButtons.get(view);
          if (actions == null) {
            actions = new ArrayList<IAction>();
          }
          if (actions != null) {
            actions.add(buttonAction);
            viewButtons.put(key, actions);
          }
        }
      }

      public IAction[] getViewButtons(String key) {
        List<IAction> result = null;
        if (key != null) {
          result = viewButtons.get(key);
          if (result != null) {
            return result.toArray(new IAction[result.size()]);
          }
        }
        return null;
      }    

Defining the actions

Next we need to define the actions for our view toolbar buttons. They cannot be of type ViewButton or ToolButton, as they would otherwise be included in the main window toolbar (unless you want to add one of the "main toolbar buttons" to a view toolbar). Create classes that derive from AbstractAction, you can will need to set an iconId and override execAction, optionally, you can define a tooltip:

  public class OutlineTreeExpandCommand extends AbstractAction {
    @Override
    public String getIconId() {
      return Icons.Expand;
    }

    @Override
    protected String getConfiguredTooltipText() {
      return TEXTS.get("Expand");
    }

    @Override
    protected void execAction() throws ProcessingException {
      if (extendedTreeForm != null) {
        extendedTreeForm.setTreeNodeState(true);
      }
    }
  }

  public class OutlineTreeCollapseCommand extends AbstractAction {
    @Override
    public String getIconId() {
      return Icons.Collapse;
    }

    @Override
    protected String getConfiguredTooltipText() {
      return TEXTS.get("Collapse");
    }

    @Override
    protected void execAction() throws ProcessingException {
      if (extendedTreeForm != null) {
        extendedTreeForm.setTreeNodeState(false);
      }
    }
  }

  public class TableActionCommand extends AbstractAction {
    @Override
    public String getIconId() {
      return Icons.Action;
    }

    @Override
    protected String getConfiguredTooltipText() {
      return TEXTS.get("TableAction");
    }

    @Override
    protected void execAction() throws ProcessingException {
      system.out.println("TableAction");
    }
  }  

Creating and registering the actions

As a last step, we need to instantiate and register our commands when the Desktop is being started.

We need to use the same key values that the extended classes use in their getView() methods.

  @Override
  protected void initConfig() {
    super.initConfig();

    ClientSession session = ClientSession.get();
    if (session != null) {
      session.addViewButton(Strings.TableView, new TableActionCommand());
      session.addViewButton(Strings.OutlineView, new OutlineTreeCollapseCommand());
      session.addViewButton(Strings.OutlineView, new OutlineTreeExpandCommand());
    }

Ensuring recreation and redrawing

In principle, the steps above are already sufficient to show the view specific toolbars. However, to make sure they are also correctly shown if ApplicationWorkbenchAdvisor.initialize() calls configurer.setSaveAndRestore(true);, a few more additions are needed.

  • Add the following members to SwtEnvironment:
    private Set<ViewToolBar> m_viewToolBars = new HashSet<ViewToolBar>();
    private Set<IRefreshableToolbarView> m_views = new HashSet<IRefreshableToolbarView>();
  • Add these two methods to SwtEnvironment:
      public void addViewToolBar(ViewToolBar viewToolBar) {
        m_viewToolBars.add(viewToolBar);
      }

      public void addView(IRefreshableToolbarView view) {
        m_views.add(view);
      }
  • Change the environmentListener that is being created in SwtEnvironment.initialize(). Change
    m_advisor.initViewButtons(d);

to

    m_advisor.initViewButtons(d);
    Iterator<ViewToolBar> itb = m_viewToolBars.iterator();
    while (itb.hasNext()) {
      itb.next().refreshToolBar();
    }
    Iterator<IRefreshableToolbarView> itv = m_views.iterator();
    while (itv.hasNext()) {
      itv.next().refreshToolbar();
    }      
  • Create a new interface IRefreshableToolbarView:
    public interface IRefreshableToolbarView {

      public void refreshToolbar();
    }
  • For each view that has a toolbar make sure that it implements this interface:
    public class TableView extends ValidationAbstractScoutView implements IRefreshableToolbarView {

and registers itself with the environment:

      public TableView() {
        ((SwtEnvironment) Activator.getDefault().getEnvironment()).addView(this);
      }

Also make sure to add the following method:

      @Override
      public void refreshToolbar() {
        getViewSite().getActionBars().getToolBarManager().update(true);
      }


Result

The result looks like this:

ViewToolbar.png