2012-01-12 45 views
2

我想製作一個GWT CellTree,帶有一個可選的彈出菜單,點擊一個TreeNode。GWT CellTree帶有一個可選的彈出式菜單,點擊一個TreeNode

所以我製作了一個CustomTreeModel。那就是:

public class CustomTreeModel implements TreeViewModel { 

/** 
* Save visited URL. We'll use it later to determine if tree node needs to be opened. 
* We decode the query string in URL so that token has a chance of matching (e.g., convert %20 to space). 
*/ 
private final String url = URL.decodeQueryString(Window.Location.getHref()); 

private final NavNode navNode; 
private final TokenService<MainEventBus> tokenService; 

/** 
* A selection model shared across all nodes in the tree. 
*/ 
private final SingleSelectionModel<NavNode> selectionModel = new SingleSelectionModel<NavNode>(); 

public CustomTreeModel(NavNode navNode, TokenService tokenService) { 
    this.navNode = navNode; 
    this.tokenService = tokenService; 
} 

@Override 
public <T> NodeInfo<?> getNodeInfo(T value) { 
    DefaultNodeInfo<NavNode> result = null; 
    if (value == null) { 
     // LEVEL 0. 
     // We passed null as the root value. Return the immediate descendants. 
     result = new DefaultNodeInfo<NavNode>(getDataProvider(navNode), getCell(), selectionModel, null); 

    } else if (value instanceof NavNode) { 
     // all other levels 
     // We pass a node, return its immediate descendants. 

     // select node if URL contains params in node's target or one of node's option's target 
     NavNode currNode = (NavNode) value; 
     if (isSelected(currNode)) { 
      selectionModel.setSelected(currNode, true); 
     } 
     if (currNode.hasOptions()) { // add pop-up menu to this node if it has options 
      result = new DefaultNodeInfo<NavNode>(getDataProvider(currNode), getCell(), selectionModel, new NodeSelectionEventManager(currNode), null); 
     } else { 
      result = new DefaultNodeInfo<NavNode>(getDataProvider(currNode), getCell(), selectionModel, null); 
     } 
    } 
    return result; 
} 

@Override 
public boolean isLeaf(Object value) { 
    boolean result = true; 
    if (value == null) { 
     if (navNode.hasChildren()) { 
      result = false; 
     } 
    } else if (value instanceof NavNode) { 
     NavNode currentNode = (NavNode) value; 
     if (currentNode.hasChildren()) { 
      result = false; 
     } 
    } 
    return result; 
} 

// Create a data provider that contains the immediate descendants. 
private ListDataProvider<NavNode> getDataProvider(NavNode node) { 
    return new ListDataProvider<NavNode>(node.getChildren()); 
} 

// Create a cell to display a descendant. 
private Cell<NavNode> getCell() { 
    Cell<NavNode> cell = new AbstractCell<NavNode>() { 
     @Override 
     public void render(Context context, NavNode value, SafeHtmlBuilder sb) { 
      if (value != null) { 
       sb.appendEscaped(value.getName()); 
      } 
     } 
    }; 
    return cell; 
} 

private boolean isSelected(NavNode node) { 
    boolean selected = false; 
    if (node != null) { 
     if (url.contains(tokenService.getToken(node))) { 
      selected = true; 
     } else { 
      for (NavOption option: node.getOptions()) { 
       if (url.contains(tokenService.getToken(option))) { 
        selected = true; 
        break; 
       } 
      } 
     } 
    } 
    return selected; 
} 

class NavNodeSelectionHandler implements SelectionChangeEvent.Handler { 

    private final VerticalPanel optionsContainer; 
    private final DecoratedPopupPanel optionsPopup; 

    public NavNodeSelectionHandler() { 
     optionsPopup = new DecoratedPopupPanel(true); 
     optionsContainer = new VerticalPanel(); 
     optionsContainer.setWidth("125px"); 

     // TODO provide a debug id... this will most likely necessitate generation of a unique key 
     optionsPopup.setWidget(optionsContainer); 
    } 

    @Override 
    public void onSelectionChange(SelectionChangeEvent event) { 
     NavNode node = selectionModel.getSelectedObject(); 
     for (NavOption option: node.getOptions()) { 
      optionsContainer.add(new Hyperlink(option.getName(), tokenService.getToken(option))); 
     } 
     // Reposition the popup relative to node 
     UIObject source = (UIObject) event.getSource(); 
     int left = source.getAbsoluteLeft() + 25; 
     int top = source.getAbsoluteTop(); 
     optionsPopup.setPopupPosition(left, top); 

     // Show the popup 
     optionsPopup.show(); 
    } 
} 


class NodeSelectionEventManager implements CellPreviewEvent.Handler<NavNode> { 

    private final VerticalPanel optionsContainer; 
    private final DecoratedPopupPanel optionsPopup; 

    public NodeSelectionEventManager(NavNode node) { 
     optionsPopup = new DecoratedPopupPanel(true); 
     optionsContainer = new VerticalPanel(); 
     optionsContainer.setWidth("125px"); 
     for (NavOption option: node.getOptions()) { 
      optionsContainer.add(new Hyperlink(option.getName(), tokenService.getToken(option))); 
     } 
     // TODO provide a debug id... this will most likely necessitate generation of a unique key 
     optionsPopup.setWidget(optionsContainer); 
    } 

    @Override 
    public void onCellPreview(CellPreviewEvent<NavNode> event) { 
     // Reposition the popup relative to node 
     UIObject source = (UIObject) event.getDisplay(); 
     int left = source.getAbsoluteLeft() + 25; 
     int top = source.getAbsoluteTop(); 
     optionsPopup.setPopupPosition(left, top); 

     // Show the popup 
     optionsPopup.show(); 

    } 

} 

}

我使用一個通用的bean(NavNode)可以幫助我確定當我有一個葉,當我有一個選項(NavOption)或選項包含一個目標用於超鏈接構建。

我希望當我點擊CellTree中的一個節點(TreeNode)時,出現一個彈出式菜單(DecoratedPopupPanel),但僅限於那些有選項的節點。

我試圖使用任何內部處理程序實現(構建一個DefaultNodeInfo)沒有成功。希望從上面的代碼示例中,您可以看到我想要做的事情。

下面是增加了一個SelectionChangeEvent.Handler到SingleSelectionModel

if (currNode.hasOptions()) { // add pop-up menu to this node if it has options 
      selectionModel.addSelectionChangeHandler(new NavNodeSelectionHandler()); 
      result = new DefaultNodeInfo<NavNode>(getDataProvider(currNode), getCell(), selectionModel, null); 
     } 

發生了什麼事是,投事件的嘗試失敗,一個ClassCastException的變體。
我想得到一個UIObject的句柄,所以我可以定位彈出窗口。我認爲我需要一個TreeNode的句柄,但看不到如何去做。

CellTree,TreeViewModel,SelectionModel和朋友是我遇到過的最鈍的API之一。

真的很感謝GWT專家的幫助!

+0

我有一個基於GWT樹的impl滿足上述用例,但它不適用於大樹。 – 2012-01-12 23:52:54

回答

0

我的一個聰明的同事能夠找到解決方案。

下面是我們繞了:

public class CustomTreeModel implements TreeViewModel { 

/** 
* Save visited URL. We'll use it later to determine if tree node needs to be opened. 
* We decode the query string in URL so that token has a chance of matching (e.g., convert %20 to space). 
*/ 
private final String url = URL.decodeQueryString(Window.Location.getHref()); 

private final NavNode navNode; 
private final TokenService<MainEventBus> tokenService; 

/** 
* A selection model shared across all nodes in the tree. 
*/ 
private final SingleSelectionModel<NavNode> selectionModel = new SingleSelectionModel<NavNode>(); 

public CustomTreeModel(NavNode navNode, TokenService tokenService) { 
    this.navNode = navNode; 
    this.tokenService = tokenService; 
} 

@Override 
public <T> NodeInfo<?> getNodeInfo(T value) { 
    DefaultNodeInfo<NavNode> result = null; 
    if (value == null) { 
     // LEVEL 0. 
     // We passed null as the root value. Return the immediate descendants. 
     result = new DefaultNodeInfo<NavNode>(getDataProvider(navNode), getCell(), selectionModel, null); 

    } else if (value instanceof NavNode) { 
     // all other levels 
     // We pass a node, return its immediate descendants. 

     // select node if URL contains params in node's target or one of node's option's target 
     NavNode currNode = (NavNode) value; 
     if (isSelected(currNode)) { 
      selectionModel.setSelected(currNode, true); 
     } 
     result = new DefaultNodeInfo<NavNode>(getDataProvider(currNode), getCell(), selectionModel, null); 
    } 
    return result; 
} 

@Override 
public boolean isLeaf(Object value) { 
    boolean result = true; 
    if (value == null) { 
     if (navNode.hasChildren()) { 
      result = false; 
     } 
    } else if (value instanceof NavNode) { 
     NavNode currentNode = (NavNode) value; 
     if (currentNode.hasChildren()) { 
      result = false; 
     } 
    } 
    return result; 
} 

// Create a data provider that contains the immediate descendants. 
private ListDataProvider<NavNode> getDataProvider(NavNode node) { 
    return new ListDataProvider<NavNode>(NavNodeUtil.getHeadedChildren(node.getChildren(), 1)); 
} 

// Create a cell to display a descendant. 
private Cell<NavNode> getCell() { 
    return new TreeCell(); 
} 

private boolean isSelected(NavNode node) { 
    boolean selected = false; 
    if (node != null) { 
     if (url.contains(tokenService.getToken(node))) { 
      selected = true; 
     } else { 
      for (NavOption option: node.getOptions()) { 
       if (url.contains(tokenService.getToken(option))) { 
        selected = true; 
        break; 
       } 
      } 
     } 
    } 
    return selected; 
} 

class TreeCell extends AbstractCell<NavNode> { 

    public TreeCell() { 
     super("click", "keydown"); 
    } 

    @Override 
    public void onBrowserEvent(Context context, Element parent, NavNode currNode, 
      NativeEvent event, ValueUpdater<NavNode> valueUpdater) { 
     // Check that the value is not null. 
     if (currNode == null) { 
      return; 
     } 

     if (currNode.hasOptions()) { // add pop-up menu to this node if it has options 
      final DecoratedPopupPanel optionsPopup = new DecoratedPopupPanel(true); 
      final VerticalPanel optionsContainer = new VerticalPanel(); 
      optionsContainer.setWidth("125px"); 
      for (NavOption option: currNode.getOptions()) { 
       optionsContainer.add(new Hyperlink(option.getName(), tokenService.getToken(option))); 
      } 
      // TODO provide a debug id... this will most likely necessitate generation of a unique key 
      optionsPopup.setWidget(optionsContainer); 
      // Reposition the popup relative to node 
      final int left = parent.getAbsoluteLeft() + 25; 
      final int top = parent.getAbsoluteTop(); 

      optionsPopup.setPopupPositionAndShow(new PopupPanel.PositionCallback() { 
       @Override 
       public void setPosition(int offsetWidth, int offsetHeight) { 
        optionsPopup.setPopupPosition(left, top); 
       } 
      }); 
     } 

     super.onBrowserEvent(context, parent, currNode, event, valueUpdater); 
    } 

    @Override 
    public void render(Context context, NavNode value, SafeHtmlBuilder sb) { 
     if (value != null) { 
      sb.appendEscaped(value.getName()); 
     } 
    } 
} 

}

注意TreeCell覆蓋onBrowserEvent。從這裏我們可以獲得節點的句柄並定位彈出窗口。彈出窗口用回調實例化。奇怪的!

NavNodeUtil在計算孩子的時候會有一些魔力,併爲節點的孩子添加A,B,C ... Z分類標題,這些分類標題超過了某個閾值。

相關問題