A NatTable requires to set cells of column 3 with BACKGROUND_COLOR = GUIHelper.COLOR_GREEN when the property of the Material is of type "Composite".
Material list is the source of data (the DataProvider)
NatTable is configured with StackingTableConfiguration.class
NatTable uses a custom label to set the cell style by means of StackingTableLabelAccumulator.class
As suggested in https://www.eclipse.org/forums/index.php/t/781508/
I have set the cellLabelAccumulator body data layer.
I used AggregateConfigLabelAccumulator since I have the custom label accumulator (AggregateConfigLabelAccumulator) and a columnLabelAccumulator.
When the Material is of type Composite, the label is added to the config labels, but the green color is nor render.
public class StackingTable {
private NatTable natTable;
public static IDataProvider bodyDataProvider;
public static String COLUMN_ONE_LABEL = "ColumnOneLabel";
public static String COLUMN_TWO_LABEL = "ColumnTwoLabel";
public static String COLUMN_THREE_LABEL = "ColumnThreeLabel";
public static String TEST = "Composite_Label";
public StackingTable(Composite parent,
EventList<AncolabStackingLayer> ancolabStackingData,
SelectionLayer stackingTableSelectionLayer ) {
bodyDataProvider = new ListDataProvider<>(ancolabStackingData, colAccessor);
final DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
final ColumnOverrideLabelAccumulator columnLabelAccumulator =
new ColumnOverrideLabelAccumulator(bodyDataLayer);
columnLabelAccumulator.registerColumnOverrides(0, COLUMN_ONE_LABEL);
columnLabelAccumulator.registerColumnOverrides(1, COLUMN_TWO_LABEL);
columnLabelAccumulator.registerColumnOverrides(2, COLUMN_THREE_LABEL);
//Create the gridLayer
natTable = new NatTable(parent, gridLayer, false);
AggregateConfigLabelAccumulator aggregateConfigLabelAccumulator =
new AggregateConfigLabelAccumulator();
aggregateConfigLabelAccumulator.add(columnLabelAccumulator);
aggregateConfigLabelAccumulator.add(
new StackingTableLabelAccumulator(bodyDataProvider));
bodyDataLayer.setConfigLabelAccumulator(aggregateConfigLabelAccumulator);
bodyDataLayer.addConfiguration(
new DefaultNatTableStyleConfiguration());
bodyDataLayer.addConfiguration(
new StackingTableConfiguration(bodyDataProvider));
bodyDataLayer.addConfiguration(new DefaultEditConfiguration());
bodyDataLayer.addConfiguration(new DefaultEditBindings());
natTable.configure();
}
The configuration registry:
public class StackingTableConfiguration extends AbstractRegistryConfiguration {
private IDataProvider bodyDataProvider;
public StackingTableConfiguration(IDataProvider dp) {
this.bodyDataProvider = dp;
#Override
public void configureRegistry(IConfigRegistry configRegistry) {
//...some configutarion attributes for other columns
Style cellStyle2 = new Style();
cellStyle2.setAttributeValue(
CellStyleAttributes.BACKGROUND_COLOR,
GUIHelper.COLOR_GREEN);
configRegistry.registerConfigAttribute(
CellConfigAttributes.CELL_STYLE, cellStyle2,
DisplayMode.NORMAL, StackingTable.TEST);
}
}
The custom label accumulator:
//Add a label to cells in column 2 when the material of the row is of type = "Composite"
public class StackingTableLabelAccumulator extends AbstractOverrider {
IDataProvider dataProvider;
public StackingTableLabelAccumulator(IDataProvider dataProvider){
this.dataProvider = dataProvider;
}
#Override
public void accumulateConfigLabels(LabelStack configLabels,
int columnPosition, int rowPosition) {
Material mat =
(Material) ((IRowDataProvider) dataProvider).getRowObject(rowPosition);
if(mat.getType().equals("Composite")&& columnPosition == 2) {
configLabels.addLabel(StackingTable.TEST);
//When a material of type composite,
//the code reachs this point, i.e. the label is added to the labelStack
System.out.println(configLabels.getLabels().get(1).toString() +
"\t" + columnPosition + "\t" + rowPosition);
}
}
}
Related
I am making a fragment that uses content provider to get contacts from any phone using listview
#SuppressWarnings("ALL")
public abstract class fragment3 extends Fragment implements
LoaderManager.LoaderCallbacks<Cursor>,
AdapterView.OnItemClickListener, androidx.loader.app.LoaderManager.LoaderCallbacks<Cursor> {
**strong text**
private LifecycleOwner owner;
private RVAdapter myadapter;
private Object CursorLoader;
public fragment3() {
}
public Loader<Cursor> loader;
private Cursor cursor;
public abstract class LoaderManager{}
ListView contactsList;
long contactId;
String contactKey;
Uri contactUri;
SimpleCursorAdapter cursorAdapter;
private final static int[] TO_IDS = {
android.R.id.text1
};
// The column index for the _ID column
final int CONTACT_ID_INDEX = 0;
// The column index for the CONTACT_KEY column
final int CONTACT_KEY_INDEX = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
// Always call the super method first
super.onCreate(savedInstanceState);
// Initializes the loader
getLoaderManager().initLoader(0, null, this);}
#SuppressLint("ResourceType")
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Set the item click listener to be the current fragment.
contactsList.setOnItemClickListener(this);
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Gets the ListView from the View list of the parent activity
contactsList =
(ListView) getActivity().findViewById(R.layout.list_view);
// Gets a CursorAdapter
cursorAdapter = new SimpleCursorAdapter(
getActivity(),
R.layout.list_item,
null,
FROM_COLUMNS, TO_IDS,
0);
// Sets the adapter for the ListView
contactsList.setAdapter(cursorAdapter);
getLoaderManager().initLoader(0, null, this);
}
// If non-null, this is the current filter the user has provided.
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.CONTACT_STATUS,
ContactsContract.Contacts.PHOTO_ID,
};
// Called just before the Fragment displays its UI
#Override
public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
/*
* Makes search string into pattern and
* stores it in the selection array
*/
selectionArgs[0] = "%" + searchString + "%";
// Starts the query
return new CursorLoader(
getActivity(),
ContactsContract.Contacts.CONTENT_URI,
PROJECTION,
SELECTION,
selectionArgs,
null
);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// Put the result Cursor in the adapter for the ListView
cursorAdapter.swapCursor(cursor);
}
// Defines the text expression
#SuppressLint("InlinedApi")
final String SELECTION =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" :
ContactsContract.Contacts.DISPLAY_NAME + " LIKE ?";
// Defines a variable for the search string
private String searchString;
// Defines the array to hold values that replace the ?
private String[] selectionArgs = {searchString};
/*
* Defines an array that contains column names to move from
* the Cursor to the ListView.
*/
#SuppressLint("InlinedApi")
private final static String[] FROM_COLUMNS = {
Build.VERSION.SDK_INT
>= Build.VERSION_CODES.HONEYCOMB ?
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY :
ContactsContract.Contacts.DISPLAY_NAME
};
#Override
public void onItemClick(
AdapterView<?> parent, View item, int position, long rowID) {
// Get the Cursor
//Cursor cursor = parent.getAdapter().getCursor();
Cursor c = ((CursorAdapter)((parent)).getAdapter()).getCursor();
// Move to the selected contact
cursor.moveToPosition(position);
// Get the _ID value
contactId = cursor.getLong(CONTACT_ID_INDEX);
// Get the selected LOOKUP KEY
contactKey = cursor.getString(CONTACT_KEY_INDEX);
// Create the contact's content Uri
String mContactKey = "";
contactUri = ContactsContract.Contacts.getLookupUri(contactId, mContactKey);
/*
* You can use contactUri as the content URI for retrieving
* the details for a contact.
*/
}
// A UI Fragment must inflate its View
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the fragment layout
return inflater.inflate(R.layout.fragment_fragment3,
container, false);
}
#SuppressLint("InlinedApi") final String[] PROJECTION =
{
ContactsContract.Contacts._ID,
ContactsContract.Contacts.LOOKUP_KEY,
Build.VERSION.SDK_INT
>= Build.VERSION_CODES.HONEYCOMB ?
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY :
ContactsContract.Contacts.DISPLAY_NAME
};
#Override
public void onLoaderReset(#NonNull androidx.loader.content.Loader<Cursor> loader) {
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
}
}
I am using iText7 to build a table of contents for my document. I know all the section names before I start, but don't know what the page numbers will be. My current process is to create a table on the first page and create all the Link objects with generic text "GO!". Then as I add sections I add through the link objects and update the text with the page numbers that I figured out as I created the document.
However, at the end, what gets written out for the link is "GO!", not the updated page number values I set as I was creating the rest of the document.
I did set the immediateFlush flag to false when I created the Document.
public class UpdateLinkTest {
PdfDocument pdfDocument = null;
List<Link>links = null;
Color hyperlinkColor = new DeviceRgb(0, 102, 204);
public static void main(String[] args) throws Exception {
List<String[]>notes = new ArrayList<>();
notes.add(new String[] {"me", "title", "this is my text" });
notes.add(new String[] {"me2", "title2", "this is my text 2" });
new UpdateLinkTest().exportPdf(notes, new File("./test2.pdf"));
}
public void exportPdf(List<String[]> notes, File selectedFile) throws Exception {
PdfWriter pdfWriter = new PdfWriter(selectedFile);
pdfDocument = new PdfDocument(pdfWriter);
Document document = new Document(pdfDocument, PageSize.A4, false);
// add the table of contents table
addSummaryTable(notes, document);
// add a page break
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
// add the body of the document
addNotesText(notes, document);
document.close();
}
private void addSummaryTable(List<String[]> notes, Document document) {
links = new ArrayList<>();
Table table = new Table(3);
float pageWidth = PageSize.A4.getWidth();
table.setWidth(pageWidth-document.getLeftMargin()*2);
// add header
addCell("Author", table, true);
addCell("Title", table, true);
addCell("Page", table, true);
int count = 0;
for (String[] note : notes) {
addCell(note[0], table, false);
addCell(note[1], table, false);
Link link = new Link("Go!", PdfAction.createGoTo(""+ (count+1)));
links.add(link);
addCell(link, hyperlinkColor, table, false);
count++;
}
document.add(table);
}
private void addNotesText(List<String[]> notes, Document document)
throws Exception {
int count = 0;
for (String[] note : notes) {
int numberOfPages = pdfDocument.getNumberOfPages();
Link link = links.get(count);
link.setText(""+(numberOfPages+1));
Paragraph noteText = new Paragraph(note[2]);
document.add(noteText);
noteText.setDestination(++count+"");
if (note != notes.get(notes.size()-1))
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
}
private static void addCell(String text, Table table, boolean b) {
Cell c1 = new Cell().add(new Paragraph(text));
table.addCell(c1);
}
private static void addCell(Link text, Color backgroundColor, Table table, boolean b) {
Cell c1 = new Cell().add(new Paragraph(text));
text.setUnderline();
text.setFontColor(backgroundColor);
table.addCell(c1);
}
}
Quite more work needs to be done compared to the code you have now because the changes to the elements don't take any effect once you've added them to the document. Immediate flush set to false allows you to relayout the elements, but that does not happen automatically. The way you calculate the current page the paragraph will be placed on (int numberOfPages = pdfDocument.getNumberOfPages();) is not bulletproof because in some cases pages might be added in advance, even if the content is not going to be placed on them immediately.
There is a very low level way to achieve your goal but with the recent version of iText (7.1.15) there is a simpler way as well, which still requires some work though. Basically your use case is very similar to target-counter concept in CSS, with page counter being the target one in your case. To support target counters in pdfHTML add-on we added new capabilities to our layout module which are possible to use directly as well.
To start off, we are going to tie our Link elements to the corresponding Paragraph elements that they will point to. We are going to do it with ID property in layout:
link.setProperty(Property.ID, String.valueOf(count));
noteText.setProperty(Property.ID, String.valueOf(count));
Next up, we are going to create custom renderers for our Link elements and Paragraph elements. Those custom renderers will interact with TargetCounterHandler which is the new capability in layout module I mentioned in the introduction. The idea is that during layout operation the paragraph will remember the page on which it was placed and then the corresponding link element (remember, link elements are connected to paragraph elements) will ask TargetCounterHandler during layout process of that link element which page the corresponding paragraph was planed on. So in a way, TargetCounterHandler is a connector.
Code for custom renderers:
private static class CustomParagraphRenderer extends ParagraphRenderer {
public CustomParagraphRenderer(Paragraph modelElement) {
super(modelElement);
}
#Override
public IRenderer getNextRenderer() {
return new CustomParagraphRenderer((Paragraph) modelElement);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
TargetCounterHandler.addPageByID(this);
return result;
}
}
private static class CustomLinkRenderer extends LinkRenderer {
public CustomLinkRenderer(Link link) {
super(link);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
Integer targetPageNumber = TargetCounterHandler.getPageByID(this, getProperty(Property.ID));
if (targetPageNumber != null) {
setText(String.valueOf(targetPageNumber));
}
return super.layout(layoutContext);
}
#Override
public IRenderer getNextRenderer() {
return new CustomLinkRenderer((Link) getModelElement());
}
}
Don't forget to assign the custom renderers to their elements:
link.setNextRenderer(new CustomLinkRenderer(link));
noteText.setNextRenderer(new CustomParagraphRenderer(noteText));
Now, the other thing we need to do it relayout. You already set immediateFlush to false and this is needed for relayout to work. Relayout is needed because on the first layout loop we will not know all the positions of the paragraphs, but we will already have placed the links on the pages by the time we know those positions. So we need the second pass to use the information about page numbers the paragraphs will reside on and set that information to the links.
Relayout is pretty straightforward - once you've put all the content you just need to call a single dedicated method:
// For now we have to prepare the handler for relayout manually, this is going to be improved
// in future iText versions
((DocumentRenderer)document.getRenderer()).getTargetCounterHandler().prepareHandlerToRelayout();
document.relayout();
One caveat is that for now you also need to subclass the DocumentRenderer since there is an additional operation that needs to be done that is not performed under the hood - propagation of the target counter handler to the root renderer we will be using for the second layout operation:
// For now we have to create a custom renderer for the root document to propagate the
// target counter handler to the renderer that will be used on the second layout process
// This is going to be improved in future iText versions
private static class CustomDocumentRenderer extends DocumentRenderer {
public CustomDocumentRenderer(Document document, boolean immediateFlush) {
super(document, immediateFlush);
}
#Override
public IRenderer getNextRenderer() {
CustomDocumentRenderer renderer = new CustomDocumentRenderer(document, immediateFlush);
renderer.targetCounterHandler = new TargetCounterHandler(targetCounterHandler);
return renderer;
}
}
document.setRenderer(new CustomDocumentRenderer(document, false));
And now we are done. Here is our visual result:
Complete code looks as follows:
public class UpdateLinkTest {
PdfDocument pdfDocument = null;
Color hyperlinkColor = new DeviceRgb(0, 102, 204);
public static void main(String[] args) throws Exception {
List<String[]> notes = new ArrayList<>();
notes.add(new String[] {"me", "title", "this is my text" });
notes.add(new String[] {"me2", "title2", "this is my text 2" });
new UpdateLinkTest().exportPdf(notes, new File("./test2.pdf"));
}
public void exportPdf(List<String[]> notes, File selectedFile) throws Exception {
PdfWriter pdfWriter = new PdfWriter(selectedFile);
pdfDocument = new PdfDocument(pdfWriter);
Document document = new Document(pdfDocument, PageSize.A4, false);
document.setRenderer(new CustomDocumentRenderer(document, false));
// add the table of contents table
addSummaryTable(notes, document);
// add a page break
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
// add the body of the document
addNotesText(notes, document);
// For now we have to prepare the handler for relayout manually, this is going to be improved
// in future iText versions
((DocumentRenderer)document.getRenderer()).getTargetCounterHandler().prepareHandlerToRelayout();
document.relayout();
document.close();
}
private void addSummaryTable(List<String[]> notes, Document document) {
Table table = new Table(3);
float pageWidth = PageSize.A4.getWidth();
table.setWidth(pageWidth-document.getLeftMargin()*2);
// add header
addCell("Author", table, true);
addCell("Title", table, true);
addCell("Page", table, true);
int count = 0;
for (String[] note : notes) {
addCell(note[0], table, false);
addCell(note[1], table, false);
Link link = new Link("Go!", PdfAction.createGoTo(""+ (count+1)));
link.setProperty(Property.ID, String.valueOf(count));
link.setNextRenderer(new CustomLinkRenderer(link));
addCell(link, hyperlinkColor, table, false);
count++;
}
document.add(table);
}
private void addNotesText(List<String[]> notes, Document document) {
int count = 0;
for (String[] note : notes) {
Paragraph noteText = new Paragraph(note[2]);
noteText.setProperty(Property.ID, String.valueOf(count));
noteText.setNextRenderer(new CustomParagraphRenderer(noteText));
document.add(noteText);
noteText.setDestination(++count+"");
if (note != notes.get(notes.size()-1))
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
}
private static void addCell(String text, Table table, boolean b) {
Cell c1 = new Cell().add(new Paragraph(text));
table.addCell(c1);
}
private static void addCell(Link text, Color backgroundColor, Table table, boolean b) {
Cell c1 = new Cell().add(new Paragraph(text));
text.setUnderline();
text.setFontColor(backgroundColor);
table.addCell(c1);
}
private static class CustomLinkRenderer extends LinkRenderer {
public CustomLinkRenderer(Link link) {
super(link);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
Integer targetPageNumber = TargetCounterHandler.getPageByID(this, getProperty(Property.ID));
if (targetPageNumber != null) {
setText(String.valueOf(targetPageNumber));
}
return super.layout(layoutContext);
}
#Override
public IRenderer getNextRenderer() {
return new CustomLinkRenderer((Link) getModelElement());
}
}
private static class CustomParagraphRenderer extends ParagraphRenderer {
public CustomParagraphRenderer(Paragraph modelElement) {
super(modelElement);
}
#Override
public IRenderer getNextRenderer() {
return new CustomParagraphRenderer((Paragraph) modelElement);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
TargetCounterHandler.addPageByID(this);
return result;
}
}
// For now we have to create a custom renderer for the root document to propagate the
// target counter handler to the renderer that will be used on the second layout process
// This is going to be improved in future iText versions
private static class CustomDocumentRenderer extends DocumentRenderer {
public CustomDocumentRenderer(Document document, boolean immediateFlush) {
super(document, immediateFlush);
}
#Override
public IRenderer getNextRenderer() {
CustomDocumentRenderer renderer = new CustomDocumentRenderer(document, immediateFlush);
renderer.targetCounterHandler = new TargetCounterHandler(targetCounterHandler);
return renderer;
}
}
}
I edited a Nebula example (_301_CustomDataProviderExample) to test the cell editing feature on pressing tab key (enable next cell editing on pressing tab key). The problem occurred when I used GridLayer: the cell editor was display incorrect. I think it's because of row and column headers but I don't know how to debug or fix. Can you help me or give me some hints? This is the code I have edited or added:
#Override
public Control createExampleControl(Composite parent) {
String[][] testData = new String[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
testData[i][j] = "" + i + "/" + j;
}
}
IDataProvider bodyDataProvider = new TwoDimensionalArrayDataProvider(testData);
final DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
SelectionLayer selectionLayer = new SelectionLayer(bodyDataLayer);
ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);
viewportLayer.addConfiguration(new DefaultEditConfiguration());
viewportLayer.addConfiguration(new DefaultEditBindings());
IDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(bodyDataProvider);
DataLayer rowHeaderDataLayer = new DataLayer(rowHeaderDataProvider, 40, DataLayer.DEFAULT_ROW_HEIGHT);
rowHeaderDataLayer.setColumnsResizableByDefault(true);
ILayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, viewportLayer, selectionLayer);
IDataProvider columnHeaderDataProvider = new LetterColumnHeaderDataProvider(20);
ILayer columnHeaderLayer = new ColumnHeaderLayer(
new DefaultColumnHeaderDataLayer(columnHeaderDataProvider), viewportLayer, selectionLayer);
IDataProvider conerDataProvider = new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider);
ILayer cornerLayer = new CornerLayer(new DataLayer(conerDataProvider), rowHeaderLayer, columnHeaderLayer);
GridLayer gridLayer = new GridLayer(viewportLayer, columnHeaderLayer, rowHeaderLayer, cornerLayer, false);
final NatTable natTable = new NatTable(parent, false);
natTable.setLayer(/* viewportLayer */ gridLayer);
natTable.addConfiguration(new DefaultNatTableStyleConfiguration() {
#Override
public void configureRegistry(IConfigRegistry configRegistry) {
super.configureRegistry(configRegistry);
configRegistry.registerConfigAttribute(
EditConfigAttributes.CELL_EDITABLE_RULE,
IEditableRule.ALWAYS_EDITABLE);
}
});
natTable.configure();
return natTable;
}
Added:
class LetterColumnHeaderDataProvider implements IDataProvider {
private int columns = 0;
public LetterColumnHeaderDataProvider(int columns) {
this.columns = columns;
}
#Override
public Object getDataValue(int columnIndex, int rowIndex) {
return (char) ('A' + columnIndex);
// TODO: support column header AA-AZ, ZA-ZZ, ZZZ...
}
#Override
public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
throw new UnsupportedOperationException();
}
#Override
public int getColumnCount() {
return this.columns;
}
#Override
public int getRowCount() {
return 1;
}
}
The issue is related to registering the DefaultEditConfiguration on the ViewportLayer. Doing this will result in placing the editor that is identified on key press relative to the body layer stack and not the GridLayer. The default configuration applied with the GridLayer contains the necessary edit configurations already. But you disabled it via the last parameter in the GridLayer constructor.
In short, remove the following two lines from your example, they are only needed in case of compositions without a GridLayer.
viewportLayer.addConfiguration(new DefaultEditConfiguration());
viewportLayer.addConfiguration(new DefaultEditBindings());
and change the creation of the GridLayer to this
GridLayer gridLayer = new GridLayer(viewportLayer, columnHeaderLayer, rowHeaderLayer, cornerLayer);
And as a hint, be careful when copying code from different examples when not understanding their purpose. The NatTable examples typically contain explanations in code to explain them in more detail.
My environment is: GWTP 0.7, GWT 2.4.0, GXT 3.0.1. This question is somehow related to GXT 3 grid scrolling issue but with the exception that the solution there does not work for me.
My case:
public abstract class AbstractGridView extends ViewImpl implements AbstractGridPresenter.MyView {
protected final VerticalLayoutContainer cont = new VerticalLayoutContainer();
protected final VerticalLayoutData toolBarData = new VerticalLayoutData(1, -1);
protected final VerticalLayoutData contentData = new VerticalLayoutData(1, -1); //grid's layout config
protected final ToolBar toolBar = new ToolBar();
protected final TextButton addItemButton = new TextButton(ADDBUTTON_TEXT);
#Inject
protected AbstractGridView() {
toolBar.add(addItemButton);
cont.add(toolBar, toolBarData);
}
public Widget asWidget() {
return cont;
}
}
public class DepartmentsView extends AbstractGridView implements DepartmentsPresenter.MyView {
private final Grid<Department> grid;
private final ColumnModel<Department> model;
private final List<ColumnConfig<Department, ?>> config = new ArrayList<ColumnConfig<Department,?>>();
private final ListStore<Department> store = new ListStore<Department>(PROPS.key());
#Inject
public DepartmentsView() {
super();
config.add(new ColumnConfig<Department, Long>(PROPS.id()));
config.get(config.size() - 1).setHeader(IDCOLUMN_HEADER);
config.add(new ColumnConfig<Department, String>(PROPS.name()));
config.get(config.size() - 1).setHeader(NAMECOLUMN_HEADER);
config.add(new ColumnConfig<Department, String>(companyNameProvider));
config.get(config.size() - 1).setHeader(COMPANYCOLUMN_HEADER);
model = new ColumnModel<Department>(config);
grid = new Grid<Department>(store, model);
grid.getView().setAutoFill(true);
filters.initPlugin(grid);
filters.setLocal(true);
filters.addFilter(idFilter);
filters.addFilter(nameFilter);
filters.addFilter(companyFilter);
cont.add(grid);
}
}
All these are injected into BorderLayoutContainer in BaseView:
public class BaseView extends ViewImpl implements BasePresenter.MyView {
private final Viewport viewPort = new Viewport();
private final BorderLayoutContainer borderContainer = new BorderLayoutContainer();
private final ContentPanel west = new ContentPanel();
private final ContentPanel north = new ContentPanel();
private final ContentPanel center = new ContentPanel();
private final BorderLayoutData westData = new BorderLayoutData(200);
private final BorderLayoutData northData = new BorderLayoutData();
private final BorderLayoutData centerData = new BorderLayoutData();
#Inject
public BaseView() {
borderContainer.setBorders(true);
west.setResize(true);
center.setResize(false);
center.setHeight("auto");
north.setResize(false);
westData.setCollapsible(false);
westData.setCollapseMini(false);
westData.setMargins(new Margins(5, 5, 5, 5));
northData.setCollapsible(false);
northData.setMargins(new Margins(5));
northData.setSize(57);
centerData.setCollapsible(false);
centerData.setMargins(new Margins(5));
borderContainer.setWestWidget(west, westData);
borderContainer.setCenterWidget(center, centerData);
borderContainer.setNorthWidget(north, northData);
viewPort.add(borderContainer);
}
public Widget asWidget() {
return viewPort;
}
#Override
public void setInSlot(Object slot, Widget content) {
setMainContent(content);
}
private void setMainContent(Widget content) {
center.clear();
if (content != null) {
center.setWidget(content);
}
}
}
So, if I do not attach contentData when adding my grid to VerticalLayoutContainer (cont) it renders equally to as VerticalLayoutData(1, -1) which is: grid's height is not computed at all and it's content flows under the bottom of the page with no scrollbar to get to any row lower the bottom page border. If I set VerticalLayoutData (1, 1) as in the link at the beginning of my question I can only see grid's header and grid's content's height is computed to 0px (though it's present in the page's DOM). And only if I set height manually, for example setHeight(300) grid's height sets that quantity of pixels and vertical scrollbar is shown to get to any row in the grid's store, though one can easily understand manually setting grid's height is not of any reason solution in case of Viewport managed application window.
What have I missed in widgets config or is this a bug with any reasonable workaround for it?
I've found the layout problem:
center.setResize(false); // BaseView's constructor
That made BorderLayoutContainer's ContentPanel not to resize child widget (VerticalLayoutContainer) to fit.
I am using a CellTable<Contact> in my GWT 2.4 project. Everything worked perfectly, so I decided to add paging to the table by using a SimplePager. The CellTable now displays the correct number of entries (page size), but all the pager buttons are disabled.
I am doing the following:
...
#UiField(provided=true) CellTable<Contact> contactsTable = new CellTable<Contact>();
#UiField SimplePager pager;
private TextColumn<Contact> nameColumn;
private TextColumn<Contact> surnameColumn;
public ViewContactsViewImplDesktop() {
initWidget(uiBinder.createAndBindUi(this));
initTable();
}
#Override
public final void updateContactList(final ArrayList<Contact> contacts) {
contactsTable.setRowCount(contacts.size());
final ListDataProvider<Contact> dataProvider = new ListDataProvider<Contact>();
List<Contact> list = dataProvider.getList();
for (final Contact c : contacts) {
list.add(c);
}
dataProvider.addDataDisplay(contactsTable);
pager = new SimplePager();
pager.setDisplay(contactsTable);
pager.setPageSize(3);
ListHandler<Contact> nameColumnSorter = new ListHandler<Contact>(list);
ListHandler<Contact> surnameColumnSorter = new ListHandler<Contact>(list);
nameColumnSorter.setComparator(nameColumn, new Comparator<Contact>() {
#Override
public int compare(Contact c1, Contact c2) {
return c1.getName().compareTo(c2.getName());
}
});
surnameColumnSorter.setComparator(surnameColumn, new Comparator<Contact>() {
#Override
public int compare(Contact c1, Contact c2) {
return c1.getSurname().compareTo(c2.getSurname());
}
});
contactsTable.addColumnSortHandler(nameColumnSorter);
contactsTable.addColumnSortHandler(surnameColumnSorter);
contactsTable.getColumnSortList().push(nameColumn);
}
private void initTable() {
nameColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.getName();
}
};
surnameColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.getSurname();
}
};
nameColumn.setSortable(true);
surnameColumn.setSortable(true);
contactsTable.addColumn(nameColumn, "Name");
contactsTable.addColumn(surnameColumn, "Surname");
}
Thanks!
Not setting the page size and/or not setting the table's row count manually could do the trick, as hinted in my comment.
I'd love to provide a concise code sample but do not have access to any code using cell widgets right now.