does anyone know how to trigger the "Show More" functionality of a GWT CellTree programmatically, without having to click on the Show More button?
My aim is to implement a kind of pager that increments the number of elements displayed when the user scrolls down a ScollPanel, so it would be something like:
//inside pager class
onScroll(ScrollEvent)
{
//here I would call CellTree's show more
}
I've been looking the CellTree and CellTreeNodeView classes code but I couldn't find a clear way to do it.
I know the class CellTreeNodeView has a showMore function which is the one who performs this action, but I don't know how to get it called from another class. I'd need a CellTreeNodeView object, and dont' know how to get it.
Thanks!
It is a package protected method in a package protected class CellTreeNodeView i.e only code in com.google.gwt.user.cellview.client can invoke it.
void showMore()
Extremely hacky solution
1) The only way around it is . Copy CellTreeNodeView and CellTree into your code base (maintain the package )
2) Change the accessors to public to allow you to invoke showMore as per your requirement.
3) Ensure you test for all possible flows.
4) Ensure the copied classes in your code base appear in a higher classpath hieararchy to GWT Compiler than gwt-user jar thus ensuring your modified classes get picked up rather than original ones.
Finally I got it working exactly as I wanted, and without having to copy the code from the protected original GWT.
The point was firing the same event as the "Show more" button, so I created a fake onMouseDown event, and triggered it with the show more button as the target:
final ScrollPanel sp = new ScrollPanel();
sp.addScrollHandler(new ScrollHandler() {
#Override
public void onScroll(ScrollEvent event)
{
int maxScrollBottom = sp.getWidget().getOffsetHeight()
- sp.getOffsetHeight();
if (sp.getVerticalScrollPosition() >= maxScrollBottom) {
NativeEvent clickEvent = Document.get().createMouseDownEvent(0,0,0,0,0,false,false,false,false,0);
Element target = (Element) cellTree.getCellTree().getElement().getLastChild().getFirstChild().getLastChild();
target.dispatchEvent(clickEvent);
}
}
});
Thank you a lot, anyway! :D
My workaround is this one:
public static void makeShowMoreVisible(Element element, boolean isVisible) {
ArrayList<Element> result = new ArrayList<Element>();
findShowMore(result, element);
for (Element elt : result) {
if (isVisible) {
element.getStyle().clearDisplay();
} else {
element.getStyle().setDisplay(Display.NONE);
}
}
}
private static void findShowMore(ArrayList res, Element element) {
String c;
if (element == null) {
return;
}
if (element.getInnerText().equals("Show more")) {
res.add(element);
}
for (int i = 0; i < DOM.getChildCount(element); i++) {
Element child = DOM.getChild(element, i);
findShowMore(res, child);
}
}
Related
we would like to link from a CellTable to a property editor page. We use the SingleSelectionModel to get notified, when a user clicks on an item.
It is initialized like this:
private final SingleSelectionModel<Device> selectionModel = new SingleSelectionModel<Device>();
We then assign the selection change handler:
selectionModel.addSelectionChangeHandler(this);
Our selection change handler looks like this:
#Override
public void onSelectionChange(SelectionChangeEvent event) {
Log.debug("DevicesPresenter: SelectionChangeEvent caught.");
Device selectedDevice = selectionModel.getSelectedObject();
if (selectedDevice != null) {
selectionModel.clear();
if (selectionModel.getSelectedObject() != null){
Log.debug("DevicesPresenter: selected item is " + selectionModel.getSelectedObject());
}
else{
Log.debug("DevicesPresenter: selected item is null");
}
deviceEditorDialog.setCurrentDevice(selectedDevice.getUuid());
// get the container data for this device
clientModelProvider.fetchContainersForDevice(selectedDevice.getUuid());
PlaceRequest request = new PlaceRequest.Builder()
.nameToken(NameTokens.deviceInfo)
.with("uuid", selectedDevice.getUuid())
.build();
Log.debug("Navigating to " + request.toString());
placeManager.revealPlace(request);
}
}
Now there are two issues: There always seem to be two SelectionChangeEvents at once and i really cannot see why. The other thing is: How is the right way do handle selection of items and the related clearing of the selection model? Do we do that the right way?
Thanks!
If you only want to get notified of "clicks" without keeping the "clicked" item selected, use a NoSelectionModel instead; no need to clear the selection model as soon as something is selected.
As for your other issue with being called twice, double-check that you haven't added your selection handler twice (if you can unit-test your DevicesPresenter, introspect the handlers inside the selection model for example)
In your line selectionModel.addSelectionChangeHandler(this); what does this refer?
Here my code how I use SingleSelectionModel
public class MyClass{
private final SingleSelectionModel<CountryDto> selectionModel = new SingleSelectionModel<CountryDto>();
...
public MyClass(){
cellTable.setSelectionModel(selectionModel);
selectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
#Override
public void onSelectionChange(SelectionChangeEvent event) {
CountryDto selected = selectionModel
.getSelectedObject();
if (selected != null) {
Window.alert("Selected country "+selected.getTitle());
}
}
});
}
}
So I have created a CellTree and what I want to do is select the cell that receives a right click so that when I open my context menu to do things, I will know what cell I am working with. Maybe I am going about it the wrong way, I can override the onBrowserEvent method and detect when someone right clicks on the tree but I can't figure out which cell is being clicked so I can manually select it. Has anyone found a solution for this problem?
The solution consists of two steps:
1)
Add a TreeViewModel to the constructor of your CellTree. With that model you can set the names of your elements in the tree. Here is a simple implementation from the API:
private static class CustomTreeModel implements TreeViewModel {
/**
* Get the {#link NodeInfo} that provides the children of the specified
* value.
*/
public <T> NodeInfo<?> getNodeInfo(T value) {
/*
* Create some data in a data provider. Use the parent value as a prefix
* for the next level.
*/
ListDataProvider<String> dataProvider = new ListDataProvider<String>();
for (int i = 0; i < 2; i++) {
dataProvider.getList().add(value + "." + String.valueOf(i));
}
// Return a node info that pairs the data with a cell.
return new DefaultNodeInfo<String>(dataProvider, new TextCell());
}
/**
* Check if the specified value represents a leaf node. Leaf nodes cannot be
* opened.
*/
public boolean isLeaf(Object value) {
// The maximum length of a value is ten characters.
return value.toString().length() > 10;
}
}
2) When you receive the right click Event, get the EventTarget name and compare it with the name of that item you set using the model.
I found a solution, I hope this helps others as I have been searching for this for a long time. There might be a better way, but here is how I accomplished the functionality that I desired:
In the cells that I used inside my tree, I did an override on the onbrowserevent to catch the mouse events and set the selection model. With abstract cells you can sink events you want it to listen to and in my case I chose mouse down.
public class CustomContactCell extends AbstractCell<ContactInfo> {
private SetSelectionModel<ContactInfo> selectionModel;
public CustomContactCell(SetSelectionModel<ContactInfo> selectionModel) {
super("mousedown");
this.selectionModel = selectionModel;
}
#Override
public void render(Context context, ContactInfo value, SafeHtmlBuilder sb) {
...
}
#Override
public void onBrowserEvent(com.google.gwt.cell.client.Cell.Context context, Element parent, ContactInfo value, NativeEvent event, ValueUpdater<ContactInfo> valueUpdater) {
if (event.getButton() == NativeEvent.BUTTON_RIGHT) {
if (selectionModel != null) {
selectionModel.clear();
selectionModel.setSelected(value, true);
}
}
super.onBrowserEvent(context, parent, value, event, valueUpdater);
}
}
I've been looking through GXT3's Tree API for some way to execute an action when I click or double click on a node in a tree, and I can't seem to find anything that would work.
I know TreeGrid has a CellClickHandler and CellDoubleClick handler, but there doesn't seem to be anything similar for Tree. There's the generic addHandler method inherited from Widget but this seems like it would apply to the whole tree, not a specific node.
Is there something I'm overlooking, or a different / better way to do this?
use the TreePanel's selection model:
treePanel.getSelectionModel().addSelectionChangedListener(
new SelectionChangedListener<BaseTreeModel>() {
#Override
public void selectionChanged(SelectionChangedEvent<BaseTreeModel> se) {
BaseTreeModel selectedItem = se.getSelectedItem();
// implement functionality
}
}
);
see the TreePanel API for a reference.
Use this for Single Selection
tree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
tree.getSelectionModel().addSelectionHandler(new SelectionHandler<MenuView.MenuDto>() {
public void onSelection(SelectionEvent<MenuDto> event) {
MenuDto mnu = event.getSelectedItem();
Info.display("Tree Handler", mnu.getDescripcion());
}
});
For Multiple Selections
tree.getSelectionModel().addSelectionChangedHandler(new SelectionChangedHandler<MenuView.MenuDto>() {
public void onSelectionChanged(SelectionChangedEvent<MenuDto> event) {
List<MenuDto> mnus = event.getSelection();
Info.display("Tree Handler", mnus.get(0).getDescripcion());
}
});
Another option is to override Tree's onDoubleClick (or onClick) method:
Tree tree = new Tree<MyModel, String>(store, valueProvider){
#Override
protected void onDoubleClick(Event event) {
TreeNode<MyModel> node = findNode(event.getEventTarget().<Element> cast());
Info.display("Double Click", "You double clicked this node!");
super.onDoubleClick(event);
}
};
Figured it out.This can be achieved by using the Cell Action Tree, an implementation of which can be found here: http://www.sencha.com/examples/#ExamplePlace:cellactiontree
I am new to GWT and the web stuff.
I am working out my own project based on
http://code.google.com/p/cloud-tasks-io/source/browse/#svn%2Ftrunk%2FCloudTasks-AppEngine%2Fsrc%2Fcom%2Fcloudtasks%2Fclient
I am trying to use popup/dialog. The popup and dialog always show behind the widget. I keep googling around and the most relevant I found is this
http://groups.google.com/group/gwt-google-apis/browse_thread/thread/40db4fcbe10d2060 which does not provide any answer. Anyway, I have 3rd party library, bst-player 1.3, which uses flash. So I disabled it(later remove it too), the popup just won't come to the top! It is still hiding behind the widget.
I have learned that popuppanel/dialogpanel alikes do not need to get added to another widget. A different way of saying is that it is not a normal widget in a sense that it cannot attach to a parent but it attaches itself to the dom to guarantee being on top (from GWT composite widget )
I am at my wit end and I am here at SO ...
UPDATE
Here is my Popup class
public class PopUp {
static PopupPanel simplePopup;
public static void init() {
simplePopup = new PopupPanel(true);
simplePopup.hide();
simplePopup.setVisible(false);
// DOM.setIntStyleAttribute(simplePopup.getElement(), "zIndex", 3);
}
public static void showpopupmsg(String msg, int left, int top) {
if (simplePopup == null) {
init();
}
if (msg != null && !msg.equalsIgnoreCase("")) {
simplePopup.ensureDebugId("cwBasicPopup-simplePopup");
simplePopup.setWidget(new HTML(msg));
simplePopup.setVisible(true);
simplePopup.setPopupPosition(left, top);
simplePopup.setWidth("475px"); //575
simplePopup.setGlassEnabled(true);
simplePopup.show();
}
}
public static void show(String message){
if (simplePopup == null) {
init();
}
simplePopup.setGlassEnabled(true);
simplePopup.setTitle(message);
simplePopup.center();
}
}
Here is how I am calling
tasksTable.doneColumn.setFieldUpdater(new FieldUpdater<TaskProxy, Boolean>() {
public void update(int index, TaskProxy task, Boolean value) {
String msg = "Here is the popup. All the way underneath";
Widget source = tasksTable;
int left = source.getAbsoluteLeft() - 50;
// source.getAbsoluteLeft() + 25;
int top = source.getAbsoluteTop() - 25;
PopUp.showpopupmsg(msg, left, top); //Here is the calling method
TaskRequest request = requestFactory.taskRequest();
TaskProxy updatedTask = request.edit(task);
updatedTask.setDone(value);
request.updateTask(updatedTask).fire();
}
});
Here is how the Popup is beneath the widget.
The source of the problem has been quite elusive since I am still new to the webapp, yet, I finally solve it myself. The culprit is the CSS. It is defining the z-index for the whole thing to quite high as seen in the following code line 1333.
http://code.google.com/p/cloud-tasks-io/source/browse/trunk/CloudTasks-AppEngine/war/CloudTasks.css#1333
I have doubted about the z-index before and try it out with a paltry value 3 as seen in the commented out code segment of Popup class in question. I have to uncomment it and set it to 101.
DOM.setIntStyleAttribute(simplePopup.getElement(), "zIndex", 101);
I was , you know, #$%###$*.
z-index is only decides which widget should show on top..
the widget popup is under benath might be having z-index value high.
set the z-index for popup thru css (recomended) or DOM will work for you
According to my feeling, using static methods of your "PopUp" object is a bit strange...
In that way, I think things a relative to the top rather than caller object.
Maybe you could consider make your class 'Popup' extending 'popupanel'
and in your calling code, just make
new PopUp(msg,left,top).show() ;
I recently wrote my own solution for a popup panel that needs to be aligned with its caller. The solution consists out of an PopupPanel extension and a Button extension.
The button extension has an instance of the panel extension, and the moment it is clicked it gives its coordinates and width and height to its panel.
#Override
public void onClick(ClickEvent event) {
if (optionsPanel.isShowing()) {
optionsPanel.hide();
} else {
optionsPanel.setButtonBounds(new Bbox(
getAbsoluteLeft(), getAbsoluteTop(), getOffsetWidth(), getOffsetHeight()));
optionsPanel.show();
}
}
(The Bbox class is just a convenience class I could use for wrapping coordinates; write your own class or 4 methods for that matter)
The main work is then done in the onLoad() override of the PopupPanel, in which the coordinates of the button are used to position the panel;
#Override
protected void onLoad() {
super.onLoad();
if (null == bounds) {
super.hide();
} else {
left = (int) bounds.getX();
top = (int) bounds.getMaxY();
setPopupPosition(left, top);
}
}
(bounds are the coordinates of the button; getMaxY() == bottom coordinate of button)
I have a CellTable with one custom column where I render it manually and put a FlowPanel with a bunch of HTMLPanel/Anchor/FlowPanel widgets, and among them is DecoratorPanel.
DecoratorPanel renders as a table, of course.
Rendering happens like this:
public class MyExpandableCell extends AbstractCell<String> {
...
#Override
public void render(com.google.gwt.cell.client.Cell.Context context, String value, SafeHtmlBuilder sb) {
if (value != null) {
FlowPanel fp = createDetailsFlowPanel(value);
sb.appendHtmlConstant(fp.getElement().getString());
}
}
Now, I have added a handler for click events to my main CellTable. In my handler I traverse the tree to find a first TR belonging to my CellTable, by checking if it contains "even row" or "odd row" CSS classes.
However, when click happens inside of the DecoratorPanel (which is inside of my cell table's TD), my handler also gets triggered, since the click belongs to the cell table area.
I can detect this my seeing that parent TR does not have CellTable CSS classes.
QUESTION: how can I return processing of such click events to the DecoratorPanel - where it really belongs to? As it is now, my DecoratorPanel does not expand and I think because my click handler intercepts and suppresses all clicks on the CellTable level.
table = setupMyCellTable(PAGE_SIZE, CLIENT_BUNDLE, myCellTableResources);
mydataprovider.addDataDisplay(table);
table.sinkEvents(Event.ONCLICK);
table.addDomHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent e) {
boolean isClick = "click".equals(e.getNativeEvent().getType());
if (isClick) {
Element originalElement = e.getNativeEvent().getEventTarget().cast();
Element element = originalElement;
String ctTrClassEven = CLIENT_BUNDLE.mainCss().cellTableEvenRow();
String ctTrClassEven = CLIENT_BUNDLE.mainCss().cellTableOddRow();
// Looking for closest parent TR which has one
// of the two row class names (for this cellTable):
while (element != null
&& !"tr".equalsIgnoreCase(element.getTagName())
&& !(element.getClassName().contains(ctTrClassEven) ||
element.getClassName().contains(ctTrClassEven))) {
element = element.getParentElement();
}
if (element != null) {
if (element.getClassName().contains(ctTrClassEven)
|| element.getClassName().contains(ctTrClassEven)) {
// Found cell table's TR. Set new TR height.
} else {
// Found TR from DecoratorPanel inside of the celltable's cell.
// Do nothing. But how do I make sure
// that decorator panel processes this click and expands?
return;
// Did not work: NS_ERROR_ILLEGAL_VALUE javascript exception.
// if (originalElement != null) {
// originalElement.dispatchEvent(e.getNativeEvent());
// }
}
} else {
debugStr.setText("(did not find tr)");
}
}
}
}, ClickEvent.getType());
Looks like a bug in GWT, triggered because decorator panel uses table to render itself:
http://code.google.com/p/google-web-toolkit/issues/detail?id=5714
(another example http://code.google.com/p/google-web-toolkit/issues/detail?id=6750)
Fix is expected to be shipped with GWT 2.5.