Skip to content

Commit

Permalink
🔖 Version 11.9.4
Browse files Browse the repository at this point in the history
⬆️ Update Gradle to version 8.1.1
⬆️ Update JavaFX plugin to version 0.0.14
⬆️ Update JavaFX to version 20.0.1
⬆️ Update MFXCore to version 11.6.2
⬆️ Update MFXEffects to version 11.2.1
⬆️ Update MFXResources to version 11.7.0
⬆️ Update JUnito to version 5.9.2

🐛 DefaultTableColumn: fix inViewport property not updating correctly in VARIABLE layout mode
🐛 DefaultTableColumnSkin: fixed sizes not updating when switching layout mode due to JavaFX caching the previous results
♻️ DefaultTableColumnSkin: improved minimum width computation in VARIABLE layout mode to be at least the size of the header

♻️ TableHelper: remove computeEstimatedSize() method override from the VariableHelper. The method has also been changed so that the virtual width/breadth is always computed as the sum of all the columns' width
♻️ TableHelper: improved layout algorithm in general. Rows now use the virtual width as their width to cover all the table even when scrolling. Improved size computation of the last table column. Do not constraint the column' sizes here anymore, just use LayoutUtils.boundWidth(...)

✨ VirtualTable: added convenience method to autosize columns
♻️ VirtualTableSkin: use the virtual width as the width for the viewport during layout

Signed-off-by: palexdev <alessandro.parisi406@gmail.com>
  • Loading branch information
palexdev committed May 29, 2023
1 parent da77d37 commit fe1d514
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 65 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ group 'io.github.palexdev'
version "$virtualizedfxVersion"

repositories {
mavenLocal()
mavenCentral()

flatDir {
Expand Down
16 changes: 8 additions & 8 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#--------------------------------------#
GROUP=io.github.palexdev
POM_ARTIFACT_ID=virtualizedfx
VERSION_NAME=11.9.3
VERSION_NAME=11.9.4

POM_NAME=virtualizedfx
POM_DESCRIPTION=Alternative VirtualFlows for JavaFX
Expand All @@ -25,21 +25,21 @@ POM_PACKAGING=jar
#--------------------------------------#
# Versions #
#--------------------------------------#
virtualizedfxVersion=11.9.3
virtualizedfxVersion=11.9.4
jdk=11
testJdk=17
# Plugins
jfxPlugin=0.0.13
jfxPlugin=0.0.14
mavenPublishPlugin=0.19.0
bndPlugin=6.2.0
modularityPlugin=1.8.10
# Dependencies
jfx=19.0.2
mfxcore=11.3.1
mfxeffects=11.0.5
mfxresources=11.3.3
jfx=20.0.1
mfxcore=11.6.2
mfxeffects=11.2.1
mfxresources=11.7.0
# Test Dependencies
junit=5.9.1
junit=5.9.2
testfx=4.0.16-alpha
cssfx=11.5.1
faker=1.6.0
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -448,12 +448,25 @@ public double maxHScroll() {
return estimatedSize.getWidth() - table.getWidth();
}

/**
* {@inheritDoc}
* <p></p>
* The virtual height is computed as the number of items multiplied by {@link VirtualTable#cellHeightProperty()}.
* The virtual width is computed by summing the width of every column in the table. This is necessary for two
* reasons:
* <p> 1) In {@link ColumnsLayoutMode#VARIABLE} the columns may have a width that is lesser or greater than
* {@link VirtualTable#columnSizeProperty()}
* <p> 2) In {@link ColumnsLayoutMode#FIXED} all columns are forced to have the sizes specified by
* {@link VirtualTable#columnSizeProperty()}, however the last column may be bigger since the layout strategy
* is made so that the last column always covers any space left in the viewport
*/
@Override
public Size computeEstimatedSize() {
double cellHeight = table.getCellHeight();
double columnWidth = table.getColumnSize().getWidth();
double length = table.getItems().size() * cellHeight;
double breadth = table.getColumns().size() * columnWidth;
double breadth = table.getColumns().stream()
.mapToDouble(c -> c.getRegion().getWidth())
.sum();
Size size = Size.of(breadth, length);
estimatedSize.set(size);
return size;
Expand Down Expand Up @@ -652,8 +665,8 @@ public Map<Orientation, List<Double>> computePositions(TableState<?> state, bool
* space.
* <p></p>
* Rows are laid out from top to bottom, relocated at the previously computed X offset and at the extracted Y position
* (+ the Y offset); and resized with the previously gathered height. The width is given by the maximum between
* the table width and the row size (given by the number of cells in the row multiplied by the columns width)
* (+ the Y offset); and resized with the previously gathered height. The width is given by the current
* {@link VirtualTable#estimatedSizeProperty()} so that rows always have the same width of the column's container.
* <p></p>
* For each row in the loop it also lays out the their cells. Each cell is relocated at the extracted X position
* and at Y 0; and resized to the previously gathered cell height. The width is the same of the corresponding column.
Expand Down Expand Up @@ -700,7 +713,7 @@ public void layout() {
for (TableRow<?> row : state.getRows().values()) {
xI = xPositions.size() - 1;
Double yPos = yPositions.get(yI);
double rowW = Math.max(row.size() * colW, table.getWidth());
double rowW = table.getEstimatedSize().getWidth();
row.resizeRelocate(xOffset, yPos + yOffset, rowW, cellH);

List<TableCell<?>> cells = new ArrayList<>(row.getCells().values());
Expand Down Expand Up @@ -781,25 +794,6 @@ public IntegerRange columnsRange() {
return IntegerRange.of(firstColumn(), lastColumn());
}

/**
* {@inheritDoc}
* <p></p>
* The breadth is computed by iterating over all the columns and getting their width.
* To be precise the width used by the computation is given by the maximum between the actual width of the
* column's region and the size specified by {@link VirtualTable#columnSizeProperty()}.
*/
@Override
public Size computeEstimatedSize() {
double cellHeight = table.getCellHeight();
double length = table.getItems().size() * cellHeight;
double breadth = table.getColumns().stream()
.mapToDouble(c -> Math.max(c.getRegion().getWidth(), table.getColumnSize().getWidth()))
.sum();
Size size = Size.of(breadth, length);
estimatedSize.set(size);
return size;
}

/**
* This binding holds the horizontal position of the viewport.
* This is the direction along the estimated breath.
Expand Down Expand Up @@ -876,19 +870,33 @@ public void autosizeColumn(TableColumn<?, ? extends TableCell<?>> column) {
Region region = column.getRegion();
ObservableList<? extends TableColumn<?, ? extends TableCell<?>>> columns = table.getColumns();
int cIndex = table.getColumnIndex(((TableColumn) column));
double targetW;

// If it's last index, special handling to always use all the available remaining space
// Find the largest cell for column, this is needed in any case
// For last column, we want a width that is enough to fill the table but also enough to fully show
// all cells
Collection<? extends TableRow<?>> rows = state.getRows().values();
double targetW = rows.stream()
.mapToDouble(r -> r.getWidthOf(cIndex))
.max()
.orElseGet(region::getWidth);

// If it's last index, special handling to use at least all the available remaining space
if (cIndex == columns.size() - 1) {
// Compute last column's width
double colW = LayoutUtils.boundWidth(region);

// First compute total width for all previous columns
double totalW = columns.stream()
.map(TableColumn::getRegion)
.mapToDouble(Region::getWidth)
.sum() - region.getWidth();
.map(TableColumn::getRegion)
.mapToDouble(Region::getWidth)
.sum() - colW;

// If less than table width then targetW becomes `table.getWidth() - totalW + colW`
if (totalW < table.getWidth()) {
targetW = table.getWidth() - totalW + region.getWidth();
targetW = Math.max(
targetW,
table.getWidth() - totalW + colW
);

// Here we terminate the auto-sizing, if the condition is false
// then the other "method" is used
Expand All @@ -900,12 +908,6 @@ public void autosizeColumn(TableColumn<?, ? extends TableCell<?>> column) {
}
}

Collection<? extends TableRow<?>> rows = state.getRows().values();
targetW = rows.stream()
.mapToDouble(r -> r.getWidthOf(cIndex))
.max()
.orElseGet(region::getWidth);

region.setPrefWidth(targetW);
computeEstimatedSize();
computePositions(state, true, false);
Expand Down Expand Up @@ -967,7 +969,7 @@ public Map<Orientation, List<Double>> computePositions(TableState<?> state, bool
TableColumn<?, ? extends TableCell<?>> column = table.getColumn(cIndex);
Region region = column.getRegion();
xPositions.add(pos);
double colW = Math.max(LayoutUtils.boundWidth(region), table.getColumnSize().getWidth());
double colW = LayoutUtils.boundWidth(region);
pos += colW;
}
}
Expand Down Expand Up @@ -1003,15 +1005,15 @@ public Map<Orientation, List<Double>> computePositions(TableState<?> state, bool
* <p> - the Y offset with {@link #verticalOffset()}
* <p></p>
* Columns are laid out from left to right, relocated at the extracted X position and at Y 0;
* and resized to the previously gathered height. The width is computed as the maximum between the column's region
* width and the minimum width specified by {@link VirtualTable#columnSizeProperty()}.
* and resized to the previously gathered height. The width is computed by {@link LayoutUtils#boundWidth(Node)}
* to honor the min, pref, and max values.
* The last column is an exception because if not all the space of the table was occupied by
* laying out the previous columns than its width will be set to the entire remaining
* space.
* <p></p>
* Rows are laid out from top to bottom, relocated at X 0 and at the extracted Y position (+ the Y offset);
* and resized with the previously gathered height. The width is given by the maximum between
* the table width and the row size (the row's region width plus the minimum columns width given by {@link VirtualTable#columnSizeProperty()}).
* and resized with the previously gathered height. The width is given by the current
* {@link VirtualTable#estimatedSizeProperty()} so that rows always have the same width of the column's container.
* <p></p>
* For each row in the loop it also lays out their cells. Each cell is relocated at the extracted X position
* and at Y 0; and resized to the previously gathered cell height. The width is the same of the corresponding column.
Expand All @@ -1036,7 +1038,7 @@ public void layout() {
for (Integer cIndex : columnsRange) {
TableColumn<?, ? extends TableCell<?>> column = table.getColumn(cIndex);
Region region = column.getRegion();
double colW = Math.max(LayoutUtils.boundWidth(region), table.getColumnSize().getWidth());
double colW = LayoutUtils.boundWidth(region);
Double xPos = xPositions.get(xI);
totalW += colW;

Expand All @@ -1056,7 +1058,7 @@ public void layout() {
for (TableRow<?> row : state.getRows().values()) {
xI = 0;
Double yPos = yPositions.get(yI);
double rowW = Math.max(LayoutUtils.boundWidth(row) + table.getColumnSize().getWidth(), table.getWidth());
double rowW = table.getEstimatedSize().getWidth();
row.resizeRelocate(0, yPos + yOffset, rowW, cellH);

List<TableCell<?>> cells = new ArrayList<>(row.getCells().values());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableObjectProperty;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableSizeProperty;
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableSizeProperty.SizeConverter;
import io.github.palexdev.mfxcore.observables.When;
import io.github.palexdev.mfxcore.utils.NumberUtils;
import io.github.palexdev.mfxcore.utils.fx.PropUtils;
import io.github.palexdev.mfxcore.utils.fx.StyleUtils;
Expand Down Expand Up @@ -244,6 +245,53 @@ public void requestViewportLayout() {
setNeedsViewportLayout(true);
}

/**
* Retrieves the column at the given index, if present and not null, delegates to
* {@link #autosizeColumn(TableColumn)}.
*/
public void autosizeColumn(int index) {
try {
TableColumn<T, ? extends TableCell<T>> column = getColumn(index);
if (column == null) return;
autosizeColumn(column);
} catch (Exception ignored) {
}
}

/**
* Auto-sizes the given column. This can be done only if the current {@link #getTableHelper()} is not null.
* For this to work properly, the table should have been laid out in the scene graph at least once. If the skin is
* still not available, the action is delayed until the skin is created.
*/
public void autosizeColumn(TableColumn<T, ? extends TableCell<T>> column) {
TableHelper helper = getTableHelper();
if (helper == null) return;
When.onChanged(skinProperty())
.condition((o, n) -> n != null && getWidth() > 0)
.then((o, n) -> helper.autosizeColumn(column))
.invalidating(widthProperty())
.oneShot(true)
.executeNow(() -> getSkin() != null && getWidth() > 0)
.listen();
}

/**
* Auto-sizes all the table's columns. This can be done only if the current {@link #getTableHelper()} is not null.
* For this to work properly, the table should have been laid out in the scene graph at least once. If the skin is
* still not available, the action is delayed until the skin is created.
*/
public void autosizeColumns() {
TableHelper helper = getTableHelper();
if (helper == null) return;
When.onChanged(skinProperty())
.condition((o, n) -> n != null && getWidth() > 0)
.then((o, n) -> helper.autosizeColumns())
.invalidating(widthProperty())
.oneShot(true)
.executeNow(() -> getSkin() != null && getWidth() > 0)
.listen();
}

/**
* Sets the {@link #rowFactoryProperty()} to a default function which produces {@link DefaultTableRow}s.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ protected void layoutChildren() {
VirtualTable<T> table = getSkinnable();

// First layout the columns and rows containers
double w = table.getWidth();
double w = table.getEstimatedSize().getWidth();
double h = table.getHeight();
double cH = table.getColumnSize().getHeight();
double rH = Math.max(h - cH, LayoutUtils.boundHeight(rContainer));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ private void initialize() {
if (o.getMinX() != n.getMinX()) bindings.invTarget(inViewport);
}))
.addInvalidatingSource(ExternalSource.of(table.positionProperty(), (o, n) -> {
if (table.getColumnsLayoutMode() == ColumnsLayoutMode.VARIABLE) return;
if (o.getX() != n.getX()) bindings.invTarget(inViewport);
}))
.addInvalidatingSource(ExternalSource.of(table.needsViewportLayoutProperty(), (o, n) -> {
Expand Down Expand Up @@ -271,10 +270,6 @@ public ReadOnlyBooleanProperty inViewportProperty() {
return inViewport;
}

@Override
public void onVisibilityChanged(boolean before, boolean now) {
}

//================================================================================
// Styleable Properties
//================================================================================
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package io.github.palexdev.virtualizedfx.table.defaults;

import io.github.palexdev.mfxcore.base.bindings.MFXBindings;
import io.github.palexdev.mfxcore.utils.fx.LayoutUtils;
import io.github.palexdev.mfxcore.utils.fx.TextUtils;
import io.github.palexdev.virtualizedfx.cell.TableCell;
import io.github.palexdev.virtualizedfx.controls.BoundLabel;
import io.github.palexdev.virtualizedfx.enums.ColumnsLayoutMode;
import io.github.palexdev.virtualizedfx.table.VirtualTable;
import javafx.beans.InvalidationListener;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.scene.Node;
Expand Down Expand Up @@ -85,8 +87,13 @@ private void addListeners() {
table.onColumnChangedFactory(column);
});

/*
* JavaFX caches the size computations, requestLayout() resets the cache
*/
VirtualTable<T> table = column.getTable();
table.heightProperty().addListener(invalidated -> column.requestLayout());
InvalidationListener layoutListener = invalidated -> column.requestLayout();
table.heightProperty().addListener(layoutListener);
table.columnsLayoutModeProperty().addListener(layoutListener);
}

private void initIcon() {
Expand All @@ -110,8 +117,8 @@ private void initIcon() {
protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
DefaultTableColumn<T, C> column = getSkinnable();
VirtualTable<T> table = column.getTable();
if (table != null && table.getColumnsLayoutMode() == ColumnsLayoutMode.VARIABLE) {
return table.getColumnSize().getWidth();
if (table.getColumnsLayoutMode() == ColumnsLayoutMode.VARIABLE) {
return Math.max(LayoutUtils.boundWidth(box), table.getColumnSize().getWidth());
}
return super.computeMinWidth(height, topInset, rightInset, bottomInset, leftInset);
}
Expand All @@ -122,7 +129,7 @@ protected double computePrefWidth(double height, double topInset, double rightIn
VirtualTable<T> table = column.getTable();
Node icon = column.getGraphic();
double gap = column.getGraphicTextGap();
if (table != null && table.getColumnsLayoutMode() == ColumnsLayoutMode.VARIABLE) {
if (table.getColumnsLayoutMode() == ColumnsLayoutMode.VARIABLE) {
return leftInset + TextUtils.computeLabelWidth(label) + (icon != null ? icon.prefWidth(-1) + gap : 0) + rightInset;
}
return super.computePrefWidth(height, topInset, rightInset, bottomInset, leftInset);
Expand Down
3 changes: 2 additions & 1 deletion src/test/java/app/table/PTableTestActions.java
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public enum PTableTestActions {
p.getRoot(),
"Switch Columns",
"Choose Second Column By Index",
Constraint.of("Invalid index", i -> i >= 0 && i < t.getColumns().size())
Constraint.of("Invalid index", i -> i >= 0 && i < t.getColumns().size())
);
if (fIndex == -1 || sIndex == -1) return;

Expand All @@ -201,6 +201,7 @@ public enum PTableTestActions {
}),
CLEAR_COLUMNS((p, t) -> t.getColumns().clear()),
RESET_COLUMNS((p, t) -> t.getColumns().setAll(p.getColumns())),
AUTOSIZE_COLUMNS((p, t) -> t.autosizeColumns()),
SWITCH_LAYOUT_MODE((p, t) -> t.setColumnsLayoutMode(EnumUtils.next(ColumnsLayoutMode.class, t.getColumnsLayoutMode()))),
GO_TO_PAGE((p, t) -> {
int index = getIntFromUser(p.getRoot(), "Choose Page...", "Number of pages: " + t.getMaxPage(), Constraint.of("Invalid", i -> i > 0 && i <= t.getMaxPage()));
Expand Down

0 comments on commit fe1d514

Please sign in to comment.