Reputation: 372
I have a rendering issue using a JXTable (swingx-all:1.6.5-1) being filtered by a TextComponentMatcherEditor (glazedlists:1.11.0) from Glazedlists under openjdk 17.
Entering "es 11" in the JTextfield produces the following rendering issue:
Any hints on what I'm doing wrong will be appreciated.
The following code can be used to reproduce the problem:
import static javax.swing.SwingUtilities.*;
import java.util.Comparator;
import java.util.stream.IntStream;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import org.jdesktop.swingx.JXFrame;
import org.jdesktop.swingx.JXTable;
import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.gui.TableFormat;
import ca.odell.glazedlists.swing.AdvancedTableModel;
import ca.odell.glazedlists.swing.GlazedListsSwing;
import ca.odell.glazedlists.swing.TextComponentMatcherEditor;
import net.miginfocom.swing.MigLayout;
public class GlazedListsJXTableFilterIssue extends JPanel {
public static void main(String[] args) {
invokeLater(() -> {
JXFrame frame = new JXFrame();
frame.add(new GlazedListsJXTableFilterIssue());
frame.pack();
frame.setVisible(true);
});
}
private JTextField filterField = new JTextField();
private JTable itemsTable = new JXTable();
// private JTable itemsTable = new JTable();
GlazedListsJXTableFilterIssue() {
initComponents();
EventList<Item> itemEventList = new BasicEventList<>();
IntStream.range(0, 100).forEach(i -> itemEventList
.add(new Item("ES " + i, null, "December", null, null)));
SortedList<Item> sortedItems = new SortedList<>(itemEventList,
Comparator.comparing(Item::getName));
TextComponentMatcherEditor<Item> textComponentMatcherEditor = new TextComponentMatcherEditor<>(
filterField, GlazedLists.textFilterator("name"));
FilterList<Item> filterList = new FilterList<>(sortedItems,
textComponentMatcherEditor);
AdvancedTableModel<Item> tableModel = GlazedListsSwing
.eventTableModelWithThreadProxyList(filterList,
new ItemTableFormat());
// uncomment next 2 lines for stacktrace and no rendering issue
itemsTable.setAutoCreateRowSorter(false);
itemsTable.setRowSorter(null);
itemsTable.setModel(tableModel);
}
private void initComponents() {
filterField.setColumns(20);
setLayout(
new MigLayout("insets dialog", "[grow, fill]", "[grow, fill]"));
add(filterField, "cell 0 0, growx, growy 0");
add(new JScrollPane(itemsTable), "cell 0 1, growx, width 300");
}
public static class Item {
private String name;
private String desc;
private String month;
private Integer number;
private Boolean bool;
Item(String name, String desc, String month, Integer number,
Boolean bool) {
this.name = name;
this.setDesc(desc);
this.month = month;
this.setNumber(number);
this.setBool(bool);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMonth() {
return month;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public Boolean getBool() {
return bool;
}
public void setBool(Boolean bool) {
this.bool = bool;
}
}
private static class ItemTableFormat implements TableFormat<Item> {
private static String[] COLUMN_NAMES = { "Name", "Description", "Month",
"Integer", "Boolean", };
@Override
public int getColumnCount() {
return COLUMN_NAMES.length;
}
@Override
public String getColumnName(int column) {
return COLUMN_NAMES[column];
}
@Override
public Object getColumnValue(Item item, int column) {
switch (column) {
case 0:
return item.getName();
case 1:
return item.getDesc();
case 2:
return item.getMonth();
case 3:
return item.getNumber();
case 4:
return item.getBool();
}
throw new IllegalStateException();
}
}
}
Upvotes: -1
Views: 48
Reputation: 20914
For what it's worth, here is a pure Swing implementation. I concentrated only on the filtering aspect.
My IDE is Eclipse 2024-06 on Windows 11 using the JDK that comes with Eclipse which is Temurin 21.0.3
Below code uses record classes, instanceof
pattern matching and switch expressions.
(Needless to say, no rendering problems. :-)
Notes after the code.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
import javax.swing.text.Document;
public class TblFltrT implements DocumentListener, Runnable {
private JFrame frame;
private JTable itemsTable;
private TableRowSorter<ItemsTableModel> sorter;
private JTextField filterField;
@Override
public void changedUpdate(DocumentEvent e) {
// Never called.
}
@Override
public void insertUpdate(DocumentEvent e) {
updateFilter();
}
@Override
public void removeUpdate(DocumentEvent e) {
updateFilter();
}
public void run() {
createAndDisplayGui();
}
private void createAndDisplayGui() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createField(), BorderLayout.PAGE_START);
frame.add(createTable(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createField() {
JPanel panel = new JPanel();
filterField = new JTextField(20);
Document doc = filterField.getDocument();
doc.addDocumentListener(this);
panel.add(filterField);
return panel;
}
private JScrollPane createTable() {
ItemsTableModel model = new ItemsTableModel();
itemsTable = new JTable(model);
sorter = new TableRowSorter<>(model);
itemsTable.setRowSorter(sorter);
JScrollPane scrollPane = new JScrollPane(itemsTable);
return scrollPane;
}
private void updateFilter() {
String text = filterField.getText();
NameRowFilter filter = new NameRowFilter(text);
sorter.setRowFilter(filter);
}
public static void main(String[] args) {
EventQueue.invokeLater(new TblFltrT());
}
}
record Item(String name,
String desc,
String month,
Integer number,
Boolean bool) {
public boolean equals(Object obj) {
boolean equal = this == obj;
if (!equal) {
if (obj instanceof Item i) {
if (name == null) {
equal = i.name() == null;
}
else {
equal = name.equals(i.name());
}
}
}
return equal;
}
}
@SuppressWarnings("serial")
class ItemsTableModel extends AbstractTableModel {
private static final String[] COLUMN_NAMES = {"Name",
"Description",
"Month",
"Integer",
"Boolean"};
private List<Item> itemEventList;
public ItemsTableModel() {
itemEventList = new ArrayList<>();
IntStream.range(0, 100)
.forEach(i -> itemEventList.add(new Item("ES " + i,
null,
"December",
null,
null)));
}
@Override
public int getColumnCount() {
return 5;
}
public String getColumnName(int column) {
return COLUMN_NAMES[column];
}
@Override
public int getRowCount() {
return itemEventList == null ? 0 : itemEventList.size();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
Item item = itemEventList.get(rowIndex);
return switch (columnIndex) {
case 0 -> item.name();
case 1 -> item.desc();
case 2 -> item.month();
case 3 -> item.number();
case 4 -> item.bool();
default -> null;
};
}
}
class NameRowFilter extends RowFilter<ItemsTableModel, Integer> {
private String name;
public NameRowFilter(String name) {
this.name = name;
}
@Override
public boolean include(Entry<? extends ItemsTableModel, ? extends Integer> entry) {
Object val = entry.getValue(0);
String str = String.valueOf(val);
return str.startsWith(name);
}
}
Whenever the text in filterField
is changed, a new RowFilter
is created and assigned to itemsTable
. The filter causes the table to display only those rows where the Name column starts with the text in filterField
.
If you fail to overcome your problems using Glazed Lists and SwingX, I hope the above can help you to develop a pure Swing solution.
Upvotes: 0