Discrete scrolling in SWT Table - java

How to do a discrete (row-by-row) scrolling in SWT Table (JFace TableViewer)?
I need a Table to be scrolled "one unbroken row at a time", putting a full cell on top.
I use JFace TableViewer, but I didn't find a way to add a mouse-listener to it, so I made something like this:
TableViewer table = new TableViewer(shell, SWT.BORDER_DASH |SWT.FULL_SELECTION);
//some visual settings ommited here
table.getControl().addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseScrolled(MouseEvent e) {
Table sourceControl = (Table)e.getSource();
System.out.println(e.count);
if(e.count >=0)
sourceControl.setTopIndex(sourceControl.getTopIndex()-1);
else
sourceControl.setTopIndex(sourceControl.getTopIndex()+1);
}
});
But it turned out, that first of all if e.count equals to 3 or more, some rows are being missed. Secondly sometimes setTopIndex() not placing rows correctly.
Can it be done in more accurate way?

From what I can tell, adding a e.doit = false in the Listener works perfectly. Here is an example:
public static void main(String[] args)
{
final Display display = new Display();
Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new FillLayout());
final Table table = new Table(shell, SWT.NONE);
for (int i = 0; i < 100; i++)
{
TableItem item = new TableItem(table, SWT.NONE);
item.setText("Item " + i);
}
table.addListener(SWT.MouseWheel, new Listener()
{
#Override
public void handleEvent(Event e)
{
e.doit = false;
if (e.count >= 0)
table.setTopIndex(table.getTopIndex() - 1);
else
table.setTopIndex(table.getTopIndex() + 1);
}
});
shell.pack();
shell.setSize(shell.getSize().x, 300);
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
The only case where the TableItem at the top isn't displayed completely is when you reach the end of the Table and the table's height isn't an exact multiple of the TableItem's height.

Related

SWT Table per-pixel scrolling

SWT Table has void setTopIndex(int index) and int getTopIndex().
I really need setTopPixel(int pixel) or setTopIndexFractional(double index).
I tried putting a Table into a ScrolledComposite, as such:
public class PixelTable extends ScrolledComposite {
private final Table table;
private final double itemHeight;
public PixelTable(Composite parent) {
super(parent, SWT.V_SCROLL | SWT.BORDER);
// setup the ScrolledComposite
table = new Table(this, SWT.VIRTUAL);
this.setExpandHorizontal(true);
this.setContent(table);
itemHeight = table.getItemHeight();
// setup the Table
table.setLinesVisible(true);
table.addListener(SWT.SetData, new Listener() {
#Override
public void handleEvent(Event e) {
int row = e.index;
TableItem item = (TableItem) e.item;
item.setText(Integer.toString(row));
}
});
}
/** Returns the fractional top index. */
public double getTopIndex() {
return getOrigin().y / itemHeight;
}
/** Sets the fractional top index. */
public void setTopIndex(double top) {
this.setOrigin(0, (int) Math.round(top * itemHeight));
}
/** Sets the item count. */
public void setItemCount(int count) {
table.setItemCount(count);
Point size = table.computeSize(SWT.DEFAULT, SWT.DEFAULT);
table.setSize(size);
}
}
This works for while, but once the inner Table's size gets bigger than ~65,000 pixels (~2,500 rows), the Table shows its scroll bar as well (probably an unsigned short somewhere on the native side). Additionally, it will be pretty tricky to get Table headers to work with this approach, since they'll scroll away.
Does anybody know what the issues would be (or how to get started) to add a per-pixel interface to the SWT API? I'm willing to branch the SWT source if necessary. I only care about the latest APIs for Win / OS X / Linux (e.g. I don't care if I have to break compatibility with old stuff).
If you want per-pixel scrolling, then just do the scrolling yourself. All you need to do is listen for SWT.MouseWheel, set Event#doit to false and then set the selection of the ScrollBar according to the scroll direction (increase/decrease by 1 (<-- that's the one pixel)).
Here is an example:
public static void main(String[] args)
{
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new FillLayout());
final Table table = new Table(shell, SWT.V_SCROLL);
for (int i = 0; i < 100; i++)
new TableItem(table, SWT.NONE).setText("Item: " + i);
table.addListener(SWT.MouseWheel, new Listener()
{
#Override
public void handleEvent(Event e)
{
int increment = table.getVerticalBar().getSelection();
if (e.count < 0)
increment++;
else if (e.count > 0)
increment--;
table.getVerticalBar().setSelection(increment);
e.doit = false;
}
});
shell.pack();
shell.setSize(400, 200);
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
display.dispose();
}

SWT Adding KeyListener to a Display

I want to add a KeyListener to my existing window. I want to catch 3 KeyDown's. On the first KeyDown I want to put something in a Combo. On the second KeyDown I want to put something in another Combo. If both textbox are filled, I want the next KeyDown to simulate the OK Button.
But I have a problem with the error widget disposed. Because I dont know when to remove the filter correct. This only happend if I open the window again!
My Code:
_disp.addFilter(SWT.KeyDown, new Listener() {
public void handleEvent(Event e) {
if(!_disp.isDisposed()){
_disp.removeFilter(SWT.KeyDown, this);
}
if (e.keyCode == SWT.CR) {
if (_cmbCCID.getText().isEmpty()) {
_cmbCCID.setText(_lastFiveCCID[0]);
} else if (_cmbDescription.getText().isEmpty()) {
_cmbDescription.setText(_lastFiveComment[0]);
} else if (!_cmbCCID.getText().isEmpty() && !_cmbDescription.getText().isEmpty()) {
_btnOk.notifyListeners(SWT.Selection, new Event());
}
}
}
});
You're removing the filter after the first key press. Try something like this:
public static void main(String[] args)
{
final Display display = new Display();
Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new FillLayout());
new Text(shell, SWT.NONE);
display.addFilter(SWT.KeyDown, new Listener()
{
int i = 0;
#Override
public void handleEvent(Event arg0)
{
if (i < 2)
System.out.println("Press " + i);
else
{
System.out.println("Press " + i);
System.out.println("Remove");
if (!display.isDisposed())
display.removeFilter(SWT.KeyDown, this);
}
i++;
}
});
shell.pack();
shell.open();
while (!shell.isDisposed())
{
while (!display.readAndDispatch())
{
display.sleep();
}
}
}
It will remove the filter after the third key press event.

How to combine and validate two text fields for a swt dialog?

i have another question. I use a ModifyListener for one textfield to activate and deactivate the OK-Button in a swt dialog. It works great.
Now I want to add a ModifyListener for another textfield. I want that the OK-Button only is activated if in both text fields is min one char.
This is the code of the two fields:
descriptionText.addModifyListener(new ModifyListener(){
public void modifyText(ModifyEvent e) {
Text text = (Text) e.widget;
if (text.getText().length() == 0) {
getButton(IDialogConstants.OK_ID).setEnabled(false);
}
if (text.getText().length() >= 1) {
getButton(IDialogConstants.OK_ID).setEnabled(true);
}
}
});
}
the second field:
ccidText.addModifyListener(new ModifyListener(){
public void modifyText(ModifyEvent e) {
Text text = (Text) e.widget;
if (text.getText().length() == 0) {
getButton(IDialogConstants.OK_ID).setEnabled(false);
}
if (text.getText().length() >= 1){
getButton(IDialogConstants.OK_ID).setEnabled(true);
}
}
});
}
I know that it doesn´t work because there are no dependencies between the two buttons.
How can i combine it?
I want to set the ok-button false while both modifylistener detect a char.
If i delete all chars in one testfield the button must be deactivated again.
Thank u.
You can use the same Listener for both Text fields and add it for SWT.KeyUp:
public static void main(String[] args)
{
final Display display = new Display();
Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new FillLayout(SWT.VERTICAL));
final Text first = new Text(shell, SWT.BORDER);
final Text second = new Text(shell, SWT.BORDER);
final Button button = new Button(shell, SWT.PUSH);
button.setText("disabled");
button.setEnabled(false);
Listener listener = new Listener()
{
#Override
public void handleEvent(Event e)
{
String firstString = first.getText();
String secondString = second.getText();
button.setEnabled(!isEmpty(firstString) && !isEmpty(secondString));
button.setText(button.isEnabled() ? "enabled" : "disabled");
}
};
first.addListener(SWT.KeyUp, listener);
second.addListener(SWT.KeyUp, listener);
shell.pack();
shell.setSize(300, shell.getSize().y);
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
private static boolean isEmpty(String input)
{
if(input == null)
return true;
else
return input.trim().isEmpty();
}
Looks like this:
The code will basically (on each key stroke) check if both Texts are empty. If so, disable the Button, else enable it.

How to always show vertical scroll bar in SWT table?

Is it possible to always show the vertical scroll bar in a SWT table even if the table is empty? By always showing a (possible disabled) vertical scroll bar one can avoid that the last column get partially hidden when the columns use ColumnWeightData for layouting.
I tried to initialize the table with SWT.V_SCROLL or to use table.getVerticalBar().setVisible(true) - both without success.
There is a method setAlwaysShowScrollBars in ScrollableComposite. What I am looking for is a similar method in Table.
UPDATE: I suppose that the scroll bars which are visible when the table contains enough data are not those scroll bars which Table inherits from Scrollable. I have debugged ScrollBar.setVisible(boolean) and it seems not be called on table layout updates. Is this observation correct?
UPDATE 2: Here is a snippet for a table construction. It would be great to have the vertical scrollbar visible even if the table is empty and to have the column headers visible even if the table data are scrolled down. Note: The snippet has left out some details as the label provider and some other controls arranged at the same parent composite.
protected void createMasterPart(final IManagedForm managedForm, Composite parentComposite)
{
FormToolkit toolkit = managedForm.getToolkit();
Composite contentComposite = toolkit.createComposite(parentComposite, SWT.NONE);
contentComposite.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1));
toolkit.paintBordersFor(contentComposite);
contentComposite.setLayout(new GridLayout(2, false));
GridData gd;
Composite tableComposite = new Composite(contentComposite, SWT.NONE);
TableColumnLayout tableColumnLayout = new TableColumnLayout();
tableComposite.setLayout(tableColumnLayout);
gd = new GridData(SWT.FILL, SWT.FILL, true, false, 1, 3);
tableComposite.setLayoutData(gd);
speakerTableViewer = new TableViewer(tableComposite, SWT.BORDER | SWT.FULL_SELECTION);
speakerTableViewer.setContentProvider(ArrayContentProvider.getInstance());
Table speakerTable = speakerTableViewer.getTable();
speakerTable.setHeaderVisible(true);
speakerTable.setLinesVisible(true);
toolkit.paintBordersFor(speakerTable);
TableViewerColumn tableViewerAudiosampleColumn = new TableViewerColumn(speakerTableViewer, SWT.NONE);
TableColumn audiosampleColumn = tableViewerAudiosampleColumn.getColumn();
tableColumnLayout.setColumnData(audiosampleColumn, new ColumnWeightData(60, true));
audiosampleColumn.setText("Sample");
TableViewerColumn tableViewerSpeakerColumn = new TableViewerColumn(speakerTableViewer, SWT.NONE);
TableColumn speakerColumn = tableViewerSpeakerColumn.getColumn();
tableColumnLayout.setColumnData(speakerColumn, new ColumnWeightData(60, true));
speakerColumn.setText("Speaker");
TableViewerColumn tableViewerRemarkColumn = new TableViewerColumn(speakerTableViewer, SWT.NONE);
TableColumn remarkColumn = tableViewerRemarkColumn.getColumn();
tableColumnLayout.setColumnData(remarkColumn, new ColumnWeightData(120, true));
remarkColumn.setText("Remark");
}
It's not possible to force the Table to always show scroll bars, the OS decides when to show them.
Alternatives
Right, I came up with a solution very similar to my answer to this question:
Is it possible to get the vertical/horizontal scroll bar visible when the SWT List is in disabled state?
The idea is to use a ScrolledComposite (as the other answer already suggested) to take care of the scrolling. The Table itself won't scroll. However, this won't make any difference, because the user won't be able to tell the difference.
ScrolledComposite has a method called setAlwaysShowScrollBars(boolean) with which you can force it to always show the scroll bars, even if they aren't required.
Here is some sample code, that will illustrate what I just talked about:
public static void main(String[] args)
{
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout());
final ScrolledComposite composite = new ScrolledComposite(shell, SWT.V_SCROLL);
composite.setLayout(new GridLayout());
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final Table table = new Table(composite, SWT.NO_SCROLL | SWT.FULL_SELECTION);
table.setHeaderVisible(true);
composite.setContent(table);
composite.setExpandHorizontal(true);
composite.setExpandVertical(true);
composite.setAlwaysShowScrollBars(true);
composite.setMinSize(table.computeSize(SWT.DEFAULT, SWT.DEFAULT));
Button fillTable = new Button(shell, SWT.PUSH);
fillTable.setText("Fill table");
fillTable.setLayoutData(new GridData(SWT.FILL, SWT.END, true, false));
fillTable.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event arg0)
{
if (table.getColumnCount() < 1)
{
for (int col = 0; col < 4; col++)
{
TableColumn column = new TableColumn(table, SWT.NONE);
column.setText("Column " + col);
}
}
for (int row = 0; row < 20; row++)
{
TableItem item = new TableItem(table, SWT.NONE);
for (int col = 0; col < table.getColumnCount(); col++)
{
item.setText(col, "Item " + row + " " + col);
}
}
for (int col = 0; col < table.getColumnCount(); col++)
{
table.getColumn(col).pack();
}
composite.setMinSize(table.computeSize(SWT.DEFAULT, SWT.DEFAULT));
}
});
Button clearTable = new Button(shell, SWT.PUSH);
clearTable.setText("Clear table");
clearTable.setLayoutData(new GridData(SWT.FILL, SWT.END, true, false));
clearTable.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event arg0)
{
table.removeAll();
composite.setMinSize(table.computeSize(SWT.DEFAULT, SWT.DEFAULT));
}
});
shell.pack();
shell.setSize(400, 300);
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
Looks like this:
As you can see, the scroll bar is always visible.
UPDATE
As pointed out in the comment, this approach will not keep the Table headers visible when you scroll down. If you could post a small working code example that illustrates your problem, we might come up with an alternative (unrelated to forcing the scroll bars).
UPDATE2
Here is some code that should do what you want, the trick is to trigger a resize event on the parent of the TableViewer, the horizontal scrollbar that is shown isn't really necessary and it disappears after you resize the window:
public static void main(String[] args)
{
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setText("StackOverflow");
shell.setLayout(new GridLayout());
createMasterPart(shell);
shell.pack();
shell.setSize(400, 300);
shell.open();
shell.layout(true, true);
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
private static void createMasterPart(Composite parentComposite)
{
Composite composite = new Composite(parentComposite, SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
composite.setLayout(new GridLayout(1, false));
Composite tableComposite = new Composite(composite, SWT.NONE);
TableColumnLayout tableColumnLayout = new TableColumnLayout();
tableComposite.setLayout(tableColumnLayout);
tableComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
TableViewer tableViewer = new TableViewer(tableComposite, SWT.BORDER | SWT.FULL_SELECTION);
tableViewer.setContentProvider(ArrayContentProvider.getInstance());
Table table = tableViewer.getTable();
table.setHeaderVisible(true);
table.setLinesVisible(true);
TableViewerColumn firstTableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE);
TableColumn firstTableColumn = firstTableViewerColumn.getColumn();
firstTableColumn.setText("Sample");
firstTableViewerColumn.setLabelProvider(new ColumnLabelProvider()
{
#Override
public String getText(Object element)
{
Dummy p = (Dummy) element;
return p.first;
}
});
TableViewerColumn secondTableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE);
TableColumn secondTableColumn = secondTableViewerColumn.getColumn();
secondTableColumn.setText("Speaker");
secondTableViewerColumn.setLabelProvider(new ColumnLabelProvider()
{
#Override
public String getText(Object element)
{
Dummy p = (Dummy) element;
return p.second;
}
});
TableViewerColumn thirdTableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE);
TableColumn thirdTableColumn = thirdTableViewerColumn.getColumn();
thirdTableColumn.setText("Remark");
thirdTableViewerColumn.setLabelProvider(new ColumnLabelProvider()
{
#Override
public String getText(Object element)
{
Dummy p = (Dummy) element;
return p.third;
}
});
List<Dummy> elements = new ArrayList<>();
for(int i = 0; i < 20; i++)
{
elements.add(new Dummy("firstfirstfirst " + i, "secondsecondsecond " + i, "thirdthirdthirdthirdthirdthird " + i));
}
tableViewer.setInput(elements);
tableColumnLayout.setColumnData(firstTableColumn, new ColumnWeightData(1, true));
tableColumnLayout.setColumnData(secondTableColumn, new ColumnWeightData(1, true));
tableColumnLayout.setColumnData(thirdTableColumn, new ColumnWeightData(2, true));
}
private static class Dummy
{
public String first;
public String second;
public String third;
public Dummy(String first, String second, String third)
{
this.first = first;
this.second = second;
this.third = third;
}
}
I have created a solution that I think is better than put your table inside a ScrolledComposite.
My solution: fill my table with empty items until my scroll bar is visible.
Example:
// Flag that knows if the empty item was added or not
boolean addedEmptyItem = false;
// Get the table client area
Rectangle rect = table.getClientArea ();
// Get the item height
int itemHeight = table.getItemHeight ();
// Get the header height
int headerHeight = table.getHeaderHeight ();
// Calculate how many items can be visible without scrolling
int visibleCount = (rect.height - headerHeight + itemHeight - 1) / itemHeight;
while ( visibleCount > table.getItemCount() ) {
// Add an empty item
new TableItem( table, SWT.NONE );
// Set the flag
addedEmptyItem = true;
}
// Vertical bar é disabled if an empty item was added
table.getVerticalBar().setEnabled( !addedEmptyItem );
I hope this solution helps someone.
Thanks.
I don't think you can do this but you can try call ScrolledComposite.setAlwaysShowScrollbars() to true, but you will see both of the enabled scrollbars all the time.

How to change the foreground color of specific items in a List?

When I press a button, I want to change the foreground color of the selected item in a List.
So far, I tried this:
list.setForeground(display.getSystemColor(SWT.COLOR_RED));
but it changes the foreground color of all the items, not just the selected one.
Any ideas how to solve this?
Doing this with a List would require custom drawing. You are better off using a Table instead (or even a TableViewer depending on your requirements). Here is an example of a table that does what you want:
public static void main(String[] args)
{
final Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout(1, false));
shell.setText("StackOverflow");
final Table table = new Table(shell, SWT.BORDER | SWT.MULTI);
table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
for (int i = 0; i < 10; i++)
{
TableItem item = new TableItem(table, SWT.NONE);
item.setText("item " + i);
}
Button button = new Button(shell, SWT.PUSH);
button.setText("Color selected");
button.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event arg0)
{
List<TableItem> allItems = new ArrayList<>(Arrays.asList(table.getItems()));
TableItem[] selItems = table.getSelection();
for (TableItem item : selItems)
{
item.setForeground(display.getSystemColor(SWT.COLOR_RED));
allItems.remove(item);
}
for (TableItem item : allItems)
{
item.setForeground(display.getSystemColor(SWT.COLOR_LIST_FOREGROUND));
}
}
});
shell.pack();
shell.open();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
Before button press:
After button press:
Just a note: This is not the most efficient way to do it, but should give you the basic idea.
List does not supports what you want.
Use Table and Table items instead.
Each table item represent a row, and it has setForeground(Color) method.

Categories

Resources