I have a Wizard that contains multiple components. On the wizard I have drop-downs that can filter the results, which then hides various rows that are not required. However when these rows are hidden the components remain in the same location and do not re-size/move up. My problem I believe is similar to this issue (SWT components relayout after visibility set to false) however it uses RowLayout instead. I have also tried copying what was done but it did not change anything. How would I get the components to be placed togeather instead of leaving gaps?
I'v also read that .pack() and .revalidate() might be options, but I cannot seem how to make them work with the wizard classes. As best I can tell, it is using org.eclipse.jface.wizard for the base components. Any advice is highly appreciated for this, and I will update my question as requires if it needs to be clarified.
Edit
Attempting to recreate the style outlined in the link above, I came up with this (please note the project abstracts a few things away, so I have to make some extra calls). However after implementing I see no notable difference in the program (e.g. everything is still in the exact same spots, no spacing differences).
RowData data = new RowData();
RowData data2 = new RowData();
guiPiece.getGUILayout().getPiece("label").setLayoutData(data);
guiPiece.getGUILayout().getPiece("field").setLayoutData(data2);
guiPiece.getGUILayout().getPiece("label").setVisible(newVisibility);
data = (RowData)guiPiece.getGUILayout().getPiece("label").getLayoutData();
data.exclude = !newVisibility;
guiPiece.getGUILayout().getPiece("field").setVisible(newVisibility);
data2 = (RowData)guiPiece.getGUILayout().getPiece("field").getLayoutData();
data2.exclude = !newVisibility;
guiPiece.getFieldParentingControl().layout(true);
You can use a similar method to the GridLayout link you provide but using RowLayout.
You need to set RowData layout data on each of your controls.
RowData data = new RowData();
control.setLayoutData(data);
RowData also has an exclude flag which you can set the include / exclude a control from the layout.
So to hide a control you would do:
control.setVisible(false);
RowData data = (RowData)control.getLayoutData();
data.exclude = true;
You then call layout(true) on the main Composite of the wizard page.
Related
How to highlight the row under the cursor when dragging and dropping?
I need to identify the row in the jtable I'm dragging onto, not the insert position which the default transfer handler supplies out of the box.
Having identified the row itself, I expect that importData(TransferSupport support) can handle the fine details.
This is a known issue in the themes for RadGridView. Currently, the ControlTemplate for GridViewRow did not have Template bindings for BorderBrush and BorderThickness. This means that the change you are making did not get propagated to the Border element that is in the GridViewRow's template. We will fix this for our official release.
However, there is a way you can do this in the current situation. You should just find the border in the template. Here is an example of how to achieve this:
var border = rowItem.ChildrenOfType<Border>().FirstOrDefault();
if(border != null) {
border.BorderBrush = new SolidColorBrush(Colors.Red);
border.BorderThickness = new Thickness(1);
}
myTable.setDropMode(DropMode....); is what I was after.
Options are:
DropMode.USE_SELECTION
DropMode.ON
DropMode.INSERT
DropMode.ON_OR_INSERT
The final one ( DropMode.ON_OR_INSERT) provided me with more than what I needed, (which is good) - the ability to distinguish between inserting a row between other rows, AND an indication of dropping data ONTO a row.
I should have done more research initially before posing the question.
I am building a JTreeTable. I found some starter code and have come pretty far. In the end my goal is to be able to have different data at different levels like a hierarchical list.
Currently, I have it working with data at different levels. However, I am running up against a wall when it comes to changing the columns as a next goal. From where I currently stand I have 3 more milestones:
Show different set of columns for different levels
Ability to adjust column widths for different levels
Ensure the JTree part of the table always stays to left
I am getting close to closing out this task but again stuck at the first of these 3.
Since creating a JTreeTable is complex, the minimum example leverages several class listed below in the image:
I am happy to post the code to any of those classes but I also did not want clog the question with useless code. First let me show the functionality I want.
The first image is when the top level is selected and the second image is when the second level is selected. Notice how the columns are different. That is what I want to happen in my application.
Top level selected:
Second level selected:
So one way I tried to solve this problem, is when the list selection is changed inside this section of code:
ListSelectionListener listener = (ListSelectionEvent e) -> {
TreeTableModelAdapter adapter = (TreeTableModelAdapter) JTreeTable.this.getModel();
//Need to see why this breaks.
JTreeTable.this.getTableHeader().setColumnModel(adapter.getColumnModel());
};
this.getSelectionModel().addListSelectionListener(listener);
This code is in the initialization of the JTreeTable. I have tried setting the column model on both the TableHeader and the table as well. Below is what happens then when I select a row:
The columns just disappear on me. The creation of the column model is happening in the TreeTableModelAdapter class with the following method:
public TableColumnModel getColumnModel(){
DefaultTableColumnModel model = new DefaultTableColumnModel();
for(int i=0;i<getColumnCount();i++){
TableColumn column = new TableColumn();
column.setIdentifier(getColumnName(i));
model.addColumn(column);
}
return model;
}
Any direction would be very helpful. Again happy to post any code you think could be helpful to answer the question. Just put a comment in and I will add it right away.
I will add the milestones as I find them in case this helps others, but for now this question is answered.
Milestone 1
I was actually able to solve the first milestone. The key is to trigger the creation of the columns of the column model, not to create a new column model. Below is the code for when the row selection is changed:
//Change columns depending on row
ListSelectionListener listener = (ListSelectionEvent e) -> {
createDefaultColumnsFromModel();
};
this.getSelectionModel().addListSelectionListener(listener);
This code creates the columns based on the row selected in the JTree part of the JTreeTable. The TreeTableModelAdapter implements the getColumnCount() and getColumnName() methods by also passing the selected row in the JTree to the JTreeTableModel so that the columns and their names are dynamically retrieved based on a particular node in the JTree. The key for this for me was trigger those to be called again to update the JTreeTable.
Milestone 2
Adjusting column widths based on the data level proved to be much more difficult than I had originally anticipated. In order to retain the cells state when the column model changed I had to disconnect the painting of the cells from it. This is a hairy process because this is done inside BasicTableUI and the method that gets the rectangle of the cell is private. So I had to subclass it, overload the paint() method and create my own methods that get called inside the paint method. There was a lot of copy pasting so that I could call normally private methods. I just renamed them and referenced these methods instead. The way the ui class was designed did not make it very flexible. Below is 2 images where I am selecting different levels and the columns are obviously different widths at different levels.
Milestone 3
I was able to make this work by keeping track of the view in the model. This seems very dirty to me as the model should separated from the view. Since the tree column's class is unique, I just returned the right class if that column was the first in the view.
The one problem I have with this technique is that I get unexpected behavior where the value returned is not consistent. I attempted to resolve this by overriding JTree.covertValueToText(). Since a JTree only expects 1 value and depending on the sequence of columns in the view this value could change. So in overriding this method I check the stored index for the JTree column's value. Again this causes the unexpected behavior. I will update the post if I find the fix.
I want to do plain UI testing (i.e., not using SWTBot or other UI test frameworks) of NatTable contents.
My approach is to create a shell, add my custom NatTable and then access the cell and check its contents (data value, config label etc.):
// Note: this is Xtend code
#Before
def void setup()
{
shell = new Shell(Display.getCurrent)
shell.layout = new FillLayout
parent = new Composite(shell, SWT.NONE)
parent.layout = new GridLayout
fixture = new MyNatTableViewer(parent) // this is my custom nattable impl under test
shell.pack
shell.visible = true
}
#Test
def void testLabel()
{
assertCellLabel(2, 2, "test-label");
}
def assertCellLabel(int row, int col, String expected)
{
val labels = parameterTable.getCellByPosition(col, row)?.configLabels
assertThat(labels).describedAs("Labels for row " + row + " col " + col).isNotNull
assertThat(labels.labels).describedAs("Labels for row " + row + " col " + col).contains(expected)
}
To test my other components it was enough to just create the shell and the parent composite; packing and setting visible was not required for my tests to work.
Yet, with NatTable, getCellByPosition() returns null if the cell is not visible - so I added the code to pack and set the shell visible. This works for small tables (with 2 rows and a few columns).
Sadly, it does not work for large tables. I suspect this is because the viewport layer does not create cells which are not in the visible area (which is, I know, the strength of NatTable - that it only creates the required structures on demand). This is, of course, desired for normal runtime behavior.
But is there a(nother) way to get the cell in a guaranteed way (in other words, can I make the NatTable/ViewportLayer believe that the cell is visible so I don't get null as long as the cell exists content-wise?)
I could, of course, test my label accumulators, data providers etc. directly, but I wanted to approach this more from a black-box point of view here.
That question is contradictory in itself. You are asking for a black box approach for testing NatTable, but you want to change the behavior of NatTable on testing. That is not a black box approach!
If you really want to test with a black box approach, you need to ensure that the cell is rendered. This can be done by triggering scrolling, e.g. by executing the ShowCellInViewportCommand. That is the real black box approach, because returning null for a non-visible cell is the correct result.
If you need something in between a real black box approach and an approach that makes use of internal knowledge (which you are asking for) you have to ways to get there.
Operate on a layer below the ViewportLayer. Typically the SelectionLayer can be used. But of course this doesn't need to mean anything, because the layer stack can differ from setup to setup. The ViewportLayer is the one that introduces the virtual nature to a NatTable and the scrolling ability. It avoids the access to the underlying layers. So asking one of these will return the value you expect.
Disable the ViewportLayer by executing the TurnViewportOffCommand. This is basically a hack and could trigger additional things in the back that you might not want. But I have seen that suggestion in other contexts and therefore want to name it here. I don't suggest to use it anyway!
Note that both approaches are more like hacks when we are talking about black box testing, because you are making assumptions to the composition. They can not be applied in general because of the various configuration abilities.
Regarding the hidden question about why you need to set the Shell visible. Well basically because the SWT events for painting and resizing need to be triggered in order to start the size calculations and printing of NatTable correctly according to the Shell state. In our examples (which are also plain SWT) we call Shell#open().
And as a last comment on your implementation, I don't understand why you are sub-classing NatTable. Our API was never intended to do that. I suppose you do this to do some static pre-configuration e.g. the layer stack. But personally I don't like that approach. Everytime someone extends our classes to override some internal methods it ends up in questions or bug reports because the behavior changes. But I think that is generally an issue of an open API to give developers the most possible flexibility on customization.
I am also trying to read data from invisible cells of nattable. I am trying to use ShowCellInViewportCommand as follows:
widget.doCommand(new ShowCellInViewportCommand(gridlayer.getBodyLayer(), column, row));
//where row is say 50 and column is 20 and the cell is invisible.
I also tried,
widget.doCommand(new ShowRowInViewportCommand(widget.getLayer(), row));
//here the default value given by nattable.getLayer is passed
widget corrosponds to nattable instance.
After the call, nothing happens in UI. The cell does not get displayed.
Do I need to do anything else?
How should I go to read invisible cells of nattable.
Ok so I think the title is all I've got to ask here.
My AbstractTableModel works fine, when I want to add an empty row, I use the following code to do so:
public void addRow() {
Object[][] oldData = data;
data = new Object[oldData.length + 1][3];
// Copy old data to new data
for (int x = 0; x < oldData.length; x++) {
data[x] = oldData[x];
}
// Append new row
data[oldData.length] = new Object[] {"", "", false};
fireTableRowsInserted(data.length - 2, data.length - 1);
}
Now, since I'm showing an empty row, I want the user to edit it, and I assume that the user will. Now, how do I make sure that the data is saved in my array as the user makes changes? Or, if that's not possible, what better alternative is there?
Ok so I should probably explain what I want to do:
I'm loading contents from a file, and displaying as a table. Now, the user might add new rows to the table on clicking the Add Row button. This will add 1 empty row per click. (In this image above is an instance when the button is pressed twice.
Now, I want that the user can edit the cells, and then maybe delete some rows (maybe) but on clicking the Save Database button, the updated data in the table is stores.
You may get a better understanding by comparing several approaches:
In this example, either a background thread or a button can add a new row to a table. The background thread sequences instances of Runnable on the EDT via invokeLater(). It is conceptually easier to understand, but it is also easier to get wrong.
SwingWorker encapsulates sequencing access to shared data. The API and tutorial show the basic approach of updating a component's model using publish()/process(). This preferred mechanism is more robust and scalable.
In this more advanced example, a Swing Timer paces an Executor that controls a series of SwingWorker instances.
In all cases, the tables remain functional. You might use any as a basis for an sscce.
Addendum: Could you explain a little more about the strategies you suggested?
I've updated the list of examples and suggested some things to look for in context.
My current application uses a JList and everything is well (the only customization I did was to set the italic font on some of the rows).
Now I want to "upgrade" the user interface and instead of just labels in the List, I want a checkbox and a text field to be able to update the entry.
I started changing the code and adding a custom cell renderer and a custom cell model. My current problem is that the JPanel that the cell renderer is returning is not using the whole width of the container, so several list items are actually shown on the same line.
But now, I am wondering whether I should just change the whole thing to use JTable. I still need to add / remove items in the list though...
Any suggestion which one is better ? and if going with the JList, how should I go about fixing my current problem ?
In my experience using JTable is usually easier as it allows more complex data and functionality out-of-the-box. Usually when I try to do something the JList can't do, I just switch to JTable without a second thought. What you want sounds like something that should be pretty trivial to implement in a table. I suggest you try it out with some mock data to see if you can make it look and work the way you like (especially in case you want it to look like a list).
Try calling setLayoutOrientation(JList.VERTICAL) on your JList. That will restrict JList to a single column.