GWT: TabLayoutPanel with custom tabs does not display correctly - gwt

I have a TabLayoutPanel where I am putting custom widgets in for the tabs to be able to display some images next to the text. I originally worked with TabPanel and using custom HTML for the tab text, but custom tab widgets allows me to modify the image on the fly as needed.
My tab widget is essentially a HorizontalPanel, a number of small images, and a line of text. The problem I'm having is that the tab doesn't want to stick to the bottom of the tab bar like normal. The tab is getting positioned at the top of the space reserved for the tab bar, and there's a gap between it and the bottom of the tab bar. I uploaded an image of the problem to http://imgur.com/fkSHd.jpg.
Is there some style that I need to apply to custom widget tabs to make them appear correctly?

In my brief experience, the newer standards mode panels (they all end in "LayoutPanel") don't get along with the older ones (the ones that just end in "Panel"). So you might consider trying a DockLayoutPanel instead of the HorizontalPanel, and it may be more cooperative.
See https://developers.google.com/web-toolkit/doc/latest/DevGuideUiPanels, particularly the section called "What won't work in Standards Mode?":
HorizontalPanel is a bit trickier. In some cases, you can simply
replace it with a DockLayoutPanel, but that requires that you specify
its childrens' widths explicitly. The most common alternative is to
use FlowPanel, and to use the float: left; CSS property on its
children. And of course, you can continue to use HorizontalPanel
itself, as long as you take the caveats above into account.

After a bit more research, I found the answer here: https://groups.google.com/d/msg/google-web-toolkit/mq7BuDaTNgk/wLqPm5MQeicJ. I had to use InlineLabel or InlineHTML widgets instead of normal Label or HTML widgets. I've tested this solution and it does exactly what I want. I pasted the code of the class below for completeness. Note two things here:
The "float" attribute cannot be set on the last element (the InlineLabel) or the incorrect drawing condition occurs again.
The code could be cleaned up a bit further by having the class extend directly from FlowPanel instead of making it a composite containing a FlowPanel.
package com.whatever;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Float;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.InlineLabel;
public class StatusTab extends Composite
{
public interface StatusImages extends ClientBundle
{
public static StatusImages instance = GWT.create(StatusImages.class);
#Source("images/status-green.png")
ImageResource green();
#Source("images/status-red.png")
ImageResource red();
}
private final ImageResource greenImage;
private final ImageResource redImage;
private final FlowPanel flowPanel;
public LinkStatusTab(String text, int numStatuses) {
greenImage = StatusImages.instance.green();
redImage = StatusImages.instance.red();
flowPanel = new FlowPanel();
initWidget(flowPanel);
for (int i = 0; i < numStatuses; i++)
{
Image statusImg = new Image(redImage);
statusImg.getElement().getStyle().setMarginRight(3, Unit.PX);
statusImg.getElement().getStyle().setFloat(Float.LEFT);
flowPanel.add(statusImg);
}
flowPanel.add(new InlineLabel(text));
}
/**
* Sets the image displayed for a specific status entry.
*/
public void setStatus(int which, boolean status)
{
Image image = (Image)flowPanel.getWidget(which);
if (status)
image.setResource(greenImage);
else
image.setResource(redImage);
}
}

Related

how to create Graphiti shapes with scroll bar

My Graphiti editor has shape that represents db table with it rows.. i am looking for a way to add scroll bar to my shape. any one know how to do that?
I've found an answer to this question.
This is the link to use: https://www.eclipse.org/forums/index.php/t/1071551/
I've created my own ScrollFigure, which extends draw2d ScrollPane and implements IGraphicsAlgorithmRenderer.
public class ScrollFigure extends ScrollPane implements IGraphicsAlgorithmRenderer {
#Override
public void setLayoutManager(LayoutManager manager) {
super.setLayoutManager(new ScrollPaneLayout());
}
}
Next you just have to adjust sizes of your figures in your custom factory, which instantiates your ScrollFigure, and scrollbars will be shown.

Using a DataGrid in a HeaderPanel

Edit
Since no one has responded to my original question I think it is worthwhile adding a description of what I am attempting to accomplish, in addition to the existing description of how I have attempted to achieve my goal:
My objective is to create a DataGrid that will resize according to any change in size of its container. This is not difficult to do, but I have an additional requirement, which is to have Panel widgets above and below the DataGrid; these two Panel widgets will contain widgets that are fixed in size (e.g., a row of buttons or text input widgets). My expectation was that a HeaderPanel would be perfect for this, but this doesn't seem to work (as can be seen in my original question, below). So ... an alternative to my original question ("why doesn't this work") is: what is the best way to implement this requirement?
My original question:
I have a DataGrid in the content area of a HeaderPanel, but the detail lines in the DataGrid are not being displayed (the DataGrid column headings are showing, however). Is there an issue with using a DataGrid in the content area of a HeaderPanel? Or is this a simple misuse of the widgets? I'm adding the HeaderPanel to the RootLayoutPanel, which should provide the necessary resize notification (I think). Here is my UiBinder code:
<ui:UiBinder
xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:c='urn:import:com.google.gwt.user.cellview.client'>
<g:HeaderPanel>
<g:SimplePanel/>
<g:ResizeLayoutPanel>
<c:DataGrid ui:field='dataGrid'/>
</g:ResizeLayoutPanel>
<g:HorizontalPanel>
<g:Button
ui:field='addRecordButton'
text='Add Record'/>
<g:Label ui:field='numberOfRecordsLabel'/>
</g:HorizontalPanel>
</g:HeaderPanel>
</ui:UiBinder>
and here is the Java code:
public class TempGWT implements EntryPoint {
#UiField
Button addRecordButton;
#UiField
DataGrid<Record> dataGrid;
#UiField
Label numberOfRecordsLabel;
private ArrayList<Record> _recordList;
interface TempGWTBinder extends UiBinder<Widget, TempGWT> {
}
private static class Record {
private String _field1;
}
#Override
public void onModuleLoad() {
_recordList = new ArrayList<Record>();
TempGWTBinder binder = GWT.create(TempGWTBinder.class);
Widget widget = binder.createAndBindUi(this);
Column<Record, String> field1Column = new Column<Record, String>(new TextInputCell()) {
#Override
public String getValue(final Record record) {
return record._field1;
}
};
dataGrid.addColumn(field1Column, "Field 1");
RootLayoutPanel.get().add(widget);
}
#UiHandler("addRecordButton")
public void onAddRecordButtonClick(final ClickEvent event) {
Record record = new Record();
record._field1 = "Record " + (_recordList.size() + 1);
_recordList.add(record);
dataGrid.setRowData(_recordList);
numberOfRecordsLabel.setText("Records:" + _recordList.size());
}
}
I've attempted to trace the execution and, although I'm not certain, it looks as though the following happens when I change the size of the browser window and the "resize" request is received by the DataGrid (I've skipped some of the "unimportant" methods):
DataGrid#onResize
HeaderPanel#forceLayout
ScrollPanel#onResize
The DataGrid object contains a HeaderPanel, which contains the headings for the DataGrid and a ScrollPanel. I don't know whether this is the key to the problem, but the ScrollPanel in the DataGrid's HeaderPanel contains a DataGrid$TableWidget object, and TableWidget does not implement RequiresResize; the ScrollPanel#onResize method only sends the resize to its child if the child implements RequiresResize.
The Tables and Frames section of the GWT Developer's Guide makes it clear that I just needed to use a width/height of 100% for the DataGrid! Like so:
<c:DataGrid
ui:field='dataGrid'
width='100%'
height='100%'/>

Nested Header Panels

I am trying to nest HeaderPanels taking advantage of the header and footer capabilities in my webpage.
The top level HeaderPanel works fine, but the second HeaderPanel in the center of the top panel does not appear. Here is sample code:
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HeaderPanel;
import com.google.gwt.user.client.ui.RootLayoutPanel;
public class Admin implements EntryPoint {
private final HeaderPanel topPanel = new HeaderPanel();
private final HeaderPanel centerPanel = new HeaderPanel();
#Override
public void onModuleLoad() {
topPanel.setHeaderWidget(new HTML("top header"));
topPanel.setFooterWidget(new HTML("top footer"));
centerPanel.setHeaderWidget(new HTML("center header"));
centerPanel.setFooterWidget(new HTML("center footer"));
centerPanel.add(new HTML("center center"));
topPanel.setContentWidget(centerPanel);
RootLayoutPanel.get().add(topPanel);
}
}
The 'center header', 'center footer' and 'center center' never appear on the webpage.
I also tried using a DocLayoutPanel in the center and it didn't work either.
I suspect that these only work when attached to the RootLayoutPanel.
Is nested Header or Doc layout panels possible?
If you try the Chrome inspect, there's something similar in IE9 too, you'll see that that the center widget has 0 height. If you set the size it will work. I would use a DockLayoutPanel. You can nest DockLayoutPanels.

GWT TextArea in ScrollPanel in TabLayoutPanel - how to take 100% of height?

Here is a miminal UI demonstrating my problem. It is the usual UIBinder boilerplate, plus the three widgets: TabLayoutPanel, ScrollPanel, TextArea. I want the TextArea to take up all the available space of the tab, and I want it to have a scroll bar if it can't fit. But this code yields a TextArea that is two lines tall. How do you fix this? Why is it ignoring the height?
In the ui.xml file:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui">
<ui:style>
.scrollPanel {
height: 100%;
}
.textArea {
height: 100%;
}
</ui:style>
<g:TabLayoutPanel barHeight="20" barUnit='PX'>
<g:tab>
<g:header>Text Area</g:header>
<g:ScrollPanel styleName='{style.scrollPanel}'>
<g:TextArea ui:field='textArea' styleName='{style.textArea}'></g:TextArea>
</g:ScrollPanel>
</g:tab>
</g:TabLayoutPanel>
</ui:UiBinder>
And in the Java file:
package com....client;
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.ResizeComposite;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.Widget;
public class BugDemoLayout extends ResizeComposite {
private static BugDemoLayoutUiBinder uiBinder = GWT.create(BugDemoLayoutUiBinder.class);
interface BugDemoLayoutUiBinder extends UiBinder<Widget, BugDemoLayout> {}
#UiField TextArea textArea;
public BugDemoLayout() {
initWidget(uiBinder.createAndBindUi(this));
StringBuilder junk = new StringBuilder();
for (int i=1; i<300; i++) {
junk.append("Line " + i + "\n");
}
textArea.setText(junk.toString());
}
}
The module file simply adds the ui to the root:
public void onModuleLoad() {
BugDemoLayout bd = new BugDemoLayout();
RootLayoutPanel.get().add(bd);
TabLayoutPanel and ScrollPanel both implement the RequireResize interface and automatically resize to the available space using absolute positioning.
You specified a relative height (100%) for the content inside the ScrollPanel. This doesn't work because the size in the parent isn't explicitly set (see here for more details).
So you can either:
Set an explicit size in the ScrollPanel in pixel and then set the Textarea height to 100%.
Extend the TextArea and implement the RequiresResize interface (implement the onResize() method where you set the height/width of the TextArea
The second approach is the cleaner recommend one as it also resizes the TextArea when you resize the browser window.
It would look something like that:
TextArea:
public class ResizableTextArea extends TextArea implements RequiresResize {
public void onResize() {
int height = getParent().getOffsetHeight();
int width = getParent().getOffsetWidth();
setSize(width+"px",height+"px");
}
}
You have to put your TabLayoutPanel into a RootLayoutPanel. This will ensure that there is an unbroken chain of LayoutPanels or Widgets that implement RequiresResize/ProvidesResize interfaces all the way down to your custom TextArea.
You're placing a TextArea inside of a ScrollPanel, but the ScrollPanel isn't necessary as the TextArea already has scrollability built-in. If you take out the ScrollPanel, it should work fine (in my testing it works).
I'm going to skip the sizing part of this question, but answer the scrolling part (as that never seemed to be resolved?).
I had the same problem, so I placed a ScrollPanel inside the tab, and nested an HTMLPanel within it, sized to 100% the width and height of that parent ScrollPanel. Then, I used panel.add( new HTML("my text" ) ); to populate it.
I also replaced "\n" with "<br />" in my actual long text string. (That was applicable for my needs).
Now, that's not quite the same thing as a TextArea, of course, but it does allow you to display long running scrolling text inside of a TabLayoutPanel.

Wrapping pre-made elements using GWT?

I've been working with GWT for awhile, I can't find a way to integrate it with a preexisting website which is a real downer. My page content is already generated for me using jsp, like:
<div id='A'></div>
<div id='B'></div>
etc.
there is no way for me to do something like this though:
public void onModuleLoad() {
SimplePanel spA = new SimplePanel(
Document.getElementById("A"));
spA.add(new Label("hello"));
SimplePanel spB = new SimplePanel(
Document.getElementById("B"));
spB.setWidth("200px");
etc ..
}
seems like there's no way to just wrap a pre-existing element. Is this true, or am I missing how to do this? I need be able to wrap a bunch of elements like this, to manipulate them later on. I see TextBox, Button, a few other classes have wrap() methods, however nothing like that exists for elements,
Thanks
There is a way to wrap existing DOM elements, like Label's wrap() method. For example:
Label label = Label.wrap(DOM.getElementById("A"));
label.setText("Foo!");
Other GWT classes can wrap DOM elements too, like Button, and CheckBox using its constructor.
Use HTMLPanel:
class MyPanel extends HTMLPanel {
private SimplePanel a = new SimplePanel();
private SimplePanel b = new SimplePanel();
public MyPanel() {
super("<div id="a"></div><div id="b"></div>);
addAndReplaceElement(a, "a");
addAndReplaceElement(b, "b");
}
}