GWT cell rendering based on selection state - gwt

I've a custom image cell in a CellTree. I want to render two different images depending on the row/node selection state, for example if the row/node is selected I want to render image A if not selected image B. The images couple is different for each node.
What is the best way to get the selection state in the render method of the cell?

CSS solution
If you can work with background images here, the easiest and most efficient solution would be CSS based.
Take a look at /com/google/gwt/user/cellview/client/CellTree.css (in gwt-user.jar). There you see the css classes ".cellTreeItem" and ".cellTreeSelectedItem". The latter one already has an image. You could assign it your own, and for ".cellTreeItem" a different one.
For general information how to adjust CellTable/CellTree/... styles, see e.g. https://stackoverflow.com/a/6387210/291741
Cell solution
You could construct your cell with the SelectionModel like
public MyCell(SelectionModel selectionModel) {
this.selectionModel = selectionModel;
}
public void render(final Cell.Context context, final Node value,
final SafeHtmlBuilder sb) {
if (selectionModel.isSelected(...
}
However, you will probably need to retrigger the cell rendering when the selection changes. It's probably possible, but I've never done that.

Related

How to gray selection highlight when NatTable not in focus

Some lists and tables gray out their selection when they lose keyboard focus.
In the presence of multiple lists/tables, this helps communicate to the user which selection is active.
Is there an easy way to do this with NatTable?
The best I've come up with so far is to flip between different attributes for DisplayMode.SELECT as focus comes and goes -- but I'm not sure I can do that after NatTable.configure() has been called.
Yes you can change configuration attributes dynamically after NatTable#configure() has been called. That is a common approach for dynamic changes. Another approach would be to configure a selection style for a special label and apply that label only in case the table is active. This approach can be seen in this example.
https://github.com/eclipse/nebula.widgets.nattable/blob/master/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples/_500_Layers/_505_Selection/_5054_SelectionProviderExample.java
I have this working, after #DirkFauth's answer. This answer includes some specifics.
After the table has been configured with NatTable.configure(), you can modify the configuration not with NatTable.addConfiguration(IConfiguration), but instead by calling IConfiguration.configureRegistry(IConfigRegistry). For example:
myConfiguration.configureRegistry( myTable.getConfigRegistry() )
Within that implementation of configureRegistry(), you can set the style for selected and anchor cells:
configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE,
selectedStyle, DisplayMode.SELECT, GridRegion.BODY);
configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE,
anchorStyle, DisplayMode.SELECT,
SelectionStyleLabels.SELECTION_ANCHOR_STYLE);
When the table is inactive, selectedStyle and anchorStyle can be modified clones of their usual setting. For example:
private static Color myInactiveColor = ...;
public static Style makeInactiveBodyCellStyleFrom(#Nonnull Style style) {
Style rv = style.clone();
rv.setAttributeValue( CellStyleAttributes.BACKGROUND_COLOR,
myInactiveColor );
return rv;
}
Similar work can be done for the styles of selected row and column headers.

Codename One Drag & Drop get target

I have an application with some labels and containers. I want to drag the labels into the containers and assert that the containers receive a specific label. (A game for kids [starting with my 6-year-old daughter, who is learning to read], where the next level can be accessed after the labels are arranged in a specific order to form words or phrases.)
For the moment, I am using something like this :
label_1.addDropListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String string = label_1.getParent().toString();
}
But this approach only gets the value of the container where the label has been before, and not the value of the drop target.
I could get the current position by a new button, but I'd rather solve this through the drag & drop operation.
There seems to a draggingOver method (like in JavaFX), but I can't figure out a way to use it.
Any kind hint would be appreciated.
The best approach is probably to subclass Container where you can override the drag over. In that case you will also know the container instance.
In the case of the listener you would need one listener per container and that way you could have the container reference.

GWT drag and drop animation

I have a flow panel with many photo-widgets inside (gallery with random number of rows and columns, depends on screen size) for which I want to implement drag and drop behavior to change their order. I am using gwt-dnd library. Its FlowPanelDropController allows you to define your own positioner (delimiter) which shows the candidate location for dropping the dragged widget.
I want this positioner to be the empty space with defined width, and the challenging thing is to implement sliding animation effect for the when positioner is added and removed.
If you are a desktop Picasa app user you know what I mean: the target row slides both sides (little to the left, little to the right) extending the space between the items where you are going to drop a photo.
The whole thing is complex enough, but any help related to how to apply the animation for positioner attach/detach is appreciated. Maybe I need to use a different approach (e.g., use GWT native dnd instead of gwt-dnd lib and no "positioners" at all) if you have any ideas how this could be helpful.
Thanks.
Well, I ended up overriding AbstractPositioningDropController (parent of FlowPanelDropController) and adding some extra features.
1) newPositioner() method now builds the Label, which is vertical space with some small width, height and decoration. This widget's element has constant id (say, "POSITIONER"), which helps to distinguish between multiple positioners if you plan to have several of them while navigating with a drag object over multiple drop targets. Also some transition CSS effects were applied to the Label, which will be responsible for handling animated extension of Label's width.
2) in onEnter() I do the following
...
removePositioner(getPositionerElement());
Widget positioner = newPositioner();
dropTarget.insert(positioner, targetIndex);
animatePositionerExtension();
where getPositionerElement() returns DOM.getElementById(POSITIONER)
At the same time removePositioner(..) resets the id of this element to something abstract and ideally should provide some animation before calling .removeFromParent(). But I didn't have enough time to properly debug this so ended up just removing the old positioner with no animation.
Method animatePositionerExtension() contains the code that changes the width of the positioner widget, so that CSS transition will catch that and provides animation.
All access to positioner widget in the class should be provided through updated methods.
3) onLeave() contains line removePositioner(getPositionerElement());
4) In the end of onMove() I added a couple of lines:
galleryWidget.extendHoveredRow(targetIndex - 1);
animatePositionerExtension();
where extendHoveredRow(hoveredWidgetOrdinal) implemented the logic to "limit" the sliding effect in the single line:
int rowHovered = -1;
public void extendHoveredRow(int hoveredWidgetOrdinal) {
int newRowHovered = getRowByOrdinalHovered(hoveredWidgetOrdinal);
if (rowHovered != newRowHovered) {
// adjust position of items in the previously hovered row
int firstInPreviouslyHoveredRow = (rowHovered - 1) * itemsInARow;
shiftFirstItemLeft(firstInPreviouslyHoveredRow, false);
rowHovered = newRowHovered;
// extend this row
int firstInThisRow = getOrdinalFirstInThisRowByOrdinal(hoveredWidgetOrdinal);
shiftFirstItemLeft(firstInThisRow, true);
}
}
This is in short how I did the thing. And still there's some room for improvements, like adding animated removal.
Again, it's all about overriding DropController and manipulations with elements inside the "gallery" widget. The benefit of this approach is that I remain in the gwt-dnd operations framework, and also reused a bunch of existent code.
Some notes:
CSS transition is not supported in IE pre-9, but this is unrelated to
this topic.
Put a transparent "glass" div on top of the Image widget if you use it
as a face of dragProxy. This will save you tons of time trying to
understand why either setting element's draggable to false, or
calling event.preventDefault() somewhere else, or other workarounds don't work in one or several browsers and the image itself is being dragged instead of the whole dragProxy widget.

GWT: How to run code after sorting a datagrid column

I really need a possibility to run some code after the whole sorting of the DataGrid is finished. Especially after the little arrow which shows if the column is sorted ascending or descending is displayed, because i need to manipulate the CSS of this arrow after it is displayed. I couldn't find the place where the arrow is really set. I tried something like this:
ListHandler<String> columnSortHandler = new ListHandler<String>(list) {
#Override
public void onColumnSort( ColumnSortEvent event ) {
super.onColumnSort( event );
// My Code here
}
};
but the code runs also before sorting finishes.
Thanks for any suggestions how to solve this problem. I am searching for a long time now but cannot find anything that helps.
EDIT: I already override the original DataGrid.Resources to provide a custom arrow-picture. I also have a complex custom header of AbstractCell<String> which supports runtime-operations and is rendered with DIV's and Image.
As you're using a ListHandler, and thus probably a ListDataProvider that will update the CellTable live (setRowData); because both ListDataProvider and CellTable (via the inner HasDataPresenter) use Scheduler#scheduleFinally(), then using Scheduler#scheduleDeferred() should be enough to guarantee that you run after them, but then you'll risk some flicker.
You could, in your custom ListHandler flush() the ListDataProvider, which will bypass one scheduleFinally and then use scheduleFinally to execute after the one of the CellTable (because flush() will call setRowData on the CellTable which will schedule the command; your command wil be scheduled after, so will run after).
You can manipulate the css resource using CellTable.Resources.
public interface TableResources extends CellTable.Resources {
#Source("up.png")
ImageResource cellTableSortAscending();
#Source("down.png")
ImageResource cellTableSortDescending();
#Source("MyCellTable.css")
CellTable.Style cellTableStyle();
}
In MyCellTable.css use the stylename and change your icon

GWT Widget not properly set in the DOM

I would like to print a GWT widget which extends Composite. This widget is composed of a grid whose cells are built with a ListDataProvider. When the user clic on a button print, the widget to print is built. Once this is done, I launch the print:
Element element = widgetToPrint.getElement();
String content = element.getInnerHTML();
print(content);
public static native boolean print(String content)
/*-{
var mywindow = window.open('', 'Printing', '');
mywindow.document.write('<html><head><title>Test</title>');
mywindow.document.write('<link rel="stylesheet" href="/public/stylesheets/ToPrintWidget.css" type="text/css" media="all"/></head><body>');
mywindow.document.write(content);
mywindow.document.write('</body></html>');
mywindow.print();
return true;
}-*/;
So, here is my problem:
The window which is opened by this method contains the core of the widget (built by the UI Binder), but some children are missing...
If I look inside the ListDataProvider and its related FlowPanel, the data are consistent, i.e. I've got several item in my list and in the flowPanel.
Consequently, it should be visible on the printing window...
I thought that maybe the problem was related to the method used to print the widget, so I also tried to add this widget into a dialogbox just before launching the print, to see if the widget was properly built... and it was.
So my widget displays well on a dialogbox, but if I try to give its innerHTML to the print method, by using getElement(), some widgets are missing... I've the feeling that the widgets which should have been built when the ListDataProvider changes are not properly set in the DOM... Somehow it works when I add the widget to a regular component, but it doesn't work when I have to give directly its innerHTML...
Do you have any idea ?
Thanks in advance.
Widgets are not just the sum of their elements, and DOM elements are not just the string that they are serialized to. Widgets are the element, and all events sunk to the dom to listen for any changes or interactions by the user. Elements then have callback functions or handlers they invoke when the user interacts with them.
By serializing the element (i.e. invoking getInnerHTML()), you are only reading out the structure of the dom, not the callbacks, and additionally not the styles set by CSS. This probably shouldn't be expected to work correctly, and as your experience is demonstrating, it doesn't.
As this is just a print window you are trying to create, event handling is probably not a concern. You just want the ability to see, but not interact with, the content that would be in that set of widgets. Styles are probably the main problem here then (though your question doesn't specify 'some children are missing' doesn't tell us what is missing, or give us any more clues as to why...) - you are adding one stylesheet in your JSNI code, but CellTable (which I assume you are using since you reference ListDataProvider) needs additional CssResource instances to appear correctly. I'm not sure how you can hijack those to draw in a new window.
Are you only using this to print content, not to let the user directly interact with the data? If so, consider another approach - use a SafeHtmlBuilder to create a giant, properly escaped string of content to draw in the new window.
String content = element.toString();
This will include all hierarchy elements in the node.
Just a reminder, all the GWT handlers will not work, and you have to sink all the events using DOM.
You might want to grab the outer HTML rather than the inner one.
GWT unfortunately has no getOuterHTML, but it's relatively easy to emulate.
If your widget is the only child within an element, then simply get the inner HTML of the parent element (w.getElement().getParentElement().getInnerHTML())
Otherwise, clone your widget's node add it to a newly created parent element, from which you'll be able to get the inner HTML:
DivElement temp = Document.get().createDivElement();
temp.appendChild(w.getElement().cloneNode(true));
return temp.getInnerHTML();
First thank you for your answers, it helped me to work out this problem.
I've almost solve the problem:
First, I do not use ListDataProvider anymore, because it wasn't clear for me when and how the view was refreshed. Instead I add my widgets by hand, which makes sense since, they are not going to move anyway.
Then, I define the style of my widgets using a common CSS stylesheet. However, in order to do it, I can't rely on CssResource, which was the way I was used to do it with GWT. I think that this comes from the JS method which gets lost by this kind of styles... Instead, I have to specify everything in a static CSS stylesheet, and to give it to the JS.
It works perfectly well, ie, I have my widgets, with thei styles, and I can print it.
But...
The color of some widgets depends on the color of the object that they represent. Consequently, I cannot write a generic CSS stylesheet... And as I said, I can't add a style using CssResource... Do you have any ideas on the way to handle that ?
To make sure I'm clear on the way I'm adding styles, here is an example:
Label l = new Label("Here is a cell in my grid to be printed");
l.addStyleName("PrintLineCell-kind_1");
With, in a public CSS stylesheet:
.PrintLineCell-kind_1{
background-color: red;
}
I hope there is a better way than to write 300 styles to cover 300 different colors...