具有可选弹出菜单的GWT CellTree单击TreeNode触发 [英] GWT CellTree with an optional pop-up menu triggered on click of a TreeNode

查看:112
本文介绍了具有可选弹出菜单的GWT CellTree单击TreeNode触发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想制作一个GWT CellTree,点击TreeNode触发可选的弹出菜单。



所以我制作了一个CustomTreeModel。这里是:

  public class CustomTreeModel实现TreeViewModel {

/ **
*保存访问的URL稍后我们将使用它来确定是否需要打开树节点。
*我们对URL中的查询字符串进行解码,以便令牌有机会匹配(例如,将%20转换为空格)。
* /
private final String url = URL.decodeQueryString(Window.Location.getHref());

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

/ **
*树中所有节点共享的选择模型。
* /
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.
//我们作为根值传递null。返回直系后代。
result = new DefaultNodeInfo&Navigator>(getDataProvider(navNode),getCell(),selectionModel,null);

} else if(value instanceof NavNode){
//所有其​​他级别
//我们传递一个节点,返回其直接后代。

//选择节点如果URL包含节点目标中的参数或节点选项的目标之一
NavNode currNode =(NavNode)value;
if(isSelected(currNode)){
selectionModel.setSelected(currNode,true);
}
if(currNode.hasOptions()){//将弹出菜单添加到此节点,如果它有选项
result = new DefaultNodeInfo< NavNode>(getDataProvider(currNode),getCell (),selectionModel,新的NodeSelectionEventManager(currNode),null);
} else {
result = new DefaultNodeInfo&Navigator>(getDataProvider(currNode),getCell(),selectionModel,null);
}
}
返回结果;
}

@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;
}
}
返回结果;
}

//创建一个包含即时后代的数据提供者。
private ListDataProvider< NavNode> getDataProvider(NavNode node){
return new ListDataProvider< NavNode>(node.getChildren());
}

//创建一个单元格来显示一个后代。
private Cell< NavNode> getCell(){
单元格< NavNode> cell = new AbstractCell< NavNode>(){
@Override
public void render(Context context,NavNode value,SafeHtmlBuilder sb){
if(value!= null){
sb.appendEscaped(value.getName());
}
}
};
返回单元格;
}

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

类NavNodeSelectionHandler实现SelectionChangeEvent.Handler {

private final VerticalPanel optionsContainer;
private final DecoratedPopupPanel optionsPopup;

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

// TODO提供调试ID ...这很可能需要生成一个唯一的键
optionsPopup.setWidget(optionsContainer);
}

@Override
public void onSelectionChange(SelectionChangeEvent event){
NavNode node = selectionModel.getSelectedObject(); (NavOption选项:node.getOptions())的
{
optionsContainer.add(new Hyperlink(option.getName(),tokenService.getToken(option)))
}
//相对于节点
重新定位弹出窗口UIObject source =(UIObject)event.getSource();
int left = source.getAbsoluteLeft()+ 25;
int top = source.getAbsoluteTop();
optionsPopup.setPopupPosition(left,top);

//显示弹出式窗口
optionsPopup.show();
}
}


class NodeSelectionEventManager实现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); (NavOption选项:node.getOptions())的
{
optionsContainer.add(new Hyperlink(option.getName(),tokenService.getToken(option)))
}
// TODO提供调试ID ...这很可能需要生成一个唯一的键
optionsPopup.setWidget(optionsContainer);
}

@Override
public void onCellPreview(CellPreviewEvent< NavNode> event){
//将弹出窗口重定位到节点
UIObject source = UIObject)event.getDisplay();
int left = source.getAbsoluteLeft()+ 25;
int top = source.getAbsoluteTop();
optionsPopup.setPopupPosition(left,top);

//显示弹出式窗口
optionsPopup.show();

}

}

}

我使用通用bean(NavNode)来帮助我确定何时有叶子,当我有一个选项(NavOption)或包含用于超链接的目标的选项我想要,当我点击CellTree中的一个节点(TreeNode)时,出现一个弹出菜单(DecoratedPopupPanel),但只有那些节点有选项。



我已经尝试使用内部Handler实现(在构造DefaultNodeInfo)中没有成功。希望从上面的代码示例中可以看到我想要做的。



这是一个将SelectionChangeEvent.Handler添加到SingleSelectionModel的变体

  if(currNode.hasOptions()){//将弹出菜单添加到此节点,如果它有选项
selectionModel.addSelectionChangeHandler(new NavNodeSelectionHandler ());
result = new DefaultNodeInfo&Navigator>(getDataProvider(currNode),getCell(),selectionModel,null);
}

正在发生的事情是尝试使用ClassCastException失败。 br>
我想在UIObject上获取一个句柄,所以我可以放置弹出窗口。我想我需要一个TreeNode上的句柄,但看不到如何做。



CellTree,TreeViewModel,SelectionModel和朋友是一些最钝的API我'我遇到了



真的很感激GWT专家的帮助!

解决方案

我的一个聪明的同事能够找到一个解决方案。



这是我们清理的:

  public class CustomTreeModel implements TreeViewModel {

/ **
*保存访问的URL。稍后我们将使用它来确定是否需要打开树节点。
*我们对URL中的查询字符串进行解码,以便令牌有机会匹配(例如,将%20转换为空格)。
* /
private final String url = URL.decodeQueryString(Window.Location.getHref());

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

/ **
*树中所有节点共享的选择模型。
* /
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.
//我们作为根值传递null。返回直系后代。
result = new DefaultNodeInfo&Navigator>(getDataProvider(navNode),getCell(),selectionModel,null);

} else if(value instanceof NavNode){
//所有其​​他级别
//我们传递一个节点,返回其直接后代。

//选择节点如果URL包含节点目标中的参数或节点选项的目标之一
NavNode currNode =(NavNode)value;
if(isSelected(currNode)){
selectionModel.setSelected(currNode,true);
}
result = new DefaultNodeInfo&Navigator>(getDataProvider(currNode),getCell(),selectionModel,null);
}
返回结果;
}

@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;
}
}
返回结果;
}

//创建一个包含即时后代的数据提供者。
private ListDataProvider< NavNode> getDataProvider(NavNode node){
return new ListDataProvider< NavNode>(NavNodeUtil.getHeadedChildren(node.getChildren(),1));
}

//创建一个单元格来显示一个后代。
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选项:node.getOptions()){
if(url.contains(tokenService.getToken(option))){
selected = true;
break;
}
}
}
}
返回选择;
}

class TreeCell扩展AbstractCell< NavNode> {

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


@Override
public void onBrowserEvent(Context context,Element parent,NavNode currNode,
NativeEvent事件,ValueUpdater< NavNode> valueUpdater){
//检查该值是否为空。
if(currNode == null){
return;
}

if(currNode.hasOptions()){//将弹出菜单添加到此节点,如果它有选项
final DecoratedPopupPanel optionsPopup = new DecoratedPopupPanel(true);
final VerticalPanel optionsContainer = new VerticalPanel();
optionsContainer.setWidth(125px);
(NavOption选项:currNode.getOptions()){
optionsContainer.add(new Hyperlink(option.getName(),tokenService.getToken(option)));
}
// TODO提供一个调试ID ...这很可能需要生成一个唯一的键
optionsPopup.setWidget(optionsContainer);
//将弹出窗口重新定位到节点
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,顶部);
}
});
}

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个分类标题。 / p>

I would like to craft a GWT CellTree with an optional pop-up menu triggered on click of a TreeNode.

So I've crafted a CustomTreeModel. Here it is:

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();

    }

}

}

I'm using a generic bean (NavNode) to help me determine when I have a leaf and when I have an option (NavOption) or options that contain a target used for Hyperlink construction.

I want, when I click on a node (TreeNode) in the CellTree, that a pop-up menu (DecoratedPopupPanel) appears, but only for those nodes that have options.

I have tried to employ either of the inner Handler implementations (on construction of a DefaultNodeInfo) to no success. Hopefully from the above code sample you can see what I'm trying to do.

Here's a variant that adds a SelectionChangeEvent.Handler to 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);
        }

What's happening is that the attempt to cast the Event fails with a ClassCastException.
I want to get a handle on an UIObject so I can position the popup. I think I need a handle on a TreeNode, but cannot see how to do it.

The CellTree, TreeViewModel, SelectionModel and friends are some of the most obtuse API I've come across.

Would really appreciate some help from a GWT expert!

解决方案

A smart colleague of mine was able to sleuth a solution.

Here's what we wound up with:

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());
        }
    }
}

}

Note the TreeCell overrides the onBrowserEvent. From here we can get a handle on the node and position the pop-up. The pop-up is instantiated with a callback. Weird!

NavNodeUtil does some magic where it counts the children and adds A,B,C...Z categorical headings for a node's children that exceed a certain threshold.

这篇关于具有可选弹出菜单的GWT CellTree单击TreeNode触发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆