Vertical align 2 jface TreeViewers to specific element - java

I have two TreeViewer objects on a page (2 columns, one TreeViewer in each column), and I want to vertically align a tree when the other is scrolled or selected.
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.widgets.Tree;
I think the solution should look something like
treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
#Override
public void selectionChanged(SelectionChangedEvent arg0) {
TreeViewer mirrorTree = (treeViewer == treeVwrSource ? treeVwrTarget : treeVwrSource);
// find position of selected element (element x) in treeViewer
// set position of element x in mirrorTree, it is already selected.
}
});
Any advice?

From what I've read, scrolling tables or trees isn't directly possible (unless that has changed in the meantime). Programmatically changing the position of the thumb of the scrollbar would not change the viewport of the table/tree.
But you could try if the following snippet works for you:
Tree tree1 = ...;
Tree tree2 = ...;
int topIndex = tree1.indexOf(tree1.getTopItem());
tree2.setTopItem(tree2.getItem(topIndex));
You would call that code in a SelectionListener registered to the vertical scrollbar of your tree (tree.getVerticalBar()).
Synchronizing the selection is fairly easy (if both tree viewers display the same input/model):
viewer.setSelection(otherViewer.getSelection)
(called by the ISelectionChangedListener from your question).

For a complete example of how to synchronize two tables see this SWT Snippet.

Related

Description tooltips for Vaadin Grid header cells

I'd like to define descriptions for Grid header cells, similarly to how AbstractComponent.setDescription(String description) works (i.e. tooltip shown on mouse hover). As the Grid doesn't support this in itself, I tried adding a Label component into the header cell, and then use setDescription() on the label. I can get the info tooltip working like this, but the downside is that clicking on the label component doesn't trigger sorting. If I want to sort the column, I need to click the header cell on the really narrow area that's left between the right edge of the label component and the column border, where the sorting indicator will be shown. If you look at the screenshot below, the highlighted area is the label component, and in order to trigger sorting, the user needs to click on the space on the right side of the component.
Is there a better way to apply descriptions to header cells than the one I described? And if not, is there a way to make the sorting work properly when the header cell contains a Component?
Based on the answer from kukis, I managed to come up with a simpler solution that doesn't require any JavaScript. Instead of adding a Label component into the header cell, I'm adding a div element manually with StaticCell.setHtml(), and setting the title attribute on it:
#Override
protected void init(VaadinRequest request) {
Grid grid = new Grid();
grid.addColumn("to");
grid.addColumn("the");
grid.addColumn("moon");
Grid.HeaderRow headerRow = grid.getDefaultHeaderRow();
headerRow.getCell("to").setHtml("<div title='Hello world'>to</div>");
headerRow.getCell("the").setHtml("<div title='Hello world 2'>the</div>");
headerRow.getCell("moon").setHtml("<div title='Hello world 3'>moon</div>");
grid.addRow("1","2","3");
grid.addRow("d","v","w");
grid.addRow("g","s","h");
setContent(new VerticalLayout(grid));
}
Feature added to Vaadin 8.4.0
Feature added to Grid in Vaadin 8.4.0.
Ticket:
https://github.com/vaadin/framework/pull/10489
Release notes:
https://vaadin.com/download/release/8.4/8.4.0/release-notes.html
Grid headers and footers now support tooltips.
Well, since Grid doesn't support it by itself you can always use JavaScript to achieve desired behaviour. SSCCE:
private final String SCRIPT;
{
StringBuilder b = new StringBuilder();
b.append("var grid = document.getElementById('mygrid');\n");
b.append("var child = grid.getElementsByClassName('v-grid-tablewrapper')[0];\n");
b.append("child = child.firstChild.firstChild.firstChild;\n");
b.append("child.childNodes[0].title='Hello world';\n");
b.append("child.childNodes[1].title='Hello world 2';\n");
b.append("child.childNodes[2].title='Hello world 3';\n");
SCRIPT = b.toString();
}
#Override
protected void init(VaadinRequest request) {
final VerticalLayout layout = new VerticalLayout();
Grid grid = new Grid();
grid.addColumn("to");
grid.addColumn("the");
grid.addColumn("moon");
grid.addRow("1","2","3");
grid.addRow("d","v","w");
grid.addRow("g","s","h");
grid.setId("mygrid");
setContent(layout);
layout.addComponent(grid);
JavaScript.getCurrent().execute(SCRIPT);
}
Another possibility would be to develop your own Grid in GWT based on the Grid provided by Vaadin Team but it is a way higher cost approach.
Another solution would be to, as you have tried, put label in a column and propagate the label-clicked-event to the Grid.
I use my own utillity finction:
public static Grid setHeaderCellDescription(Grid grid, int rowIndex, String property, String description) {
Grid.HeaderCell cell;
String cellHeader = "<span title=\"%s\">%s</span>";
cell = grid.getHeaderRow(rowIndex).getCell(property);
cell.setHtml(String.format(cellHeader, description, cell.getText()));
return grid;
}
You may add some additional checks if need (existing of cell and row number).
Or other variant - instead setHtml use cetComponent.
Grid.HeaderCell cell = grid.getHeaderRow(rowIndex).getCell(property);
Label newLabel = new Label(cell.getText());
newLabel.setDescription(description);
cell.setComponent(newLabel);
Update for Vaadin 23: you can use your own Component as a column header with this method: com.vaadin.flow.component.grid.Grid.Column#setHeader(com.vaadin.flow.component.Component).
So you can use e.g. a Span with a title:
Span headerComponent = new Span();
headerComponent.setText("Your header text");
headerComponent.getElement().setProperty("title", "Your tooltip text");
column.setHeader(headerComponent);

Grouping objects in JavaFX?

So I'm currently making a JavaFX GUI (with SceneBuilder) for a Connect4 game, and I was wondering if there's a way to 'group' objects together so that I can perform an action on all of them together?
Examples of what I mean:
I have 7 buttons for the columns (colButton1-7) and I want to disable all of them at once.
I use Ellipses for counters (counter1-40) and would like to change the color of all of them to white.
I searched around but couldn't find anything. I know how to do both for an individual object, but can't think of a way to easily apply the changes to all of them at the same time. Any help would be appreciated :)
There is no grouping mechanism to perform a single action on all of the members of the same group. On the contrary, you can have a single group/container to hold all your controls and apply the same action to each of its member.
For example, lets say I have a VBox containing Buttons and I want to disable them all.
for(Node node:vBox.getChildren()) {
node.setDisable(true);
}
or, to set Styling
for(Node node:vBox.getChildren()) {
node.setStyle("-fx-something");
}
For disabling, if you disable a node, then all it's child nodes will have disabled set to true. So you can do:
VBox buttonHolder = new VBox();
Button button = new Button(...);
buttonHolder.getChildren().add(button);
// repeat as necessary...
buttonHolder.setDisable(true); // all buttons in the VBox will now be disbaled
For styled properties, such as the fill of a shape, you should use an external style sheet. If you change the style class of the parent, then with an appropriate external style sheet you can change the style of all the children in one shot.
E.g.
Pane counterPane = new Pane();
for (int i=0; i<numCounters; i++) {
Ellipse counter = new Ellipse(...);
counter.getStyleClass().add("counter");
counterPane.getChildren().add(counter);
}
// ...
counterPane.getStyleClass().add("counter-pane"); // all counters white
// change to red:
counterPane.getStyleClass().remove("counter-pane");
counterPane.getStyleClass().add("warning");
External style sheet:
.counter-pane > .counter {
-fx-fill: white ;
}
.warning > .counter {
-fx-fill : red ;
}

ListCellRenderer setting all rows to the same color

My intention is to use the ListCellRenderer in order to highlight red cells that contain links that have been visited(or clicked) and green those which have not been visited this works partially but not quite. It seems that the renderer works as far as it concerns marking the cells red. If I however add more rows, they come all red colored thereafter. In addition if I mark two cells that are not adjacent then it marks them all red as well.
I have a class Feed, where I initially had a boolean variable, but I have modified the code so that the m_isRead variable is in the listModel here is the constructor:
public Feed (URL url, String urlName) {
this.m_urlName = urlName;
this.m_observers = new ArrayList<Observer>();
this.m_isRead = isRead;
}
Now this instance variable is set to false in the listModel Class which is the one that contains the renderer.
m_isRead = false.
When using the ListCellRenderer which I now have adjusted so that it does not require this method:
m_feeds.get(index).getM_urlName();
I proceed as follows:
class MyCellRenderer extends JLabel implements ListCellRenderer {
public MyCellRenderer() {
setOpaque(true);
}
public Component getListCellRendererComponent(JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
setText(value.toString());
Color background = Color.GREEN;
Color foreground = Color.BLACK;
//find out if the specific has been read or not
if (m_feeds.get(index).isM_isRead() == true) {
background = Color.RED;
foreground = Color.WHITE;
} else {
background = Color.GREEN;
foreground = Color.BLACK;
};
setBackground(background);
setForeground(foreground);
return this;
}
}
Then I have another inner class with a method which I use to get the selected item, at that point I set m_isRead to true (to read) this is now independent from the Feed class and the code which related to it has been commented out:
public class ChangeSelectedIndex implements ListSelectionListener {
#Override
public void valueChanged(ListSelectionEvent e) {
for (int i = 0; i < m_listModel.size(); i++) {
if (m_listModel.getElementAt(i) != null) {
m_urlName = m_list.getSelectedValue();
initiateParsing();
m_updateFeedButton.setEnabled(true);
// TODO fix behavior for cell renderer
//this sets the value of the feed being clicked to true
// m_feeds.get(i).setM_isRead(true);
m_isRead = true;
}
}// end for
}
}
Now the result is the same, if I add the rows they are green and that is correct, if I click on each row each turns read provided that I have clicked the adjacent rows to the first one I click but if I, for example, have four rows and I click the first row and the last row, all the rows, including those in between (which I have not clicked) turn red. Likewise, if I add new rows they come in red. That is if I click even one of those rows then the ones I add thereafter will be red.
Can anybody help?
Thank you in advance,
Cheers
After a while thinking about it I have concluded that there was nothing wrong my original cell renderer, it has had to do with the list model itself. The JList simply did not support multiple NON contiguous selection without clicking the Ctrl button right out of the box. This is what triggered further searching on my side on how to emulate the Ctrl click down; which I found here on answer number 8 (working code):
Individual and not continuous JTable's cell selection
The interesting here is adding the mouse event to the list. This mouse event emulates a Ctrl down event, which the ListSelectionModel which is used by JList as well as JTable is set to MULTPLE_SELECTION_INTERVAL it behaves as desired. That is, the user now able to click on whatever feed, even if it is not contiguous, and it will color the desired feed or feeds without coloring whatever unclicked feed may lay in between.
As for the renderer, it would suffice to use the isSelected parameter which comes in through with its method getListCellRenderer(). However, in my case, what I had done has the same effect with the addition that I was using an array to add all the statuses of the feeds, meaning, read or unread. Proceeding this way, I had in mind that if I closed the program and save the feed list, including its isRead parameter set to either true or false, then later on upon retrieving the feed list, the same feed status would be restored from, for example, a file, or at least that is what I had in mind.

how to add a clickHandler on a subRow of celltable in GWT

I am using Custom Data Grid from GWT showcase example ..
http://gwt.googleusercontent.com/samples/Showcase/Showcase.html#!CwCustomDataGrid
Every thing is working fine ..I have sub Rows inside my rows in the cell table ..
I have anchor cell.. which are in the main row and in the sub row ..
the ClickHandler for the main row is working but not in the sub row ..
this is my code for that cell
// ViewDetail.
td = row.startTD();
td.className(cellStyles);
td.style().trustedColor("blue");
td.style().cursor(Cursor.POINTER);
if (isNetworkRow) {
//td.text("subRowsAnchor");
} else {
}
renderCell(td, createContext(19), viewDetailsColumn, rowValue);
I am rendering the cell in both cases , either its a row or sub row
so i can see the anchor and its clickHandler also works ..
Is there any way i can differentiate that which anchor is been clicked ,, main rows or sub row's.
I just tried to make a small work around . i.e changing the name of the anchor text if its a sub row .. as u can c in my code ..td.text..
but then get the error on renderCell...
Attributes cannot be added after appending HTML or adding a child element.
Any idea , what could be solution...
thanks
To distinguish between which row has been clicked (according to the showcase sample, but should be the same in general), simply rely on which row has been selected (provided that you haven't overridden/disabled the selection handling).
Set up a FieldUpdater to the column (that renders itself using your anchor cell) and check for the subrow selection using getKeyboardSelectedSubRow(). Something like:
yourColumn.setFieldUpdater(new FieldUpdater<T, String>() {
public void update(int index, T object, String value) {
if (yourGrid.getKeyboardSelectedRow() != -1 ) {
if (yourGrid.getKeyboardSelectedSubRow() > 0) {
// Subrow selected.
} else {
// Main row selected.
}
}
}
});

How to force a minimum height of a JFace Wizard in Eclipse?

I'm presenting a wizard (code here) that gets too low, so the content does not show completely (see screenshot below):
How can I force the wizard to have a minimum height?
According to the answer on this question here on StackOverflow, the wizard will be the same height as the largest wizardpage in the wizard, but my wizard obvilusly does not get resized according to at least the content of the largest page, and I also tried to set the minimum height of the first visible wizard page with code like this in the WizardPage class:
#Override
public void createControl(Composite parent) {
// create the composite to hold the widgets
Composite composite = new Composite(parent, SWT.NULL);
composite.setSize(300, 1024); // TODO: Does this work?
GridData gridData = new GridData(300, 1024);
gridData.heightHint = 1024;
gridData.minimumHeight = 1024;
composite.setLayoutData(gridData);
... but without success so far.
Any hints?
Try to set
parent.setLayout(new GridLayout());
to your createControl() implementation of your first page.
It appears the parent you get in that method has an instance of PageContainerFillLayout as layout, not GridLayout.
If you have access to your WizardDialog, you could also call
wizardDialog.setMinimumPageSize(300, 1024)
You may need to specify a height hint for the GridData.
Alternatively, you could use a three column GridLayout and get all the rows after the first to span columns 2 and 3. I tend to use GridLayout and haven't had this problem before.
you can use this codes below:
parent.addControlListener(new ControlAdapter() {
public void controlResized(ControlEvent event) {
parent.setSize(parent.computeSize(490, 320));
}
});
I just had a very similar problem (on Windows): A wizard where the wizard page had no message was not shown completely. What quickly solved it for me was to just add a message with a single space in the wizard page constructor:
class Page extends WizardPage {
Page() {
super("page.id", "Page title", null);
setMessage(" ");
The accepted answer did not work for me (the wizard has quite some fields).

Categories