In my experience CONSTRAINED_RESIZE_POLICY is broken if the TableView contains some fixed width columns unless I am doing something wrong here:
public class JavaFX2Sandbox extends Application
{
public static void main(String[] args)
{
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception
{
StackPane root = new StackPane();
root.autosize();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
configureTable(root);
}
public static class Person
{
public Person(String firstName, String lastName)
{
this.fixedValue = "Dr.";
this.firstName = firstName;
this.lastName = lastName;
}
public String fixedValue;
public String firstName;
public String lastName;
}
private static class CellValueFactory<T> implements Callback<TableColumn.CellDataFeatures<T, Object>, ObservableValue<Object>>
{
private String mFieldName;
public CellValueFactory(String fieldName)
{
mFieldName = fieldName;
}
#Override
public ObservableValue call(TableColumn.CellDataFeatures<T, Object> p)
{
try
{
if (p == null)
{
return new SimpleObjectProperty(null);
}
Object o = p.getValue();
if (o == null)
{
return new SimpleObjectProperty(null);
}
Object fieldVal = o.getClass().getField(mFieldName).get(o);
if (fieldVal == null)
{
return new SimpleObjectProperty(null);
}
return new SimpleObjectProperty(fieldVal);
}
catch (Exception ex)
{
return new SimpleObjectProperty(ex);
}
}
}
private void configureTable(StackPane root)
{
TableView<Person> table = new TableView<>();
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
ArrayList<Person> teamMembers = new ArrayList<>();
teamMembers.add(new Person("John", "Big"));
teamMembers.add(new Person("Frank", "Small"));
TableColumn col0 = new TableColumn("fixed-width");
col0.setResizable(false);
col0.setCellValueFactory(new CellValueFactory("fixedValue"));
TableColumn col1 = new TableColumn("First Name");
col1.setCellValueFactory(new CellValueFactory("firstName"));
TableColumn col2 = new TableColumn("Last Name");
col2.setCellValueFactory(new CellValueFactory("lastName"));
table.getColumns().setAll(col0, col1, col2);
table.setItems(FXCollections.observableList(teamMembers));
root.getChildren().add(table);
}
}
So I started to implement my own resize policy which behaves slightly differently from CONSTRAINED_RESIZE_POLICY but I like mine better :-). It is all okay except for the sum of the widths of the columns do not add up to the width of the TableView.
Here is the implementation of my resize policy class:
static class ColumnResizePolicy implements Callback<TableView.ResizeFeatures, Boolean>
{
double mTVWidth;
#Override
public Boolean call(ResizeFeatures arg0)
{
TableView tv = arg0.getTable();
Double tvWidth = tv.widthProperty().getValue();
if (tvWidth == null || tvWidth <= 0.0)
{
return false;
}
if (mTVWidth != tvWidth && arg0.getColumn() == null)
{
mTVWidth = tvWidth;
int numColsToSize = 0;
double fixedColumnsWidths = 0;
for (TableColumn col : new ArrayList<TableColumn>(tv.getColumns()))
{
if (col.isResizable() && col.isVisible())
{
++numColsToSize;
}
else if (col.isVisible())
{
fixedColumnsWidths += col.getWidth();
}
}
if (numColsToSize == 0)
return TableView.UNCONSTRAINED_RESIZE_POLICY.call(arg0);
TableColumn lastCol = null;
for (TableColumn col : new ArrayList<TableColumn>(tv.getColumns()))
{
if (col.isResizable() && col.isVisible())
{
double newWidth = (tvWidth - fixedColumnsWidths) / numColsToSize;
col.setPrefWidth(newWidth);
lastCol = col;
}
}
if (lastCol != null)
{
lastCol.setPrefWidth(lastCol.getPrefWidth()-2);
}
return true;
}
else
{
return TableView.UNCONSTRAINED_RESIZE_POLICY.call(arg0);
}
}
}
And my question (ironically after this long post) is about number 2 in this line:
lastCol.setPrefWidth(lastCol.getPrefWidth()-2);
I assume that the table width gives back the outer width including the border, hence the difference, but how do I get the inner width?
Try next:
double borderWidth = table.getBoundsInLocal().getWidth() - table.getWidth()
Have you tried getting the width of the place holder node?
I have a problem same https://community.oracle.com/message/12334734#12334734
And this implementing custom column resize policy of you was help me.
Thanks you!
Your code.
if (col.isResizable() && col.isVisible()) {
double newWidth = (tvWidth - fixedColumnsWidths) / numColsToSize;
col.setPrefWidth(newWidth);
lastCol = col;
}
I change to
if (col.isResizable() && col.isVisible()) {
double newWidth = (tvWidth - fixedColumnsWidths)*col.getPercentWidth();
col.setPrefWidth(newWidth);
lastCol = col;
}
With col is
public class PTableColumn<S, T> extends javafx.scene.control.TableColumn<S, T>
And getPercentWidth is
private DoubleProperty percentWidth = new SimpleDoubleProperty(1);
Related
I am trying to program a basic Nine Men's Morris game in Java for school.
The GUI consists of 24 JButtons. The first Button, that is clicked, is supposed to set the starting position of a move, the second button the destination. I tried to create a bool and set it to true after a button was clicked once, and check, whether that boolean is true or false to determine, whether the Button should set the start or the destination.
However, when I tried it like that and printed Start and Dest to the console, they both were set to 0,1,0. I am fairly new to Java, so there might be quite some bad practice going on right there.
JButton button010 = new JButton("");
button010.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(!targ) {
setStart(0,1,0);
targ = true;
} else {
setDest(0,1,0);
targ = false;
}
}
});
button010.setOpaque(false);
button010.setContentAreaFilled(false);
button010.setBorderPainted(false);
button010.setBounds(222, 11, 47, 44);
contentPane.add(button010);
This is not an answer to you question, only a suggestion for a better design to remove the hardcoding in your listener code.
Why do you call your Boolean variable "targ". Since you have "start" and "dest" methods why are you creating a third variable name? I would suggest a better name is "start" so you know it is related to your start/dest methods. It would default to true, since that is the first value you are trying to set when you click a button.
So your custom Action might be something like:
class GameAction action = new Action()
{
private int value1;
private int value2;
private int value3;
public GameAction(int value1, int value2, int value3)
{
this.value1 = value1;
this.value2 = value2;
this.value3 = value3;
}
#Override
public void actionPerformed(ActionEvent e)
{
if (start)
{
setStart(value1, value2, value3);
start = false;
}
else
{
setDest(value1, value2, value3);
start = true;
}
}
You would use more appropriate variable names.
Now all your logic is in a single class and if you ever need to change the logic it is contained in one place instead of 24 custom listeners.
Then in your code when you create the button you would do:
JButton button010 = new JButton("");
button010.addActionListener( new GameAction(0, 1, 0) );
Of course it would be even better to create your 24 buttons in a loop so you don't have to hardcode the parameters.
Not a real or full answer since your question is currently incomplete and cannot be answered as currently stated, but I'm testing this code out on how to create a 9 Man's Morris Grid using Swing, using layout managers, and with separation of the model from the GUI. I have created a 7 x 7 grid of JLabel and added it to a JPanel that uses GridLayout(7, 7). If the grid position matches a cell position in the 9 man's morris, then a grid cell is created, with mouse listener and all.
The following code creates this GUI:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.*;
#SuppressWarnings("serial")
public class MorrisPanel extends JPanel {
private static final int ICON_WIDTH = 80;
private static final int SMALL_OVAL = 10;
private static final int LARGE_OVAL = 40;
private static final int GRID_SIDES = 7;
private MorrisGameGrid game = new MorrisGameGrid();
private Map<MorrisCell, JLabel> cellLabelMap = new HashMap<>();
private Map<JLabel, MorrisCell> labelCellMap = new HashMap<>();
private Icon blankIcon;
private Icon cellIcon;
private Icon whiteIcon;
private Icon blackIcon;
public MorrisPanel() {
blankIcon = createIcon(null, 0);
cellIcon = createIcon(Color.DARK_GRAY, SMALL_OVAL);
whiteIcon = createIcon(Color.WHITE, LARGE_OVAL);
blackIcon = createIcon(Color.DARK_GRAY, LARGE_OVAL);
setLayout(new GridLayout(GRID_SIDES, GRID_SIDES));
MyMouse myMouse = new MyMouse();
List<MorrisColumn> columns = Arrays.asList(MorrisColumn.values());
Collections.reverse(columns);
for (MorrisColumn column : columns) {
for (MorrisRow row : MorrisRow.values()) {
MorrisCell cell = new MorrisCell(column, row);
JLabel label = new JLabel(blankIcon);
if (game.contains(cell)) {
label.setIcon(cellIcon);
label.addMouseListener(myMouse);
cellLabelMap.put(cell, label);
labelCellMap.put(label, cell);
}
add(label);
}
}
}
private Icon createIcon(Color color, int size) {
int w = ICON_WIDTH;
int h = ICON_WIDTH;
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
if (color != null) {
Graphics2D g2 = img.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(color);
int x = (ICON_WIDTH - size) / 2;
int y = x;
int width = size;
int height = size;
g2.fillOval(x, y, width, height);
g2.setColor(Color.BLACK);
g2.drawOval(x, y, width, height);
g2.dispose();
}
return new ImageIcon(img);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (MorrisEdge edge : game.getEdges()) {
JLabel l1 = cellLabelMap.get(edge.getCell1());
JLabel l2 = cellLabelMap.get(edge.getCell2());
int x1 = l1.getLocation().x + l1.getWidth() / 2;
int x2 = l2.getLocation().x + l2.getWidth() / 2;
int y1 = l1.getLocation().y + l1.getHeight() / 2;
int y2 = l2.getLocation().y + l2.getHeight() / 2;
g2.drawLine(x1, y1, x2, y2);
}
}
private class MyMouse extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
JLabel source = (JLabel) e.getSource();
MorrisCell cell = labelCellMap.get(source);
// TODO: Begin testing block -- delete this
System.out.println(cell); // !! just for testing purposes
Icon icon = source.getIcon();
if (icon == cellIcon) {
source.setIcon(blackIcon);
} else if (icon == blackIcon) {
source.setIcon(whiteIcon);
} else if (icon == whiteIcon) {
source.setIcon(cellIcon);
}
// TODO: end testing block
// TODO: finish
}
}
private static void createAndShowGui() {
MorrisPanel mainPanel = new MorrisPanel();
JFrame frame = new JFrame("TestButtons");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MorrisGame {
private MorrisGameGrid grid = new MorrisGameGrid();
// TODO: logic for game goes here
public MorrisGameGrid getGrid() {
return grid;
}
}
class MorrisGameGrid {
Set<MorrisCell> cells = new HashSet<>();
Set<MorrisEdge> edges = new HashSet<>();
public MorrisGameGrid() {
cells.add(new MorrisCell("a1"));
cells.add(new MorrisCell("d1"));
cells.add(new MorrisCell("g1"));
cells.add(new MorrisCell("b2"));
cells.add(new MorrisCell("d2"));
cells.add(new MorrisCell("f2"));
cells.add(new MorrisCell("c3"));
cells.add(new MorrisCell("d3"));
cells.add(new MorrisCell("e3"));
cells.add(new MorrisCell("a4"));
cells.add(new MorrisCell("b4"));
cells.add(new MorrisCell("c4"));
cells.add(new MorrisCell("e4"));
cells.add(new MorrisCell("f4"));
cells.add(new MorrisCell("g4"));
cells.add(new MorrisCell("c5"));
cells.add(new MorrisCell("d5"));
cells.add(new MorrisCell("e5"));
cells.add(new MorrisCell("b6"));
cells.add(new MorrisCell("d6"));
cells.add(new MorrisCell("f6"));
cells.add(new MorrisCell("a7"));
cells.add(new MorrisCell("d7"));
cells.add(new MorrisCell("g7"));
connectLoop(getCell("a1"), getCell("d1"), getCell("g1"),
getCell("g4"), getCell("g7"), getCell("d7"),
getCell("a7"), getCell("a4"));
connectLoop(getCell("b2"), getCell("d2"), getCell("f2"),
getCell("f4"), getCell("f6"), getCell("d6"),
getCell("b6"), getCell("b4"));
connectLoop(getCell("c3"), getCell("d3"), getCell("e3"),
getCell("e4"), getCell("e5"), getCell("d5"),
getCell("c5"), getCell("c4"));
connectEdge(getCell("d1"), getCell("d2"), getCell("d3"));
connectEdge(getCell("d7"), getCell("d6"), getCell("d5"));
connectEdge(getCell("a4"), getCell("b4"), getCell("c4"));
connectEdge(getCell("e4"), getCell("f4"), getCell("g4"));
}
public boolean contains(MorrisCell cell) {
return cells.contains(cell);
}
public Set<MorrisCell> getCells() {
return cells;
}
public Set<MorrisEdge> getEdges() {
return edges;
}
private void connectCells(MorrisCell cell1, MorrisCell cell2) {
cell1.addNeighbors(cell2);
cell2.addNeighbors(cell1);
edges.add(new MorrisEdge(cell1, cell2));
}
private void connectEdge(MorrisCell... cells) {
for (int i = 0; i < cells.length - 1; i++) {
connectCells(cells[i], cells[i + 1]);
}
}
private void connectLoop(MorrisCell... cells) {
connectEdge(cells);
connectCells(cells[0], cells[cells.length - 1]);
}
public MorrisCell getCell(String text) {
if (text == null || text.length() != 2) {
String errorTxt = "For text: " + text;
throw new IllegalArgumentException(errorTxt);
}
MorrisCell temp = new MorrisCell(text);
for (MorrisCell morrisCell : cells) {
if (morrisCell.equals(temp)) {
return morrisCell;
}
}
return null;
}
}
class MorrisEdge {
private MorrisCell cell1;
private MorrisCell cell2;
public MorrisEdge(MorrisCell cell1, MorrisCell cell2) {
if (cell1.compareTo(cell2) < 0) {
this.cell1 = cell1;
this.cell2 = cell2;
} else {
this.cell2 = cell1;
this.cell1 = cell2;
}
}
public MorrisCell getCell1() {
return cell1;
}
public MorrisCell getCell2() {
return cell2;
}
#Override
public int hashCode() {
int result = ((cell1 == null) ? 0 : cell1.hashCode());
result += ((cell2 == null) ? 0 : cell2.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MorrisEdge other = (MorrisEdge) obj;
boolean e1 = compareCells(cell1, other.cell1);
e1 &= compareCells(cell2, other.cell2);
boolean e2 = compareCells(cell1, other.cell2);
e2 &= compareCells(cell2, other.cell1);
return e1 || e2;
}
private static boolean compareCells(MorrisCell c1, MorrisCell c2) {
if (c1 == null) {
if (c2 != null)
return false;
} else if (!c1.equals(c2))
return false;
return true;
}
#Override
public String toString() {
return "Edge [" + cell1 + ", " + cell2 + "]";
}
}
class MorrisCell implements Comparable<MorrisCell> {
private MorrisColumn column;
private MorrisRow row;
private Set<MorrisCell> neighbors = new HashSet<>();
private MorrisPlayer player = null;
public MorrisCell(MorrisColumn column, MorrisRow row) {
this.column = column;
this.row = row;
}
public MorrisCell(String text) {
if (text.length() != 2) {
String errorTxt = "For text: " + text;
throw new IllegalArgumentException(errorTxt);
}
String columnTxt = text.substring(0, 1).toLowerCase();
String rowTxt = text.substring(1, 2);
MorrisColumn column1 = null;
MorrisRow row1 = null;
for (MorrisColumn c : MorrisColumn.values()) {
if (c.getText().equals(columnTxt)) {
column1 = c;
break;
}
}
for (MorrisRow r : MorrisRow.values()) {
if (r.getText().equals(rowTxt)) {
row1 = r;
break;
}
}
if (column1 == null || row1 == null) {
String errorTxt = "For text: " + text;
throw new IllegalArgumentException(errorTxt);
}
this.column = column1;
this.row = row1;
}
public void addNeighbors(MorrisCell neighbor) {
neighbors.add(neighbor);
}
public boolean isNeighbor(MorrisCell possibleNeighbor) {
return neighbors.contains(possibleNeighbor);
}
public MorrisRow getRow() {
return row;
}
public MorrisColumn getColumn() {
return column;
}
public void setPlayer(MorrisPlayer player) {
this.player = player;
}
public MorrisPlayer getPlayer() {
return player;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((column == null) ? 0 : column.hashCode());
result = prime * result + ((row == null) ? 0 : row.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MorrisCell other = (MorrisCell) obj;
if (column != other.column)
return false;
if (row != other.row)
return false;
return true;
}
#Override
public String toString() {
return column.getText() + row.getText();
}
#Override
public int compareTo(MorrisCell o) {
int colCompare = column.compareTo(o.column);
int rowCompare = row.compareTo(o.row);
return colCompare != 0 ? colCompare : rowCompare;
}
}
enum MorrisRow {
ONE("1"), TWO("2"), THREE("3"), FOUR("4"), FIVE("5"), SIX("6"), SEVEN("7");
private String text;
private MorrisRow(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
enum MorrisColumn {
A("a"), B("b"), C("c"), D("d"), E("e"), F("f"), G("g");
private String text;
private MorrisColumn(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
enum MorrisPlayer {
WHITE("White"), BLACK("Black");
private String text;
private MorrisPlayer(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
This question already has an answer here:
Javafx TableView not showing data
(1 answer)
Closed 4 years ago.
Purpose: To browse for a folder and select it. And to add its properties in columns.
For example, the name of the folder goes in the name column, the location goes in the location, and etc.
For some reason, I am not able to add the properties of selected folder into the table named folderTable. The program seem to know that something is in the first row after I add a folder but it doesn't display the folder itself.
I am learning, so any help is appreciated.
public class mainClass2 extends Application {
Stage window;
TableView<syncedFolders> folderTable;
Button file, edit, view, addFolder, printInfo, close, deleteFolder;
private String name, location, dateModified;
private long size;
private double printSize = 0, bytes = 0, kilobytes = 0, megabytes = 0, gigabytes = 0, tempSize = 0;
private String printSizeAb = "";
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
HBox topMenu = new HBox();
file = new Button("File");
edit = new Button("Edit");
view = new Button("View");
topMenu.getChildren().addAll(file, edit, view);
VBox leftMenu = new VBox();
printInfo = new Button("Print folder info");
printInfo.setOnAction(e -> {
round(printSize, 1);
System.out.println("Name: " + name);
System.out.println("Location: " + location);
System.out.println("Last Modified: " + dateModified);
System.out.println("Size: " + tempSize + printSizeAb);
});
leftMenu.getChildren().add(printInfo);
HBox botBox = new HBox();
addFolder = new Button("Add Folder");
deleteFolder = new Button("Delete Folder");
close = new Button("Exit");
addFolder.setOnAction(e -> {
DirectoryChooser chooser = new DirectoryChooser();
chooser.setTitle("JavaFX Projects");
File defaultDirectory = new File("D:\\");
chooser.setInitialDirectory(defaultDirectory);
File selectedDirectory = chooser.showDialog(window);
name = selectedDirectory.getName();
location = selectedDirectory.toString();
size = getFolderSize(selectedDirectory);
bytes = size;
kilobytes = (bytes / 1024);
megabytes = (kilobytes / 1024);
gigabytes = (megabytes / 1024);
if (bytes < 1024) {
printSize = kilobytes;
printSizeAb = " KB";
} else if (bytes >= 1024 && bytes < Math.pow(1024, 3)) {
printSize = megabytes;
printSizeAb = " MB";
} else // if (bytes >= Math.pow(1024, 2) && bytes <= Math.pow(1024, 3))
{
printSize = gigabytes;
printSizeAb = " GB";
}
addFolder();
lasModifiedDate();
});
// Name column
TableColumn<syncedFolders, String> nameCol = new TableColumn<>("Name");
nameCol.setMinWidth(200);
nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));
// location column
TableColumn<syncedFolders, String> locationCol = new TableColumn<>("Location");
locationCol.setMinWidth(200);
locationCol.setCellValueFactory(new PropertyValueFactory<>("location"));
// date modified column
TableColumn<syncedFolders, String> dateModifiedCol = new TableColumn<>("Last Modified");
dateModifiedCol.setMinWidth(200);
dateModifiedCol.setCellValueFactory(new PropertyValueFactory<>("dateModified"));
// size column
TableColumn<syncedFolders, Double> sizeCol = new TableColumn<>("Size");
sizeCol.setMinWidth(200);
sizeCol.setCellValueFactory(new PropertyValueFactory<>("size"));
folderTable = new TableView<>();
folderTable.setItems(getSyncedFolders());
folderTable.getColumns().addAll(nameCol, locationCol, dateModifiedCol, sizeCol);
close.setOnAction(e -> closeProgram());
botBox.setPadding(new Insets(10, 10, 10, 10));
botBox.setSpacing(10);
botBox.getChildren().addAll(addFolder, deleteFolder, close);
BorderPane borderPane = new BorderPane();
borderPane.setTop(topMenu);
borderPane.setLeft(leftMenu);
borderPane.setCenter(folderTable);
borderPane.setBottom(botBox);
Scene scene = new Scene(borderPane, 800, 600);
window.setScene(scene);
window.setTitle("the title");
window.show();
window.setOnCloseRequest(e -> {
e.consume();
closeProgram();
});
}
// Get all of the products
public ObservableList<syncedFolders> getSyncedFolders() {
ObservableList<syncedFolders> folders = FXCollections.observableArrayList();
folders.add(new syncedFolders("Folder", "D://", "July", 3.4));
folders.add(new syncedFolders(name, location, dateModified, tempSize));
return folders;
}
public void addFolder() {
round(printSize, 1);
folderTable.setItems(getSyncedFolders());
folderTable.getItems().add(new syncedFolders(name, location, dateModified, tempSize));
}
private double round(double value, int precision) {
int scale = (int) Math.pow(10, precision);
tempSize = (double) Math.round(value * scale) / scale;
return tempSize;
}
public String lasModifiedDate() {
Path path = Paths.get(location);
DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy - hh:mm:ss");
FileTime fileTime = null;
try {
fileTime = Files.getLastModifiedTime(path);
} catch (IOException e1) {
System.out.println("Cannot get the last modified time");
}
dateModified = dateFormat.format(fileTime.toMillis());
return dateModified;
}
private long getFolderSize(File folder) {
long length = 0;
File[] files = folder.listFiles();
int count = files.length;
for (int i = 0; i < count; i++) {
if (files[i].isFile()) {
length += files[i].length();
} else {
length += getFolderSize(files[i]);
}
}
return length;
}
private void closeProgram() {
/**
* Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("Confirmation
* Dialog"); alert.setHeaderText("You are about to exit");
* alert.setContentText("Are you ok with this?");
*
* Optional<ButtonType> result = alert.showAndWait(); if (result.get() ==
* ButtonType.OK)
**/
window.close();
}
}
public class syncedFolders {
private String name, location, dateModified;
private double size;
public syncedFolders() {
this.name = "";
this.location = "";
this.dateModified = "";
this.size = 0;
}
public syncedFolders(String s) {
this.name = s;
}
public syncedFolders(String name, String location, String dateModified, double size) {
this.name = name;
this.location = location;
this.dateModified = dateModified;
this.size = size;
}
public String getname() {
return name;
}
public void setname(String s) {
name = s;
}
public String getlocation() {
return location;
}
public void setlocation(String s) {
location = s;
}
public String getdateModified() {
return dateModified;
}
public void setdateModified(String s) {
dateModified = s;
}
public double getsize() {
return size;
}
public void setsize(double d) {
size = d;
}
}
The TableView uses Properties and Bindings to display the data in each column. You have a CellValueFactory installed on the columns, but you're populating it usinga PropertyValueFactory.
That is the right way to do it, but there's a problem with your syncedFolders class. The PropertyValueFactory looks into your syncedFolders class and tries to find a Property with the name you specified. The problem is that you do not have any properties defined:
private String name, location, dateModified;
private double size;
These are fields, not properties.
You will need to rewrite your syncedFolders class to have observable properties instead, like this:
private SimpleStringProperty name = new SimpleStringProperty();
Do that for each of your fields, update your constructor and setters/getters and try again!
public class syncedFolders {
private SimpleStringProperty name = new SimpleStringProperty();
private SimpleStringProperty location = new SimpleStringProperty();
private SimpleStringProperty dateModified = new SimpleStringProperty();
private SimpleDoubleProperty size = new SimpleDoubleProperty();
public syncedFolders(
String name,
String location,
String dateModified,
double size) {
this.name.set(name);
this.location.set(location);
this.dateModified.set(dateModified);
this.size.setValue(size);
}
public String getName() {
return name.get();
}
public SimpleStringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public String getLocation() {
return location.get();
}
public SimpleStringProperty locationProperty() {
return location;
}
public void setLocation(String location) {
this.location.set(location);
}
public String getDateModified() {
return dateModified.get();
}
public SimpleStringProperty dateModifiedProperty() {
return dateModified;
}
public void setDateModified(String dateModified) {
this.dateModified.set(dateModified);
}
public double getSize() {
return size.get();
}
public SimpleDoubleProperty sizeProperty() {
return size;
}
public void setSize(double size) {
this.size.set(size);
}
}
EDIT:
I would also recommend changing how you populate your table. Your current addFolder() method is adding the item directly to the table's items property, but you really should add them to the underlying ObservableList that is set as the table's items:
public void addFolder() {
round(printSize, 1);
folderTable.setItems(getSyncedFolders());
folderTable.getItems().add(new syncedFolders(name, location, dateModified, tempSize));
}
The way folderTable.setItems() works is that it binds a list to the TableView. But that I mean that any changes made to the list will be reflected in the TableView automatically; you should not need to use folderTable.getItems().add() at all.
I am using JavaFX from 2 years back. Now i am creating spreadsheet like control using JavaFX.For creating control i am using TableView and ScrollPane with ListView control for Spreadsheet Row header as JavaFX do not provide support for row headers.Everything is working fine except one thing when i scroll table , initially table and scrollpane rows scroll in sync but after some scroll its starting mismatch.I have attached the screen shots of the same.
1)Initial screen without scroll
2)Screen after some scroll
Following are the code snippet for this control which i have pasted in pastebin.
1)Cells.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Cells extends Application {
public void start(Stage stage) {
stage.setScene(new Scene(new SpreadSheet(100, 26)));
stage.setTitle("Cells");
stage.setWidth(400);
stage.setHeight(400);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
2)SpreadSheet.java
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.HBox;
public class SpreadSheet extends HBox {
public SpreadSheet(int height, int width) {
super();
Model model = new Model(height, width);
TableView<ObservableList<Model.Cell>> table = new TableView<>();
table.setEditable(true);
table.setItems(model.getCellsAsObservableList());
for (char w = 'A'; w < 'A'+width; w++) {
TableColumn<ObservableList<Model.Cell>, String> column = new TableColumn<>(w+"");
column.setSortable(false);
column.setMinWidth(50);
column.setCellFactory(TextFieldTableCell.forTableColumn());
final char w0 = w;
column.setCellValueFactory(param -> param.getValue().get(w0-'A').text);
column.setOnEditStart(event -> {
int row = event.getTablePosition().getRow();
int col = event.getTablePosition().getColumn();
Model.Cell c = model.getCells()[row][col];
c.setShowUserData(true);
});
column.setOnEditCommit(event -> {
int row = event.getTablePosition().getRow();
int col = event.getTablePosition().getColumn();
Model.Cell c = model.getCells()[row][col];
System.out.println("Hello");
c.userData.set(event.getNewValue());
c.setShowUserData(false);
});
table.getColumns().add(column);
}
ListView<String> rowHeaders = new ListView<>();
rowHeaders.getItems().add("");
for (int i = 0; i < height; i++) {
rowHeaders.getItems().add(i+"");
}
ScrollPane scrolledRowHeaders = new ScrollPane(rowHeaders);
scrolledRowHeaders.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scrolledRowHeaders.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
table.getChildrenUnmodifiable().addListener((ListChangeListener<Node>) c -> {
ScrollBar vbarTable = (ScrollBar) table.lookup(".scroll-bar:vertical");
ScrollBar vbarRowHeaders = (ScrollBar) scrolledRowHeaders.lookup(".scroll-bar:vertical");
if (vbarRowHeaders != null && vbarTable != null)
vbarTable.valueProperty().bindBidirectional(vbarRowHeaders.valueProperty());
});
getChildren().addAll(scrolledRowHeaders, table);
}
}
3)Model.java
import java.util.List;
import javafx.beans.binding.Binding;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
class Model {
private Cell[][] cells;
Model(int height, int width) {
cells = new Cell[height][width];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
cells[i][j] = new Cell();
}
}
}
public Cell[][] getCells() {
return cells;
}
public ObservableList<ObservableList<Cell>> getCellsAsObservableList() {
ObservableList<ObservableList<Cell>> cs = FXCollections.observableArrayList();
for (int i = 0; i < cells.length; i++) {
cs.add(FXCollections.observableArrayList());
for (int j = 0; j < cells[i].length; j++) {
cs.get(i).add(cells[i][j]);
}
}
return cs;
}
class Cell {
public final StringProperty userData = new SimpleStringProperty("");
public final StringProperty text = new SimpleStringProperty("");
ObservableValue<Double>[] toArray(List<ObservableValue<Double>> l) {
return l.toArray(new ObservableValue[l.size()]);
}
// Has same problem
// public ObservableValue<Double> value = EasyBind.map(userData, Parser::parse)
// .flatMap(f -> Bindings.createObjectBinding(() -> f.eval(Model.this), toArray(f.getReferences(Model.this))));
// Has same problem
public ObservableValue<Double> value =
Bindings.createObjectBinding(() -> {
System.out.println(System.currentTimeMillis());
Formula f = Parser.parse(userData.get());
ObservableValue<Double>[] fs = toArray(f.getReferences(Model.this));
Binding<Double> d = Bindings.createObjectBinding(() -> {
double v = f.eval(Model.this);
// text.set(String.valueOf(v));
return v;
}, fs);
d.addListener((v, o, n) -> {
// ???
});
return d.getValue();
}, userData);
public void setShowUserData(Boolean b) {
if (b) text.setValue(userData.get());
else text.setValue(String.valueOf(value.getValue()));
}
}
}
4)Parser.java
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class Parser {
private static Parser instance = new Parser();
private static Tokenizer tokenizer;
static {
tokenizer = new Tokenizer();
tokenizer.add("[a-zA-Z_]\\d+", Token.CELL);
tokenizer.add("[a-zA-Z_]\\w*", Token.IDENT);
tokenizer.add("-?\\d+(\\.\\d*)?", Token.DECIMAL);
tokenizer.add("=", Token.EQUALS);
tokenizer.add(",", Token.COMMA);
tokenizer.add(":", Token.COLON);
tokenizer.add("\\(", Token.OPEN_BRACKET);
tokenizer.add("\\)", Token.CLOSE_BRACKET);
}
public static Formula parse(String formulaString) {
return instance.parseFormula(formulaString);
}
String formulaString;
LinkedList<Token> tokens;
Token lookahead;
private Parser() {}
private Formula parseFormula(String formulaString) {
this.formulaString = formulaString;
try {
tokenizer.tokenize(formulaString.replaceAll("\\s+",""));
} catch (ParseError e) {
System.out.println(e.getMessage());
}
this.tokens = tokenizer.getTokens();
if (tokens.isEmpty()) return Formula.Empty;
lookahead = this.tokens.getFirst();
return formula();
}
private Formula formula() {
switch(lookahead.token) {
case Token.DECIMAL:
String n = lookahead.sequence;
nextToken();
return new Number(Double.parseDouble(n));
case Token.EQUALS:
nextToken();
return expression();
case Token.EPSILON:
return Formula.Empty;
default:
return new Textual(formulaString);
}
}
private Formula expression() {
switch(lookahead.token) {
case Token.CELL:
int c = lookahead.sequence.charAt(0) - 'A';
int r = Integer.parseInt(lookahead.sequence.substring(1));
nextToken();
if (lookahead.token == Token.COLON) { // Range
nextToken();
if (lookahead.token == Token.CELL) {
int c2 = lookahead.sequence.charAt(0) - 'A';
int r2 = Integer.parseInt(lookahead.sequence.substring(1));
nextToken();
return new Range(new Coord(r, c), new Coord(r2, c2));
} else {
throw new ParseError("Incorrect Range: " + lookahead.sequence);
}
} else {
return new Coord(r, c);
}
case Token.DECIMAL:
Double d = Double.parseDouble(lookahead.sequence);
nextToken();
return new Number(d);
case Token.IDENT:
return application();
default:
throw new ParseError("Incorrect Expression: " + lookahead.sequence);
}
}
private Formula application() {
String opName = lookahead.sequence;
nextToken();
if (lookahead.token != Token.OPEN_BRACKET)
throw new ParseError("No opening bracket: " + opName);
nextToken();
List<Formula> args = new ArrayList<Formula>();
while (true) {
if (lookahead.token == Token.EPSILON)
throw new ParseError("No closing bracket");
args.add(expression());
if (lookahead.token == Token.COMMA) nextToken();
if (lookahead.token == Token.CLOSE_BRACKET)
return new Application(opName, args);
}
}
private void nextToken() {
tokens.pop();
if (tokens.isEmpty()) lookahead = new Token(Token.EPSILON, "");
else lookahead = tokens.getFirst();
}
}
class ParseError extends RuntimeException {
ParseError(String message) {
super(message);
}
}
class Token {
public static final int EPSILON = 0;
public static final int EQUALS = 1;
public static final int IDENT = 2;
public static final int DECIMAL = 3;
public static final int OPEN_BRACKET = 4;
public static final int CLOSE_BRACKET = 5;
public static final int COMMA = 6;
public static final int COLON = 7;
public static final int CELL = 8;
public final int token;
public final String sequence;
public Token(int token, String sequence) {
this.token = token;
this.sequence = sequence;
}
}
class Tokenizer {
private LinkedList<TokenInfo> tokenInfos;
private LinkedList<Token> tokens;
public Tokenizer() {
tokenInfos = new LinkedList<TokenInfo>();
tokens = new LinkedList<Token>();
}
public void add(String regex, int token) {
tokenInfos.add(new TokenInfo(Pattern.compile("^("+regex+")"), token));
}
public void tokenize(String s) {
tokens.clear();
while (!s.equals("")) {
boolean match = false;
for (TokenInfo info : tokenInfos) {
Matcher m = info.regex.matcher(s);
if (m.find()) {
match = true;
String tok = m.group().trim();
tokens.add(new Token(info.token, tok));
s = m.replaceFirst("");
break;
}
}
if (!match) throw new ParseError("Unexpected char in input: " + s);
}
}
public LinkedList<Token> getTokens() {
return tokens;
}
private static class TokenInfo {
public final Pattern regex;
public final int token;
public TokenInfo(Pattern regex, int token) {
super();
this.regex = regex;
this.token = token;
}
}
}
5)Formula.java
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Cell;
import java.util.*;
abstract class Formula {
public static final Formula Empty = new Textual("");
public double eval(Model env) { return 0.0; }
public List<ObservableValue<Double>> getReferences(Model env) { return Collections.emptyList(); }
}
class Textual extends Formula {
String value;
public Textual(String value) {
this.value = value;
}
public String toString() {
return value;
}
}
class Number extends Formula {
double value;
public Number(double value) {
this.value = value;
}
public String toString() {
return String.valueOf(value);
}
public double eval(Model env) {
return value;
}
}
class Coord extends Formula {
int row, column;
public Coord(int row, int column) {
this.row = row;
this.column = column;
}
public String toString() {
return ((char)('A'+column))+""+row;
}
public double eval(Model env) {
return env.getCells()[row][column].value.getValue();
}
public List<ObservableValue<Double>> getReferences(Model env) {
List<ObservableValue<Double>> result = new ArrayList<>(1);
result.add(env.getCells()[row][column].value);
return result;
}
}
class Range extends Formula {
Coord coord1, coord2;
public Range(Coord coord1, Coord coord2) {
this.coord1 = coord1; this.coord2 = coord2;
}
public String toString() {
return String.valueOf(coord1)+":"+String.valueOf(coord2);
}
public double eval(Model env) {
throw new RuntimeException("Range cannot be evaluated!");
}
public List<ObservableValue<Double>> getReferences(Model env) {
List<ObservableValue<Double>> result = new ArrayList<>();
for (int r = coord1.row; r <= coord2.row; r++) {
for (int c = coord1.column; c <= coord2.column; c++) {
result.add(env.getCells()[r][c].value);
}
}
return result;
}
}
class Application extends Formula {
String function;
List<Formula> arguments;
public Application(String function, List<Formula> arguments) {
this.function = function;
this.arguments = arguments;
}
public String toString() {
StringBuilder t = new StringBuilder();
t.append(function);
t.append("(");
for (int i = 0; i < arguments.size()-1; i ++) {
t.append(arguments.get(i).toString());
t.append(", ");
}
if (!arguments.isEmpty()) t.append(arguments.get(arguments.size()-1).toString());
t.append(")");
return t.toString();
}
public double eval(Model env) {
try {
List<Double> argvals = evalList(arguments, env);
return opTable.get(function).eval(argvals);
} catch(Exception e) {
return Double.NaN;
}
}
public List<ObservableValue<Double>> getReferences(Model env) {
List<ObservableValue<Double>> result = new ArrayList<>();
for (Formula argument : arguments) {
result.addAll(argument.getReferences(env));
}
return result;
}
private static List<Double> evalList(List<Formula> args, Model env) {
List<Double> result = new ArrayList<>();
for (Formula f : args) {
if (f instanceof Range) {
for (ObservableValue<Double> c : f.getReferences(env)) {
result.add(c.getValue());
}
} else {
result.add(f.eval(env));
}
}
return result;
}
private static Map<String, Op> opTable = new HashMap<>();
static {
opTable.put("add", vals -> vals.get(0) + vals.get(1));
opTable.put("sub", vals -> vals.get(0) - vals.get(1));
opTable.put("div", vals -> vals.get(0) / vals.get(1));
opTable.put("mul", vals -> vals.get(0) * vals.get(1));
opTable.put("mod", vals -> vals.get(0) % vals.get(1));
opTable.put("sum", vals -> {
double accum = 0;
for (Double i : vals) {
accum += i;
}
return accum;
});
opTable.put("prod", vals -> {
double accum = 1;
for (Double i : vals) {
accum *= i;
}
return accum;
});
}
private static interface Op {
public double eval(List<Double> vals);
}
}
Try this
For your ListView get your VBar and bind it with your TableView VBar and your alignment will be intact.
To get your ListView VBar run this code same for TableView VBar
VirtualFlow tvVF = (VirtualFlow) TableView.lookup("#virtual-flow");
for (Node n : tvVF.getChildrenUnmodifiable()) {
if (n.getClass().isAssignableFrom(VirtualScrollBar.class)) {
VirtualScrollBar table_vsb = (VirtualScrollBar) n;
if (tvVF.getWidth() - table_vsb.getWidth() > tvVF.getWidth() / 2) {
//table_vsb is your Vertical bar for the TableView
....//close braces
VirtualFlow lvVF = (VirtualFlow) ListView.lookup("#virtual-flow");
// we do the same for listview
for (Node c : lvVF.getChildrenUnmodifiable()) {
if (c.getClass().isAssignableFrom(VirtualScrollBar.class)) {
VirtualScrollBar list_vsb = (VirtualScrollBar) c;
if (tvVF.getWidth() - vsb.getWidth() > tvVF.getWidth() / 2) {
//list_vsb is your vbar for the listview
now since you have your two VBar's bind the ListView's to the TableView like this
list_vsb.valueProperty().bind(table_vsb.valueProperty());
Please note that you need to call these codes after a full layout, or when Stage.show(); is called - to be safe
Hope its lucid and helps
EDIT
[][]3
it does work for me :)
I've got following problem:
I styled a TableViews Cells into Rectangles. Now I want to add Objects with an X and Y Coordinate in it (obj.getAblaufX() and obj.getAblaufY()).
How can I add them correctly in JavaFX TableView?
My Current Code:
ObservableList<Event> eventList = loadEvents();
TableView<Event> tableView = new TableView<Event>();
tableView.getSelectionModel().setCellSelectionEnabled(true);
tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
tableView.setItems(eventList);
for(int i = 0; i < 10; i++) {
TableColumn<Event, String> tmpCol = new TableColumn<>();
tmpCol.setId(Integer.toString(i));
tmpCol.setMaxWidth(40);
tmpCol.setCellValueFactory(cellData -> new ObservableValue<String>() {
#Override
public void addListener(InvalidationListener listener) {
// TODO Auto-generated method stub
}
#Override
public void removeListener(InvalidationListener listener) {
// TODO Auto-generated method stub
}
#Override
public void addListener(ChangeListener<? super String> listener) {
// TODO Auto-generated method stub
}
#Override
public void removeListener(ChangeListener<? super String> listener) {
// TODO Auto-generated method stub
}
#Override
public String getValue() {
if(Integer.parseInt(tmpCol.getId()) == cellData.getValue().getAblaufX()) {
return cellData.getValue().getAbteilung();
} else {
return "";
}
}
});
tableView.getColumns().add(tmpCol);
}
Result:
But:
Object SP1 has X = 0 and Y = 0
Object SP2 has X = 1 and Y = 0
So, how to fix this Problem?
By using Event as item type, you pervent the TableView from displaying more than one Event per row. Use Integer as item type instead and store the values in a ObservableMap<TablePos, Event> where TablePos is a class containing the x and y coordinates:
public final class TablePos {
private final int x;
private final int y;
public TablePos(int x, int y) {
this.x = x;
this.y = y;
}
#Override
public int hashCode() {
int hash = 7;
hash = 17 * hash + this.x;
hash = 17 * hash + this.y;
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof TablePos)) {
return false;
}
final TablePos other = (TablePos) obj;
return (this.x == other.x && this.y == other.y);
}
}
public class Event {
private final StringProperty name;
private final IntegerProperty x;
private final IntegerProperty y;
public Event(String name, int x, int y) {
// set beans for properties here to make this instance accessible to listeners
this.y = new SimpleIntegerProperty(this, "y", y);
this.x = new SimpleIntegerProperty(this, "x", x);
this.name = new SimpleStringProperty(this, "name", name);
}
public final String getName() {
return this.name.get();
}
public final void setName(String value) {
this.name.set(value);
}
public final StringProperty nameProperty() {
return this.name;
}
public final int getX() {
return this.x.get();
}
public final void setX(int value) {
this.x.set(value);
}
public final IntegerProperty xProperty() {
return this.x;
}
public final int getY() {
return this.y.get();
}
public final void setY(int value) {
this.y.set(value);
}
public final IntegerProperty yProperty() {
return this.y;
}
}
private static void put(Map<TablePos, Event> map, Event evt) {
map.put(new TablePos(evt.getX(), evt.getY()), evt);
}
#Override
public void start(Stage primaryStage) {
ObservableMap<TablePos, Event> contents = FXCollections.observableHashMap();
TableView<Integer> tableView = new TableView<>(FXCollections.observableArrayList());
TableColumn columnGroup = new TableColumn("Heutige Termine");
ChangeListener<Number> xChangeListener = (observable, oldValue, newValue) -> {
Event evt = (Event) ((Property) observable).getBean();
TablePos oldPos = new TablePos(oldValue.intValue(), evt.getY());
TablePos newPos = new TablePos(newValue.intValue(), evt.getY());
contents.remove(oldPos);
contents.put(newPos, evt);
};
ChangeListener<Number> yChangeListener = (observable, oldValue, newValue) -> {
Event evt = (Event) ((Property) observable).getBean();
TablePos oldPos = new TablePos(evt.getX(), oldValue.intValue());
TablePos newPos = new TablePos(evt.getX(), newValue.intValue());
contents.remove(oldPos);
contents.put(newPos, evt);
};
contents.addListener((MapChangeListener.Change<? extends TablePos, ? extends Event> change) -> {
if (change.wasRemoved()) {
Event evt = change.getValueRemoved();
evt.xProperty().removeListener(xChangeListener);
evt.yProperty().removeListener(yChangeListener);
}
if (change.wasAdded()) {
Event evt = change.getValueAdded();
evt.xProperty().addListener(xChangeListener);
evt.yProperty().addListener(yChangeListener);
}
});
// items denote the y coordinate
for (int i = 0; i < 10; i++) {
tableView.getItems().add(i);
}
// one column per x coordiante
for (int i = 0; i < 10; i++) {
final int index = i;
TableColumn<Integer, String> column = new TableColumn<>();
// take value from Map using x (index) and y (item value)
column.setCellValueFactory(cd
-> Bindings.selectString(
Bindings.valueAt(contents, new TablePos(index, cd.getValue())), "name"));
columnGroup.getColumns().add(column);
}
tableView.getColumns().add(columnGroup);
Event opTarget = new Event("Something", 2, 9);
put(contents, new Event("SP1", 0, 0));
put(contents, new Event("SP2", 1, 0));
put(contents, opTarget);
Button move = new Button("move");
move.setOnAction(evt -> opTarget.setX(9 - opTarget.getX()));
Button rename = new Button("rename");
rename.setOnAction(evt -> opTarget.setName(opTarget.getName().equals("42") ? "Answer" : "42"));
Scene scene = new Scene(new VBox(10, tableView, move, rename));
primaryStage.setScene(scene);
primaryStage.show();
}
Note that this requires you to know the number of rows/columns at the initialisation (I used 10 for both for simplicity). If this is not the case, you could add a listener to the ObservableMap that dynamically adds/removes rows / columns.
I'm using SWT (and Eclipse RCP) to render a table. My problem is that if I change the background of a cell (a ViewerCell in fact) I can see that it has the new color.
My problem is that if I select a row in my Table or if I hover over the row containing my cell in question then the selection/hover background overrides my cell color. How can I override this?
Problem solved with StyledCellLabelProvider. Tell me if you want to see some code.
Edit:
We use it do display validation errors so ignore the validation stuff here:
public class ValidationCellLabelProvider extends StyledCellLabelProvider {
private static final int DELAY = 200;
private static final int SHIFT_X = 5;
private static final int SHIFT_Y = 5;
private static final int DISPLAY = 5000;
private CellLabelProvider provider;
private String propertyName;
private final StyleRange[] styleRanges = new StyleRange[1];
/**
* Default constructor.
* #param provider provider
* #param propertyName propertyName
*/
public ValidationCellLabelProvider(CellLabelProvider provider, String propertyName) {
super(StyledCellLabelProvider.COLORS_ON_SELECTION);
this.provider = provider;
this.propertyName = propertyName;
this.setOwnerDrawEnabled(true);
}
#Override
public void initialize(ColumnViewer viewer, ViewerColumn column) {
super.initialize(viewer, column);
final StyleRange styleRange = new StyleRange();
styleRange.foreground = Display.getCurrent().getSystemColor(SWT.COLOR_WHITE);
styleRange.background = Display.getCurrent().getSystemColor(SWT.COLOR_RED);
styleRanges[0] = styleRange;
}
#Override
public void update(ViewerCell cell) {
provider.update(cell);
if (cell.getStyleRanges() == null) {
cell.setStyleRanges(styleRanges);
}
if (cell.getElement() instanceof IValidable) {
IValidable model = (IValidable) cell.getElement();
if (!ControllerRegistry.getCurrentViolations().getViolations(model.getModelId(), propertyName).isEmpty()) {
if (cell.getText().isEmpty()) {
cell.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
cell.setImage(FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR).getImage());
} else {
if (styleRanges[0].length < cell.getText().length()) {
styleRanges[0].length = cell.getText().length();
}
}
} else {
if (cell.getImage() != null) {
cell.setImage(null);
}
cell.setStyleRanges(null);
}
}
super.update(cell);
}
//mine
#Override
protected void paint(Event event, Object element) {
if (element instanceof IValidable) {
IValidable model = (IValidable) element;
if (!ControllerRegistry.getCurrentViolations().getViolations(model.getModelId(), propertyName).isEmpty()) {
int width = 1000;
int x = event.x;
int y = event.y;
int height = event.height - 1;
GC gc = event.gc;
Color oldBackground = gc.getBackground();
gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_RED));
gc.fillRectangle(x, y, width, height);
gc.setBackground(oldBackground);
}
}
super.paint(event, element);
}
//-----
#Override
public String getToolTipText(Object element) {
String ret = null;
if (element instanceof IValidable) {
List<ConstraintViolation> constraintViolations = ControllerRegistry.getCurrentViolations().getViolations(
((IValidable) element).getModelId(), propertyName);
if (!constraintViolations.isEmpty()) {
ret = ValidationHelper.getMessage(constraintViolations);
}
}
if (ret != null) {
ret = ret.length() > 0 ? ret.toString() : null;
}
return ret;
}
#Override
public int getToolTipDisplayDelayTime(Object object) {
return DELAY;
}
#Override
public Point getToolTipShift(Object object) {
return new Point(SHIFT_X, SHIFT_Y);
}
#Override
public int getToolTipTimeDisplayed(Object object) {
return DISPLAY;
}
}
The only option I see would be use an OwnerDrawLabelProvider and paint the whole cell yourself.
There is a way to prevent the table from drawing its selection background but the font color will still change to its selection color, so depending on your OS you might end up with white text on white background when a row is selected.