GWT CellList; programmatical scrolling between elements options - gwt

I would like to understand the options available to scrolling to a specific element in a CellList? Currently I have a 100 elements in the list and need to "jump" through the elements at the click of a button but can't seems to locate any methods on the celllist (or in the code) that provides this feature.
Any Ideas?
Many Thanks in advance,
Ian.
**EDIT
working code example below;
public class CellListTest implements EntryPoint {
private CellList<String> cellList;
private SingleSelectionModel<String> stringSingleSelectionModel;
/**
* This is the entry point method.
*/
public void onModuleLoad() {
cellList = new CellList<String>(new TextCell());
cellList.setRowData(buildStringList(200));
cellList.setKeyboardSelectionPolicy(HasKeyboardSelectionPolicy.KeyboardSelectionPolicy.BOUND_TO_SELECTION);
Button byTen = new Button("Jump Forward 10");
stringSingleSelectionModel = new SingleSelectionModel<String>();
cellList.setSelectionModel(stringSingleSelectionModel);
byTen.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
jumpForward(10);
}
});
byTen.setHeight("30px");
HTMLPanel htmlPanel = new HTMLPanel("");
VerticalPanel verticalPanel = new VerticalPanel();
cellList.setHeight("600px");
ScrollPanel scrollPanel = new ScrollPanel(cellList);
verticalPanel.add(byTen);
verticalPanel.add(scrollPanel);
htmlPanel.add(verticalPanel);
RootLayoutPanel.get().add(htmlPanel);
}
final Random random = new Random();
private List<String> buildStringList(int numberToCreate) {
final ArrayList<String> randomValues = new ArrayList<String>();
for (int i = 0; i < numberToCreate; i++) {
randomValues.add(String.valueOf(random.nextInt()));
}
return randomValues;
}
private int currentPosition = 0;
private void jumpForward(int toJump) {
Element rowElement = cellList.getRowElement(currentPosition += toJump);
rowElement.scrollIntoView();
}
}

I do not think CellList has a direct method for your purpose.
What you can do is use Element's scrollIntoView method. This method adjusts the scrollLeft and scrollTop properties of each scrollable element to ensure that the specified element is completely in view. In order to use that method you need to get the element containing the cell you want to show. One way to achive this is by using CellList public getRowElement(int indexOnPage).
I have not tryed it, but I believe the following code should work:
//Ensures cell 22 on page is shown
Element element = myCellList.getRowElement(22);
element.scrollIntoView();

Scrolling the element into view is one thing; changing the keyboard focus to a particular element is quite another. After much exploration, I have found that the best way to achieve that is to fire native events to simulate the user pressing keys.
private void hitKeyOnList(int keyCode) {
NativeEvent keyDownEvent = Document.get().createKeyDownEvent(
false, false, false, false, keyCode);
list.getElement().dispatchEvent(keyDownEvent);
}
In the above snippet, the list variable is a reference to a CellList.
So, to move the cursor to the end of the list, do this:
hitKeyOnList(KeyCodes.KEY_END);
to move the cursor to the next item down from the one that currently has keyboard focus:
hitKeyOnList(KeyCodes.KEY_DOWN);
This approach should work for all of these keys, which are handled by AbstractHasData:
KeyCodes.KEY_DOWN
KeyCodes.KEY_UP
KeyCodes.KEY_PAGEDOWN
KeyCodes.KEY_PAGEUP
KeyCodes.KEY_HOME
KeyCodes.KEY_END

Related

GWT - Finding which button is checked in a Dynamically generated list of Radiobuttons

I have a for loop that displays a list of text fields and radio buttons.
What is the best way to reference the widgets so that I can read the text fields and aslo find which radio button is checked.
Here is my loop
for(int x = 0; x<getLoopCount(); x++)
{
answerTable.setWidget(x,0, new Label("Answer:"));
answerTable.setWidget(x,1, new TextBox());
answerTable.setWidget(x,2, new RadioButton(""));
}
Is there a way to ID each widget so I can reference it?
I would recommend grouping the three widgets together in a composite widget like this:
class AnswerComposite extends Composite {
private final Label label;
private final TextBox textBox;
private final RadioButton radioButton;
public AnswerComposite() {
label = new Label("Answer:");
textBox = new TextBox();
radioButton = new RadioButton("answerGroup");
HorizontalPanel contentPanel = new HorizontalPanel();
contentPanel.add(label);
contentPanel.add(textBox);
contentPanel.add(radioButton);
initWidget(contentPanel);
}
public String getText() {
return textBox.getValue();
}
public boolean isSelected() {
return radioButton.getValue();
}
}
You can then add them to a panel and/or put them in a list like this:
VerticalPanel answersPanel = new VerticalPanel();
List<AnswerComposite> answerComposites = new ArrayList<AnswerComposite>();
for (int i = 0; i < getLoopCount(); i++) {
AnswerComposite answerComposite = new AnswerComposite();
answersPanel.add(answerComposite);
answerComposites.add(answersComposite);
}
Checking your widgets then becomes very easy:
answerComposites.get(i).getText();
answerComposites.get(i).isSelected();
It will probably also be convenient to add a ValueChangeHandler to your RadioButtons (see enrybo's answer).
You can add a ValueChangeHandler to your RadioButton when you are creating them.
for(int x = 0; x<getLoopCount(); x++){
answerTable.setWidget(x,0, new Label("Answer:"));
answerTable.setWidget(x,1, new TextBox());
RadioButton rb = new RadioButton("");
rb.addValueChangeHandler(new ValueChangeHandler(){
#Override
void onValueChange(ValueChangeEvent<Boolean> event){
// Do something
}
});
answerTable.setWidget(x,2, rb);
}
The ValueChangeEvent will only be fired when the RadioButton is checked. It will not fire if another RadioButton in the same group is checked.
Since you're adding the ValueChangeHandler as you're creating your RadioButton you should know what is to be done with it without having to create an ID for it.
Let me give you an adhoc answer, so don't care about the syntax but the algorithmic idea.
Extend GWT button.
abstract class MyButton
extends Button{
// provide the appropriate constructor in impl class,
// especially if using uibinder
abstract public void helloDolly(... args ...);
}
Instantiate all those buttons using MyButton.
MyButton[] buttons = {
new MyButton(){
public void helloDolly(... args ...){
Window.alert("allo allo #1");
}
},
new MyButton(){
public void helloDolly(... args ...){
Window.alert("allo allo #2");
}
},
// blah blah black sheep ....
}
Use clickEvent.getSource() when defining handler.
buttons[i].addEventHandler(
new ClickHandler(ClickEvent click){
Object src = click.getSource();
if (src !instanceOf MyButton){
throw new MyAngryException("For goodness' sake, pls use MyButton");
// or ignore
return;
}
((MyButton)src).helloDolly(... args ...);
}
)

CellTree "Show More" programmatically

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

GWT - Reference to a global variable set by callback method

Please help me, as I will go mad with this soon:
When I run the code, on first occasion loadNewPoint() is executed and displays some data from global variable - allPointsAndPlaces
However when I click a button (from a child class), the same method loadNewPoint() gives me null pointer for allPointsAndPlaces.
I have changed the code structure a lot from an original trying to solve this issue, and moved this method (loadNewPoint()) to a parent class to see, if it would solve the issue.
Parent class:
public class CabbieApp implements EntryPoint {
private GetLocationsServiceAsync getAllLocationsService = GWT.create(GetLocationsService.class);
CabbiePoint[] allPointsAndPlaces;
PointsQuiz quiz;
/**
* Entry point method.
*/
public void onModuleLoad() {
//Get all the required data from DB
getAllPointsAndLocations();
}
private void loadAppPages(){
// Associate the Main panel with the HTML host page.
RootPanel rootPanel = RootPanel.get("pointsList");
quiz = new PointsQuiz();
rootPanel.setStyleName("GWTapp");
rootPanel.add(quiz.getMainPanel());
loadNewPoint();
}
private void getAllPointsAndLocations() {
// Initialize the service proxy.
if (getAllLocationsService == null) {
getAllLocationsService = GWT.create(GetLocationsService.class);
}
// Set up the callback object.
AsyncCallback<CabbiePoint[]> callback = new AsyncCallback<CabbiePoint[]>() {
public void onFailure(Throwable caught) {
System.out.println(caught.getMessage());
}
public void onSuccess(CabbiePoint[] result) {
//allPointsAndPlaces = result;
System.out.println(result.length);
allPointsAndPlaces = result;
loadAppPages();
}
};
// Make the call to the service.
getAllLocationsService.getLocations(callback);
}
void loadNewPoint(){
int r = Random.nextInt(allPointsAndPlaces.length);
quiz.CurrentPlace = allPointsAndPlaces[r].getPlaceName();
quiz.CurrentLocation = allPointsAndPlaces[r].getPlaceLocation();
quiz.point.setText(quiz.CurrentPlace);
quiz.location.setText(quiz.CurrentLocation);
quiz.location.setStyleName("invisibleText");
}
}
Child class:
public class PointsQuiz extends CabbieApp{
VerticalPanel mainPanel = new VerticalPanel();
HorizontalPanel navigation = new HorizontalPanel();
TextBox point = new TextBox();
TextBox location = new TextBox();
Button showLocation = new Button("Show Location");
Button nextPoint = new Button("Next Point");
String CurrentPlace, CurrentLocation;
public PointsQuiz() {
// Assemble Add Stock panel.
navigation.add(showLocation);
navigation.add(nextPoint);
navigation.setCellHorizontalAlignment(nextPoint, HasHorizontalAlignment.ALIGN_RIGHT);
navigation.addStyleName("addPanel");
mainPanel.setSpacing(5);
mainPanel.setStyleName("body");
mainPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
mainPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
// Assemble Main panel.
mainPanel.add(point);
point.setWidth("200px");
mainPanel.add(location);
location.setWidth("200px");
mainPanel.add(navigation);
navigation.setWidth("200px");
// Move cursor focus to the input box.
showLocation.setFocus(true);
// Listen for mouse events on the show location button.
showLocation.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
showCurrentLocation();}
});
// Listen for mouse events on the next point button.
nextPoint.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
loadNewPoint();
}
});
}
private void showCurrentLocation(){
location.setStyleName("visibleText");
}
public VerticalPanel getMainPanel() {
return mainPanel;
}
}
I managed to find a solution to this problem with Bhumika's help.
To make this work I had to change CabbiePoint[] allPointsAndPlaces to static.
This would solve the reference problem one way - from child to parent.
Also I managed to find out trough debugging, that this reference
quiz = new PointsQuiz();
is also null on a second run of loadNewPoint(). So this child reference (PointsQuiz quiz;) and any other references to children were set also to static.
You are getting null pointer error because of allPointsAndPlaces is null. As per your coding The value of allPointsAndPlaces is assigned after completion of RPC call in getAllPointsAndLocations() method. so the allPointsAndPlaces has some assigned values.
Here you try to directly access loadNewPoint() method in child class. At a time, allPointsAndPlaces is not assigned.

Setting a drag image in GWT 2.4

I need to implement drag and drop for cells in a CellTable. Following the example from the MobileWebApp I implemented a custom draggable cell:
public class DraggableCell extends AbstractCell<ProductProxy>{
interface Templates extends SafeHtmlTemplates {
#SafeHtmlTemplates.Template("<div draggable=\"true\">{0}</div>")
SafeHtml simpleTextTemplate(String text);
}
protected Templates templates = GWT.create(Templates.class);
public DraggableCell() {
super("dragstart");
}
#Override
public void render(Context context, ProductProxy value, SafeHtmlBuilder sb){
sb.append(templates.simpleTextTemplate(value.getName()));
}
#Override
public void onBrowserEvent(Context context, Element parent,
ProductProxy value, NativeEvent event,
ValueUpdater<ProductProxy> valueUpdater) {
final Integer cursorOffsetX = 0;
final Integer cursorOffsetY = 0;
if ("dragstart".equals(event.getType())) {
// Save the ID of the entity
DataTransfer dataTransfer = event.getDataTransfer();
dataTransfer.setData("text", value.getId());
SafeHtmlBuilder sb = new SafeHtmlBuilder();
sb.appendEscaped(value.getSn());
Element element = DOM.createDiv();
element.setInnerHTML(sb.toSafeHtml().asString());
// Set the helper image.
dataTransfer.setDragImage(element, cursorOffsetX, cursorOffsetY);
}
}
I use a new element for the drag image (in the MobileWebApp they just use the parent element), but unfortunately no image is displayed during the drag. I thought that maybe the new element needs to be attached to the DOM first, so I created a helperPanel and attached the element to it:
DOM.getElementById("dragHelperPanel").appendChild(element);
// Set the helper image.
dataTransfer.setDragImage(element, cursorOffsetX, cursorOffsetY);
This works fine in Firefox 6, but no luck in Chrome (using the latest stable version), so maybe this isn't the right way to do it. Any ideas? Thanks!
You can try the GWT Drag & Drop API. I have used it and its a good one even in mobile devices.
http://code.google.com/p/gwt-dnd/
Try the Demo here,
http://allen-sauer.com/com.allen_sauer.gwt.dnd.demo.DragDropDemo/DragDropDemo.html
Its quite simple to implement and don't have any issues so far for me

GWT - implement ClickHandler event on a row of a FlexTable

So my software is displaying a flextable (the data is grabbed and displayed from a database) with users allowing to click on a checkbox to select a data.
//users is the flextable object.
userCheck = new ClickHandler() {
public void onClick(ClickEvent event) {
CheckBox src = (CheckBox) event.getSource();
for (int i = 1, n = users.getRowCount(); i < n; i++) {
CheckBox box = (CheckBox) users.getWidget(i, 0);
if (!box.equals(src)) {
box.setValue(false, false);
}
}
removeUserButton.setEnabled(src.getValue());
editUserButton.setEnabled(src.getValue());
}
};
The code above works, but now I'm trying to implement an action where instead of the user clicking on the checkbox, I want the user to click on a row (which ever cell of the table) and make the whole row (where the user have selected) to be highlighted. So I implemented this code below but so far it doesn't work (like the mouseclick won't register, I've yet to implement the color stuff yet.. :( ). Any suggestions?
userRowCheck = new ClickHandler() {
public void onClick(ClickEvent event) {
Cell src = users.getCellForEvent(event);
int rowIndex = src.getRowIndex();
System.out.println("Cell Selected: userRowCheck Handler, rowIndex: " + rowIndex);
//This is just to check if this method is even called out. And well, it doesn't.
}
};
Thanks very much!!!!
If you added userRowCheck to the FlexTable : myFlexTable.addClickHandler(userRowCheck); it should work. Just make sure you test src for null, because if you didn't put a widget in a cell and the user clicks on that cell it returns null.