Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4cb62db297 | ||
![]() |
75d507e20c | ||
![]() |
e159bbd3c4 |
10
build.gradle
10
build.gradle
@ -15,10 +15,18 @@ repositories {
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'org.openjfx.javafxplugin'
|
||||
|
||||
javafx {
|
||||
version = "$jfx"
|
||||
modules = ['javafx.controls', 'javafx.fxml', 'javafx.media', 'javafx.swing', 'javafx.web']
|
||||
}
|
||||
|
||||
test {
|
||||
// Because the new TestFX is garbage :)
|
||||
jvmArgs += [
|
||||
'--add-opens', 'javafx.graphics/com.sun.javafx.application=ALL-UNNAMED'
|
||||
]
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
import java.nio.file.Paths
|
||||
|
||||
plugins {
|
||||
id 'application'
|
||||
id 'org.beryx.jlink' version "$jlink"
|
||||
@ -13,49 +15,43 @@ repositories {
|
||||
}
|
||||
}
|
||||
|
||||
compileJava {
|
||||
sourceCompatibility = "$jdk"
|
||||
targetCompatibility = "$jdk"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':materialfx')
|
||||
|
||||
implementation("io.github.palexdev:scenicview:$scenicView") {
|
||||
exclude group: 'org.openjfx'
|
||||
}
|
||||
implementation("fr.brouillard.oss:cssfx:$cssfx") { exclude group: 'org.openjfx' }
|
||||
implementation "org.kordamp.ikonli:ikonli-core:$ikonli"
|
||||
implementation "org.kordamp.ikonli:ikonli-javafx:$ikonli"
|
||||
implementation "org.kordamp.ikonli:ikonli-fontawesome5-pack:$ikonli"
|
||||
implementation "io.github.palexdev:virtualizedfx:$vfx"
|
||||
|
||||
testImplementation "org.testfx:testfx-core:$testfx"
|
||||
testImplementation "org.testfx:testfx-junit5:$testfx"
|
||||
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit"
|
||||
testImplementation "org.junit.platform:junit-platform-suite-api:$junitSuite"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit"
|
||||
|
||||
implementation "io.github.palexdev:scenicview:$scenicView"
|
||||
implementation("fr.brouillard.oss:cssfx:$cssfx") { exclude group: 'org.openjfx' }
|
||||
implementation "org.kordamp.ikonli:ikonli-core:$ikonli"
|
||||
implementation "org.kordamp.ikonli:ikonli-javafx:$ikonli"
|
||||
implementation "org.kordamp.ikonli:ikonli-fontawesome5-pack:$ikonli"
|
||||
implementation "io.github.palexdev:virtualizedfx:$vfx"
|
||||
implementation project(':materialfx')
|
||||
}
|
||||
|
||||
compileJava {
|
||||
sourceCompatibility = "$testJdk"
|
||||
targetCompatibility = "$testJdk"
|
||||
}
|
||||
|
||||
compileTestJava {
|
||||
moduleOptions {
|
||||
compileOnClasspath = true
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
|
||||
moduleOptions {
|
||||
runOnClasspath = true
|
||||
}
|
||||
javafx {
|
||||
modules = ['javafx.controls', 'javafx.fxml', 'javafx.swing']
|
||||
configurations = ["implementation", "testImplementation"]
|
||||
}
|
||||
|
||||
application {
|
||||
setMainModule("MaterialFX.Demo")
|
||||
mainModule = "MaterialFX.Demo"
|
||||
String main = project.findProperty("chooseMain").toString()
|
||||
if (main != "null" && !main.trim().isEmpty()) {
|
||||
setMainClassName(main)
|
||||
mainClass = main
|
||||
} else {
|
||||
setMainClassName("io.github.palexdev.materialfx.demo.Demo")
|
||||
mainClass = "io.github.palexdev.materialfx.demo.Demo"
|
||||
}
|
||||
applicationDefaultJvmArgs = ["-Dglass.disableGrab=true"]
|
||||
}
|
||||
@ -66,26 +62,40 @@ jlink {
|
||||
mainClass = "io.github.palexdev.materialfx.demo.Demo"
|
||||
name = 'MaterialFX Demo'
|
||||
}
|
||||
|
||||
addExtraDependencies("javafx")
|
||||
|
||||
jpackage {
|
||||
imageOptions = ['--icon', 'src/main/resources/logo.ico']
|
||||
imageOptions = ['--icon', 'src/main/resources/io/github/palexdev/materialfx/demo/logo.ico']
|
||||
}
|
||||
|
||||
targetPlatform("linux-x64") {
|
||||
jdkHome = jdkDownload("https://cdn.azul.com/zulu/bin/zulu19.32.13-ca-jdk19.0.2-linux_x64.tar.gz")
|
||||
addExtraModulePath("/home/palexdev/Documents/JavaFX_jmods/linux_x64")
|
||||
def jmodBasePath = Paths.get(rootProject.projectDir.getAbsolutePath()).resolve("jfx-jmods").toString()
|
||||
targetPlatform("linux") {
|
||||
jdkHome = jdkDownload("https://cdn.azul.com/zulu/bin/zulu21.38.21-ca-jdk21.0.5-linux_x64.tar.gz")
|
||||
addExtraModulePath("$jmodBasePath/linux-$jfx")
|
||||
|
||||
jpackage {
|
||||
targetPlatformName = "linux"
|
||||
}
|
||||
}
|
||||
|
||||
targetPlatform("win") {
|
||||
jdkHome = jdkDownload("https://cdn.azul.com/zulu/bin/zulu19.32.13-ca-jdk19.0.2-win_x64.zip")
|
||||
addExtraModulePath("/home/palexdev/Documents/JavaFX_jmods/win_x64")
|
||||
jdkHome = jdkDownload("https://cdn.azul.com/zulu/bin/zulu21.38.21-ca-jdk21.0.5-win_x64.zip")
|
||||
addExtraModulePath("$jmodBasePath/win-$jfx")
|
||||
|
||||
jpackage {
|
||||
targetPlatformName = "win"
|
||||
}
|
||||
}
|
||||
|
||||
targetPlatform("mac") {
|
||||
jdkHome = jdkDownload("https://cdn.azul.com/zulu/bin/zulu19.32.13-ca-jdk19.0.2-macosx_x64.tar.gz")
|
||||
addExtraModulePath("/home/palexdev/Documents/JavaFX_jmods/mac_x64")
|
||||
}
|
||||
jdkHome = jdkDownload("https://cdn.azul.com/zulu/bin/zulu21.38.21-ca-jdk21.0.5-macosx_x64.tar.gz")
|
||||
addExtraModulePath("$jmodBasePath/mac-$jfx")
|
||||
|
||||
addExtraDependencies('javafx')
|
||||
jpackage {
|
||||
targetPlatformName = "mac"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('doPackageAll') {
|
||||
|
@ -18,8 +18,10 @@
|
||||
|
||||
package io.github.palexdev.materialfx.demo.controllers;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXTableColumn;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableRow;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableView;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXTableRowCell;
|
||||
import io.github.palexdev.materialfx.filter.StringFilter;
|
||||
@ -35,9 +37,6 @@ import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
public class FontResourcesController implements Initializable {
|
||||
private final ObservableList<IconDescriptor> fontResources;
|
||||
|
||||
@ -67,12 +66,13 @@ public class FontResourcesController implements Initializable {
|
||||
|
||||
private void handleProvider(IconDescriptor desc) {
|
||||
if (desc.getClass() == current) return;
|
||||
if (desc instanceof FontAwesomeSolid) {
|
||||
icon.setIconsProvider(IconsProviders.FONTAWESOME_SOLID);
|
||||
} else if (desc instanceof FontAwesomeRegular) {
|
||||
icon.setIconsProvider(IconsProviders.FONTAWESOME_REGULAR);
|
||||
} else if (desc instanceof FontAwesomeBrands) {
|
||||
icon.setIconsProvider(IconsProviders.FONTAWESOME_BRANDS);
|
||||
switch (desc) {
|
||||
case FontAwesomeSolid fontAwesomeSolid -> icon.setIconsProvider(IconsProviders.FONTAWESOME_SOLID);
|
||||
case FontAwesomeRegular fontAwesomeRegular ->
|
||||
icon.setIconsProvider(IconsProviders.FONTAWESOME_REGULAR);
|
||||
case FontAwesomeBrands fontAwesomeBrands ->
|
||||
icon.setIconsProvider(IconsProviders.FONTAWESOME_BRANDS);
|
||||
default -> {}
|
||||
}
|
||||
current = desc.getClass();
|
||||
}
|
||||
@ -92,15 +92,10 @@ public class FontResourcesController implements Initializable {
|
||||
});
|
||||
codeColumn.setRowCellFactory(resource -> new MFXTableRowCell<>(IconDescriptor::getCode, character -> Integer.toHexString(character | 0x10000).substring(1).toUpperCase()));
|
||||
|
||||
tableView.setTableRowFactory(resource -> new MFXTableRow<>(tableView, resource) {{
|
||||
setPrefHeight(48);
|
||||
}});
|
||||
tableView.getTableColumns().addAll(iconColumn, descriptionColumn, codeColumn);
|
||||
tableView.setRowsHeight(48.0);
|
||||
//tableView.getColumns().addAll(iconColumn, descriptionColumn, codeColumn);
|
||||
tableView.getFilters().add(new StringFilter<>("Description", IconDescriptor::getDescription));
|
||||
tableView.setItems(fontResources);
|
||||
tableView.features().enableBounceEffect();
|
||||
tableView.features().enableSmoothScrolling(0.7);
|
||||
tableView.autosizeColumnsOnInitialization();
|
||||
tableView.getItems().setAll(fontResources);
|
||||
|
||||
header.setText("MaterialFX Font Resources (" + fontResources.size() + " in total)");
|
||||
}
|
||||
|
@ -18,37 +18,40 @@
|
||||
|
||||
package io.github.palexdev.materialfx.demo.controllers;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXCheckListView;
|
||||
import io.github.palexdev.materialfx.controls.MFXListView;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXCheckListCell;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXListCell;
|
||||
import io.github.palexdev.materialfx.controls.legacy.MFXLegacyListCell;
|
||||
import io.github.palexdev.materialfx.controls.legacy.MFXLegacyListView;
|
||||
import io.github.palexdev.materialfx.demo.model.Model;
|
||||
import io.github.palexdev.materialfx.demo.model.Person;
|
||||
import io.github.palexdev.materialfx.effects.DepthLevel;
|
||||
import io.github.palexdev.mfxresources.fonts.MFXFontIcon;
|
||||
import io.github.palexdev.materialfx.utils.ColorUtils;
|
||||
import io.github.palexdev.materialfx.utils.others.FunctionalStringConverter;
|
||||
import io.github.palexdev.mfxeffects.enums.ElevationLevel;
|
||||
import io.github.palexdev.mfxresources.fonts.MFXFontIcon;
|
||||
import io.github.palexdev.virtualizedfx.cells.base.VFXCell;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class ListViewsController implements Initializable {
|
||||
|
||||
@FXML
|
||||
private MFXListView<String> list;
|
||||
private MFXListView<String, VFXCell<String>> list;
|
||||
|
||||
@FXML
|
||||
private MFXListView<Person> custList;
|
||||
private MFXListView<Person, VFXCell<Person>> custList;
|
||||
|
||||
@FXML
|
||||
private MFXCheckListView<String> checkList;
|
||||
private MFXCheckListView<String, VFXCell<String>> checkList;
|
||||
|
||||
@FXML
|
||||
private MFXListView<Person> legacyList;
|
||||
private MFXLegacyListView<Person> legacyList;
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
@ -57,45 +60,42 @@ public class ListViewsController implements Initializable {
|
||||
StringConverter<Person> converter = FunctionalStringConverter.to(person -> (person == null) ? "" : person.getName() + " " + person.getSurname());
|
||||
|
||||
list.setItems(strings);
|
||||
list.setCellFactory(MFXListCell::new);
|
||||
|
||||
custList.setItems(people);
|
||||
custList.setCellFactory(p -> new PersonCellFactory(p).setConverter(converter));
|
||||
|
||||
checkList.setItems(strings);
|
||||
custList.setConverter(converter);
|
||||
custList.setCellFactory(person -> new PersonCellFactory(custList, person));
|
||||
custList.features().enableBounceEffect();
|
||||
custList.features().enableSmoothScrolling(0.5);
|
||||
checkList.setCellFactory(MFXCheckListCell::new);
|
||||
|
||||
legacyList.setItems(people);
|
||||
legacyList.setConverter(converter);
|
||||
}
|
||||
|
||||
@FXML
|
||||
void changeColors(ActionEvent event) {
|
||||
custList.setTrackColor(ColorUtils.getRandomColor());
|
||||
custList.setThumbColor(ColorUtils.getRandomColor());
|
||||
custList.setThumbHoverColor(ColorUtils.getRandomColor());
|
||||
legacyList.setCellFactory(p -> new MFXLegacyListCell<>() {
|
||||
@Override
|
||||
protected void updateItem(Person item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
setText(item != null ? converter.toString(item) : "");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
void changeDepth(ActionEvent event) {
|
||||
DepthLevel newLevel = (custList.getDepthLevel() == DepthLevel.LEVEL0) ? DepthLevel.LEVEL2 : DepthLevel.LEVEL0;
|
||||
custList.setDepthLevel(newLevel);
|
||||
ElevationLevel depth = list.getDepth();
|
||||
ElevationLevel next = depth == ElevationLevel.LEVEL0 ? ElevationLevel.LEVEL3 : ElevationLevel.LEVEL0;
|
||||
|
||||
list.setDepth(next);
|
||||
custList.setDepth(next);
|
||||
checkList.setDepth(next);
|
||||
}
|
||||
|
||||
private static class PersonCellFactory extends MFXListCell<Person> {
|
||||
private final MFXFontIcon userIcon;
|
||||
|
||||
public PersonCellFactory(MFXListView<Person> listView, Person data) {
|
||||
super(listView, data);
|
||||
public PersonCellFactory(Person data) {
|
||||
super(data);
|
||||
|
||||
userIcon = new MFXFontIcon("fas-user", 18);
|
||||
MFXFontIcon userIcon = new MFXFontIcon("fas-user", 18);
|
||||
userIcon.getStyleClass().add("user-icon");
|
||||
render(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(Person data) {
|
||||
super.render(data);
|
||||
if (userIcon != null) getChildren().add(0, userIcon);
|
||||
setGraphic(userIcon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import io.github.palexdev.materialfx.controls.MFXButton;
|
||||
import io.github.palexdev.materialfx.controls.MFXIconWrapper;
|
||||
import io.github.palexdev.materialfx.controls.MFXNotificationCenter;
|
||||
import io.github.palexdev.materialfx.controls.MFXSimpleNotification;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXNotificationCell;
|
||||
import io.github.palexdev.materialfx.demo.MFXDemoResourcesLoader;
|
||||
import io.github.palexdev.materialfx.demo.model.Model;
|
||||
import io.github.palexdev.materialfx.enums.NotificationPos;
|
||||
@ -59,11 +58,7 @@ public class NotificationsController {
|
||||
MFXNotificationCenterSystem.instance().initOwner(stage);
|
||||
|
||||
MFXNotificationCenter center = MFXNotificationCenterSystem.instance().getCenter();
|
||||
center.setCellFactory(notification -> new MFXNotificationCell(center, notification) {
|
||||
{
|
||||
setPrefHeight(400);
|
||||
}
|
||||
});
|
||||
center.setCellHeight(400.0);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -18,24 +18,15 @@
|
||||
|
||||
package io.github.palexdev.materialfx.demo.controllers;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXPaginatedTableView;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableColumn;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableView;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXTableRowCell;
|
||||
import io.github.palexdev.materialfx.demo.model.Device;
|
||||
import io.github.palexdev.materialfx.demo.model.Model;
|
||||
import io.github.palexdev.materialfx.demo.model.Person;
|
||||
import io.github.palexdev.materialfx.filter.EnumFilter;
|
||||
import io.github.palexdev.materialfx.filter.IntegerFilter;
|
||||
import io.github.palexdev.materialfx.filter.StringFilter;
|
||||
import io.github.palexdev.materialfx.utils.others.observables.When;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.geometry.Pos;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Comparator;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class TableViewsController implements Initializable {
|
||||
|
||||
@ -47,7 +38,7 @@ public class TableViewsController implements Initializable {
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
setupTable();
|
||||
/* setupTable();
|
||||
setupPaginated();
|
||||
|
||||
table.autosizeColumnsOnInitialization();
|
||||
@ -55,10 +46,10 @@ public class TableViewsController implements Initializable {
|
||||
|
||||
When.onChanged(paginated.currentPageProperty())
|
||||
.then((oldValue, newValue) -> paginated.autosizeColumns())
|
||||
.listen();
|
||||
.listen();*/
|
||||
}
|
||||
|
||||
private void setupTable() {
|
||||
/* private void setupTable() {
|
||||
MFXTableColumn<Person> nameColumn = new MFXTableColumn<>("Name", true, Comparator.comparing(Person::getName));
|
||||
MFXTableColumn<Person> surnameColumn = new MFXTableColumn<>("Surname", true, Comparator.comparing(Person::getSurname));
|
||||
MFXTableColumn<Person> ageColumn = new MFXTableColumn<>("Age", true, Comparator.comparing(Person::getAge));
|
||||
@ -104,5 +95,5 @@ public class TableViewsController implements Initializable {
|
||||
new EnumFilter<>("State", Device::getState, Device.State.class)
|
||||
);
|
||||
paginated.setItems(Model.devices);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ module MaterialFX.Demo {
|
||||
requires javafx.controls;
|
||||
requires javafx.fxml;
|
||||
requires javafx.graphics;
|
||||
requires javafx.media;
|
||||
|
||||
requires fr.brouillard.oss.cssfx;
|
||||
requires org.kordamp.ikonli.javafx;
|
||||
|
@ -20,19 +20,28 @@
|
||||
@import 'Common.css';
|
||||
@import 'MFXColors.css';
|
||||
|
||||
.mfx-list-view .virtual-flow .mfx-list-cell .data-label {
|
||||
.vfx-scroll-pane {
|
||||
-vfx-smooth-scroll: true;
|
||||
-vfx-layout-mode: COMPACT;
|
||||
}
|
||||
|
||||
.vfx-scroll-pane .viewport {
|
||||
-fx-padding: 4px;
|
||||
}
|
||||
|
||||
.mfx-list-view .mfx-list-cell .data-label {
|
||||
-fx-font-family: 'Open Sans Regular';
|
||||
}
|
||||
|
||||
.mfx-list-view .virtual-flow .mfx-list-cell:selected .data-label {
|
||||
.mfx-list-view .mfx-list-cell:selected .data-label {
|
||||
-fx-font-family: 'Open Sans SemiBold';
|
||||
}
|
||||
|
||||
.mfx-check-list-view .virtual-flow .mfx-check-list-cell .data-label {
|
||||
.mfx-check-list-view .mfx-check-list-cell .data-label {
|
||||
-fx-font-family: 'Open Sans Regular';
|
||||
}
|
||||
|
||||
.mfx-check-list-view .virtual-flow .mfx-check-list-cell:selected .data-label {
|
||||
.mfx-check-list-view .mfx-check-list-cell:selected .data-label {
|
||||
-fx-font-family: 'Open Sans SemiBold';
|
||||
}
|
||||
|
||||
@ -41,43 +50,34 @@
|
||||
-fx-background-radius: 10;
|
||||
-fx-border-color: -mfx-purple;
|
||||
-fx-border-radius: 10;
|
||||
-fx-padding: 4;
|
||||
}
|
||||
|
||||
#custList .virtual-flow {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
#custList:focused {
|
||||
-fx-border-color: -mfx-purple;
|
||||
}
|
||||
|
||||
#custList .virtual-flow .mfx-list-cell {
|
||||
#custList .mfx-list-cell {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-color: transparent;
|
||||
-fx-background-radius: 10;
|
||||
-fx-border-radius: 10;
|
||||
}
|
||||
|
||||
#custList .virtual-flow .mfx-list-cell .data-label {
|
||||
#custList .mfx-list-cell .label {
|
||||
-fx-text-fill: white;
|
||||
-fx-padding: 2px;
|
||||
}
|
||||
|
||||
#custList .virtual-flow .mfx-list-cell .user-icon {
|
||||
#custList .mfx-list-cell .user-icon {
|
||||
-mfx-color: white;
|
||||
}
|
||||
|
||||
#custList .virtual-flow .mfx-list-cell:hover,
|
||||
#custList .virtual-flow .mfx-list-cell:selected {
|
||||
#custList .mfx-list-cell:hover,
|
||||
#custList .mfx-list-cell:selected {
|
||||
-fx-background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
#custList .virtual-flow .mfx-list-cell .mfx-ripple-generator {
|
||||
-mfx-auto-clip: true;
|
||||
}
|
||||
|
||||
#custList .virtual-flow .mfx-list-cell:selected .mfx-ripple-generator {
|
||||
-mfx-paused: true;
|
||||
#custList .mfx-list-cell .mfx-ripple-generator {
|
||||
-mfx-ripple-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.mfx-button {
|
||||
|
@ -18,7 +18,11 @@
|
||||
~ along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<?import io.github.palexdev.materialfx.controls.*?>
|
||||
<?import io.github.palexdev.materialfx.controls.legacy.MFXLegacyListView?>
|
||||
<?import io.github.palexdev.materialfx.controls.MFXButton?>
|
||||
<?import io.github.palexdev.materialfx.controls.MFXCheckListView?>
|
||||
<?import io.github.palexdev.materialfx.controls.MFXListView?>
|
||||
<?import io.github.palexdev.virtualizedfx.controls.VFXScrollPane?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
@ -54,31 +58,34 @@
|
||||
<Label alignment="CENTER" maxWidth="1.7976931348623157E308" styleClass="header-label" text="Lists"
|
||||
GridPane.columnSpan="3"/>
|
||||
<Label styleClass="sub-header-label" text="Default" GridPane.rowIndex="1"/>
|
||||
<MFXListView fx:id="list" prefWidth="170.0" GridPane.rowIndex="2" GridPane.rowSpan="3">
|
||||
<VFXScrollPane prefWidth="170.0" GridPane.rowIndex="2" GridPane.rowSpan="3">
|
||||
<content>
|
||||
<MFXListView fx:id="list"/>
|
||||
</content>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="15.0"/>
|
||||
</GridPane.margin>
|
||||
</MFXListView>
|
||||
</VFXScrollPane>
|
||||
<Label styleClass="sub-header-label" text="Custom" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||
<MFXListView id="custList" fx:id="custList" prefWidth="170.0" GridPane.columnIndex="1" GridPane.rowIndex="2"
|
||||
GridPane.rowSpan="3">
|
||||
<VFXScrollPane prefWidth="170.0" GridPane.columnIndex="1" GridPane.rowIndex="2"
|
||||
GridPane.rowSpan="3">
|
||||
<content>
|
||||
<MFXListView id="custList" fx:id="custList"/>
|
||||
</content>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="15.0"/>
|
||||
</GridPane.margin>
|
||||
</MFXListView>
|
||||
</VFXScrollPane>
|
||||
<Label styleClass="sub-header-label" text="Check List" GridPane.columnIndex="2" GridPane.rowIndex="1"/>
|
||||
<MFXCheckListView fx:id="checkList" prefWidth="170.0" GridPane.columnIndex="2" GridPane.rowIndex="2"
|
||||
GridPane.rowSpan="3">
|
||||
<VFXScrollPane prefWidth="170.0" GridPane.columnIndex="2" GridPane.rowIndex="2"
|
||||
GridPane.rowSpan="3">
|
||||
<content>
|
||||
<MFXCheckListView fx:id="checkList"/>
|
||||
</content>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="15.0"/>
|
||||
</GridPane.margin>
|
||||
</MFXCheckListView>
|
||||
<MFXButton minHeight="32.0" onAction="#changeColors" text="Change Scrollbar Colors" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="5">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="20.0"/>
|
||||
</GridPane.margin>
|
||||
</MFXButton>
|
||||
</VFXScrollPane>
|
||||
<StackPane prefHeight="150.0" prefWidth="200.0" styleClass="grid-background" GridPane.columnIndex="4"
|
||||
GridPane.rowSpan="2147483647">
|
||||
<GridPane.margin>
|
||||
@ -86,12 +93,12 @@
|
||||
</GridPane.margin>
|
||||
</StackPane>
|
||||
<Label styleClass="header-label" text="Legacy" GridPane.columnIndex="4"/>
|
||||
<MFXListView fx:id="legacyList" prefWidth="170.0" GridPane.columnIndex="4" GridPane.rowIndex="2"
|
||||
GridPane.rowSpan="3">
|
||||
<MFXLegacyListView fx:id="legacyList" prefWidth="170.0" GridPane.columnIndex="4" GridPane.rowIndex="2"
|
||||
GridPane.rowSpan="3">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="15.0"/>
|
||||
</GridPane.margin>
|
||||
</MFXListView>
|
||||
</MFXLegacyListView>
|
||||
<MFXButton minHeight="32.0" onAction="#changeDepth" text="3D/2D" GridPane.columnIndex="1" GridPane.rowIndex="6">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="20.0"/>
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 113 KiB |
@ -3,6 +3,6 @@ import javafx.application.Application;
|
||||
public class Launcher {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Application.launch(Reproducer.class, args);
|
||||
Application.launch(Playground.class, args);
|
||||
}
|
||||
}
|
||||
|
@ -1,59 +0,0 @@
|
||||
import io.github.palexdev.materialfx.controls.MFXTableColumn;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableView;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXTableRowCell;
|
||||
import io.github.palexdev.materialfx.demo.model.Model;
|
||||
import io.github.palexdev.materialfx.demo.model.Person;
|
||||
import io.github.palexdev.materialfx.filter.IntegerFilter;
|
||||
import io.github.palexdev.materialfx.filter.StringFilter;
|
||||
import io.github.palexdev.materialfx.theming.CSSFragment;
|
||||
import io.github.palexdev.materialfx.theming.JavaFXThemes;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.materialfx.theming.UserAgentBuilder;
|
||||
import io.github.palexdev.mfxcore.builders.InsetsBuilder;
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
public class Reproducer extends Application {
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) throws Exception {
|
||||
StackPane pane = new StackPane();
|
||||
pane.setPadding(InsetsBuilder.all(10));
|
||||
|
||||
MFXTableView<Person> table = new MFXTableView<>(Model.people);
|
||||
MFXTableColumn<Person> name = new MFXTableColumn<>("Name");
|
||||
name.setRowCellFactory(p -> new MFXTableRowCell<>(Person::getName));
|
||||
MFXTableColumn<Person> surname = new MFXTableColumn<>("Surname");
|
||||
surname.setRowCellFactory(p -> new MFXTableRowCell<>(Person::getSurname));
|
||||
MFXTableColumn<Person> age = new MFXTableColumn<>("Age");
|
||||
age.setRowCellFactory(p -> new MFXTableRowCell<>(Person::getAge));
|
||||
table.getTableColumns().addAll(name, surname, age);
|
||||
pane.getChildren().add(table);
|
||||
table.setMinSize(400.0, 400.0);
|
||||
|
||||
table.getFilters().addAll(
|
||||
new StringFilter<>("Name", Person::getName),
|
||||
new StringFilter<>("Surname", Person::getSurname),
|
||||
new IntegerFilter<>("Age", Person::getAge)
|
||||
);
|
||||
|
||||
CSSFragment.Builder.build()
|
||||
.addSelector(".mfx-filter-pane")
|
||||
.addStyle("-mfx-main: blue")
|
||||
.closeSelector()
|
||||
.applyOn(table);
|
||||
|
||||
UserAgentBuilder.builder()
|
||||
.themes(JavaFXThemes.MODENA)
|
||||
.themes(MaterialFXStylesheets.forAssemble(false))
|
||||
.setResolveAssets(true)
|
||||
.setDeploy(true)
|
||||
.build()
|
||||
.setGlobal();
|
||||
Scene scene = new Scene(pane, 600, 600);
|
||||
stage.setScene(scene);
|
||||
stage.show();
|
||||
}
|
||||
}
|
48
demo/src/test/java/collections/TransformableListTest.java → demo/src/test/java/collections/RefineListTest.java
Executable file → Normal file
48
demo/src/test/java/collections/TransformableListTest.java → demo/src/test/java/collections/RefineListTest.java
Executable file → Normal file
@ -1,6 +1,8 @@
|
||||
package collections;
|
||||
|
||||
import io.github.palexdev.materialfx.collections.TransformableList;
|
||||
import java.util.Comparator;
|
||||
|
||||
import io.github.palexdev.materialfx.collections.RefineList;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
@ -9,36 +11,34 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.testfx.framework.junit5.ApplicationExtension;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ExtendWith(ApplicationExtension.class)
|
||||
public class TransformableListTest {
|
||||
public class RefineListTest {
|
||||
private final ObservableList<String> source = FXCollections.observableArrayList("A", "B", "C", "D", "E");
|
||||
|
||||
@Test
|
||||
public void sortTest1() {
|
||||
TransformableList<String> transformed = new TransformableList<>(source);
|
||||
transformed.setComparator(Comparator.reverseOrder(), true);
|
||||
RefineList<String> transformed = new RefineList<>(source);
|
||||
transformed.setComparator(Comparator.reverseOrder());
|
||||
|
||||
assertEquals(transformed.get(4), "A");
|
||||
assertEquals(transformed.indexOf("E"), 0);
|
||||
assertEquals(transformed.viewToSource(0), 4);
|
||||
assertEquals(transformed.sourceToView(0), 4);
|
||||
assertEquals("A", transformed.getView().get(4));
|
||||
assertEquals(0, transformed.getView().indexOf("E"));
|
||||
assertEquals(4, transformed.viewToSource(0));
|
||||
assertEquals(4, transformed.sourceToView(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sortAndFilterTest1() {
|
||||
TransformableList<String> transformed = new TransformableList<>(source);
|
||||
transformed.setComparator(Comparator.reverseOrder(), true);
|
||||
RefineList<String> transformed = new RefineList<>(source);
|
||||
transformed.setComparator(Comparator.reverseOrder());
|
||||
transformed.setPredicate(s -> s.equals("A") || s.equals("C") || s.equals("E"));
|
||||
|
||||
assertThrows(IndexOutOfBoundsException.class, () -> transformed.get(4));
|
||||
assertEquals(transformed.get(1), "C");
|
||||
assertEquals(transformed.indexOf("E"), 0);
|
||||
assertEquals(transformed.viewToSource(1), 2);
|
||||
assertEquals(transformed.sourceToView(1), -1);
|
||||
assertThrows(IndexOutOfBoundsException.class, () -> transformed.getView().get(4));
|
||||
assertEquals("C", transformed.getView().get(1));
|
||||
assertEquals(0, transformed.getView().indexOf("E"));
|
||||
assertEquals(2, transformed.viewToSource(1));
|
||||
assertTrue(transformed.sourceToView(1) < 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -46,10 +46,10 @@ public class TransformableListTest {
|
||||
SortedList<String> sorted = new SortedList<>(source);
|
||||
sorted.setComparator(Comparator.reverseOrder());
|
||||
|
||||
assertEquals(sorted.get(4), "A");
|
||||
assertEquals(sorted.indexOf("E"), 0);
|
||||
assertEquals(sorted.getSourceIndex(0), 4);
|
||||
assertEquals(sorted.getViewIndex(0), 4);
|
||||
assertEquals("A", sorted.get(4));
|
||||
assertEquals(0, sorted.indexOf("E"));
|
||||
assertEquals(4, sorted.getSourceIndex(0));
|
||||
assertEquals(4, sorted.getViewIndex(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -61,9 +61,9 @@ public class TransformableListTest {
|
||||
filtered.setPredicate(s -> s.equals("A") || s.equals("C") || s.equals("E"));
|
||||
|
||||
assertThrows(IndexOutOfBoundsException.class, () -> filtered.get(4));
|
||||
assertEquals(filtered.get(1), "C");
|
||||
assertEquals(filtered.indexOf("E"), 0);
|
||||
assertEquals(filtered.getSourceIndex(1), 2);
|
||||
assertEquals("C", filtered.get(1));
|
||||
assertEquals(0, filtered.indexOf("E"));
|
||||
assertEquals(2, filtered.getSourceIndex(1));
|
||||
assertTrue(filtered.getViewIndex(1) < 0);
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package combobox;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXFilterComboBox;
|
||||
import javafx.application.Application;
|
||||
import javafx.collections.FXCollections;
|
||||
@ -11,8 +13,6 @@ import javafx.scene.layout.HBox;
|
||||
import javafx.stage.Stage;
|
||||
import org.scenicview.ScenicView;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class ComboBoxTest extends Application {
|
||||
|
||||
@Override
|
||||
@ -34,7 +34,7 @@ public class ComboBoxTest extends Application {
|
||||
MFXFilterComboBox<String> c3 = new MFXFilterComboBox<>(s3);
|
||||
|
||||
c2.valueProperty().bind(c1.valueProperty());
|
||||
c3.getSelectionModel().bindIndexBidirectional(c1.getSelectionModel());
|
||||
c3.selection().bindBidirectional(c1.selection());
|
||||
|
||||
hBox.getChildren().addAll(c1, c2, c3);
|
||||
Scene scene = new Scene(hBox, 800, 800);
|
||||
|
@ -18,39 +18,41 @@
|
||||
|
||||
package selection;
|
||||
|
||||
import io.github.palexdev.materialfx.selection.MultipleSelectionModel;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import io.github.palexdev.materialfx.selection.SelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.base.ISelectionModel;
|
||||
import io.github.palexdev.materialfx.utils.FXCollectors;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class MultipleSelectionModelTests {
|
||||
private final ObservableList<String> strings = IntStream.rangeClosed(0, 30)
|
||||
.mapToObj(i -> "String " + i)
|
||||
.collect(FXCollectors.toList());
|
||||
private final MultipleSelectionModel<String> selectionModel = new MultipleSelectionModel<String>(strings);
|
||||
private final ISelectionModel<String> selectionModel = new SelectionModel<>(strings);
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
selectionModel.setAllowsMultipleSelection(true);
|
||||
selectionModel.clearSelection();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrder() {
|
||||
Integer[] toSelect = {0, 6, 3, 9, 6};
|
||||
selectionModel.selectIndexes(List.of(toSelect));
|
||||
selectionModel.selectIndexes(toSelect);
|
||||
|
||||
assertEquals(4, selectionModel.getSelection().size());
|
||||
assertEquals(4, selectionModel.selection().size());
|
||||
|
||||
// Indexes
|
||||
int i = 0;
|
||||
Set<Integer> indexes = selectionModel.getSelection().keySet();
|
||||
Set<Integer> indexes = selectionModel.selection().keySet();
|
||||
for (int val : indexes) {
|
||||
assertEquals(toSelect[i], val);
|
||||
i++;
|
||||
@ -64,7 +66,7 @@ public class MultipleSelectionModelTests {
|
||||
"String 3",
|
||||
"String 9"
|
||||
};
|
||||
List<String> values = selectionModel.getSelectedValues();
|
||||
List<String> values = selectionModel.getSelectedItems();
|
||||
for (String value : values) {
|
||||
assertEquals(expected[i], value);
|
||||
i++;
|
||||
|
@ -3,8 +3,10 @@ package selection;
|
||||
import io.github.palexdev.materialfx.bindings.BiBindingManager;
|
||||
import io.github.palexdev.materialfx.bindings.BindingManager;
|
||||
import io.github.palexdev.materialfx.demo.model.Person;
|
||||
import io.github.palexdev.materialfx.selection.SingleSelectionModel;
|
||||
import javafx.beans.property.*;
|
||||
import io.github.palexdev.materialfx.selection.SelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.base.ISelectionModel;
|
||||
import javafx.beans.property.ListProperty;
|
||||
import javafx.beans.property.SimpleListProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@ -12,8 +14,6 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.testfx.framework.junit5.ApplicationExtension;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ExtendWith(ApplicationExtension.class)
|
||||
@ -43,8 +43,8 @@ public class SingleSelectionModelTests {
|
||||
new Person("Phil"))
|
||||
);
|
||||
|
||||
private final SingleSelectionModel<Person> model1 = new SingleSelectionModel<>(people1);
|
||||
private final SingleSelectionModel<Person> model2 = new SingleSelectionModel<>(people2);
|
||||
private final ISelectionModel<Person> model1 = new SelectionModel<>(people1);
|
||||
private final ISelectionModel<Person> model2 = new SelectionModel<>(people2);
|
||||
|
||||
@AfterEach
|
||||
public void checkManagers() {
|
||||
@ -61,26 +61,26 @@ public class SingleSelectionModelTests {
|
||||
@Test
|
||||
public void testIndexSelection1() {
|
||||
model1.selectIndex(0);
|
||||
assertEquals(0, model1.getSelectedIndex());
|
||||
assertEquals(0, model1.getSelectedEntry().getKey());
|
||||
assertEquals("Jack", model1.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexSelection2() {
|
||||
assertThrows(IndexOutOfBoundsException.class, () -> model1.selectIndex(10));
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedEntry());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndexSelection3() {
|
||||
assertThrows(IndexOutOfBoundsException.class, () -> model1.selectIndex(-1));
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedEntry());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testItemSelection1() {
|
||||
model1.selectItem(new Person("Mark"));
|
||||
assertEquals(1, model1.getSelectedIndex());
|
||||
assertEquals(1, model1.getSelectedEntry().getKey());
|
||||
assertEquals("Mark", model1.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@ -90,441 +90,10 @@ public class SingleSelectionModelTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndexProperty1() {
|
||||
IntegerProperty property = new SimpleIntegerProperty();
|
||||
model1.bindIndex(property, i -> model1.getUnmodifiableItems().get(i));
|
||||
property.set(2);
|
||||
assertEquals(2, model1.getSelectedIndex());
|
||||
assertEquals("Linda", model1.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndexProperty2() {
|
||||
IntegerProperty property = new SimpleIntegerProperty();
|
||||
model1.bindIndex(property, i -> model1.getUnmodifiableItems().get(i));
|
||||
assertThrows(IllegalStateException.class, () -> model1.selectIndex(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndexProperty3() {
|
||||
AtomicReference<Throwable> ex = new AtomicReference<>();
|
||||
IntegerProperty property = new SimpleIntegerProperty();
|
||||
model1.bindIndex(property, i -> {
|
||||
try {
|
||||
return model1.getUnmodifiableItems().get(i);
|
||||
} catch (Exception exception) {
|
||||
ex.set(exception);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
property.set(6);
|
||||
assertNotNull(ex.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndexProperty4() {
|
||||
model1.selectIndex(0);
|
||||
|
||||
AtomicReference<Throwable> ex = new AtomicReference<>();
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> ex.set(e));
|
||||
|
||||
IntegerProperty property = new SimpleIntegerProperty();
|
||||
model1.bindIndex(property, i -> model1.getUnmodifiableItems().get(i));
|
||||
property.set(-1);
|
||||
|
||||
assertNotNull(ex.get());
|
||||
assertEquals(0, model1.getSelectedIndex());
|
||||
assertEquals("Jack", model1.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndex1() {
|
||||
model1.bindIndex(model2);
|
||||
model2.selectIndex(2);
|
||||
|
||||
assertEquals(2, model1.getSelectedIndex());
|
||||
assertEquals(2, model2.getSelectedIndex());
|
||||
|
||||
assertEquals("Linda", model1.getSelectedItem().getName());
|
||||
assertEquals("Alex", model2.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndex2() {
|
||||
model1.bindIndex(model2);
|
||||
assertThrows(IllegalStateException.class, () -> model1.selectIndex(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndex3() {
|
||||
AtomicReference<Throwable> ex = new AtomicReference<>();
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> ex.set(e));
|
||||
|
||||
model1.bindIndex(model2);
|
||||
model2.selectIndex(8);
|
||||
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
assertEquals(8, model2.getSelectedIndex());
|
||||
assertEquals("Alex", model2.getSelectedItem().getName());
|
||||
|
||||
assertNotNull(ex.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndex4() {
|
||||
model1.bindIndex(model2);
|
||||
model2.selectIndex(0);
|
||||
assertThrows(IndexOutOfBoundsException.class, () -> model2.selectIndex(-1));
|
||||
|
||||
assertEquals(0, model1.getSelectedIndex());
|
||||
assertEquals("Jack", model1.getSelectedItem().getName());
|
||||
assertEquals(0, model2.getSelectedIndex());
|
||||
assertEquals("Mark", model2.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndexBidirectionalProperty1() {
|
||||
IntegerProperty property = new SimpleIntegerProperty();
|
||||
model1.bindIndexBidirectional(property, i -> model1.getUnmodifiableItems().get(i), (clearing, i, other) -> other.setValue(i));
|
||||
|
||||
property.set(1);
|
||||
assertEquals(1, model1.getSelectedIndex());
|
||||
assertEquals("Mark", model1.getSelectedItem().getName());
|
||||
|
||||
model1.selectIndex(5);
|
||||
assertEquals("Sam", model1.getSelectedItem().getName());
|
||||
assertEquals(5, property.get());
|
||||
|
||||
model1.selectItem(new Person("Lily"));
|
||||
assertEquals(4, model1.getSelectedIndex());
|
||||
assertEquals(4, property.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndexBidirectionalProperty2() {
|
||||
IntegerProperty property = new SimpleIntegerProperty();
|
||||
model1.bindIndexBidirectional(property, i -> model1.getUnmodifiableItems().get(i), (clearing, i, other) -> other.setValue(i));
|
||||
|
||||
property.set(1);
|
||||
assertEquals(1, model1.getSelectedIndex());
|
||||
assertEquals("Mark", model1.getSelectedItem().getName());
|
||||
|
||||
property.set(-1);
|
||||
assertEquals(-1, property.get());
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndexBidirectionalProperty3() {
|
||||
IntegerProperty property = new SimpleIntegerProperty();
|
||||
model1.bindIndexBidirectional(property, i -> model1.getUnmodifiableItems().get(i), (clearing, i, other) -> other.setValue(i));
|
||||
|
||||
assertThrows(IndexOutOfBoundsException.class, () -> model1.selectIndex(8));
|
||||
assertEquals(0, property.get());
|
||||
assertEquals(0, model1.getSelectedIndex());
|
||||
assertEquals("Jack", model1.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndexBidirectional1() {
|
||||
model1.bindIndexBidirectional(model2);
|
||||
|
||||
model2.selectIndex(1);
|
||||
assertEquals("Mark", model1.getSelectedItem().getName());
|
||||
assertEquals("Roberto", model2.getSelectedItem().getName());
|
||||
|
||||
model1.selectIndex(3);
|
||||
assertEquals("Marty", model1.getSelectedItem().getName());
|
||||
assertEquals("Samantha", model2.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndexBidirectional2() {
|
||||
AtomicReference<Throwable> reference = new AtomicReference<>();
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
|
||||
model1.bindIndexBidirectional(model2);
|
||||
|
||||
model2.selectIndex(8);
|
||||
assertNotNull(reference.get());
|
||||
assertEquals("Alex", model2.getSelectedItem().getName());
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
|
||||
model1.selectIndex(0);
|
||||
assertEquals("Jack", model1.getSelectedItem().getName());
|
||||
assertEquals("Mark", model2.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindIndexBidirectional3() {
|
||||
AtomicReference<Throwable> reference = new AtomicReference<>();
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
|
||||
model2.bindIndexBidirectional(model1);
|
||||
|
||||
model1.selectIndex(0);
|
||||
assertEquals("Jack", model1.getSelectedItem().getName());
|
||||
assertEquals("Mark", model2.getSelectedItem().getName());
|
||||
|
||||
model2.selectIndex(8);
|
||||
assertNotNull(reference.get());
|
||||
assertEquals("Alex", model2.getSelectedItem().getName());
|
||||
assertEquals(0, model1.getSelectedIndex());
|
||||
assertEquals("Jack", model1.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItemProperty1() {
|
||||
ObjectProperty<Person> property = new SimpleObjectProperty<>();
|
||||
model1.bindItem(property, Person -> model1.getUnmodifiableItems().indexOf(Person));
|
||||
|
||||
property.set(new Person("Mark"));
|
||||
assertEquals(1, model1.getSelectedIndex());
|
||||
assertEquals("Mark", model1.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItemProperty2() {
|
||||
ObjectProperty<Person> property = new SimpleObjectProperty<>();
|
||||
model2.bindItem(property, Person -> model2.getUnmodifiableItems().indexOf(Person));
|
||||
|
||||
property.set(new Person("Alex"));
|
||||
assertEquals(2, model2.getSelectedIndex());
|
||||
assertEquals("Alex", model2.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItemProperty3() {
|
||||
AtomicReference<Throwable> reference = new AtomicReference<>();
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
|
||||
|
||||
ObjectProperty<Person> property = new SimpleObjectProperty<>();
|
||||
model2.bindItem(property, Person -> model2.getUnmodifiableItems().indexOf(Person));
|
||||
|
||||
property.set(new Person("Unexisting"));
|
||||
assertEquals("Unexisting", property.get().getName());
|
||||
assertEquals(-1, model2.getSelectedIndex());
|
||||
assertNull(model2.getSelectedItem());
|
||||
assertNotNull(reference.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItem1() {
|
||||
model1.bindItem(model2);
|
||||
model2.selectItem(new Person("Mark"));
|
||||
assertEquals(1, model1.getSelectedIndex());
|
||||
assertEquals(0, model2.getSelectedIndex());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItem2() {
|
||||
model1.bindItem(model2);
|
||||
assertThrows(IllegalStateException.class, () -> model1.selectItem(new Person("Mark")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItem3() {
|
||||
AtomicReference<Throwable> reference = new AtomicReference<>();
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
|
||||
|
||||
model1.bindItem(model2);
|
||||
model2.selectItem(new Person("Alex"));
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
assertEquals(2, model2.getSelectedIndex());
|
||||
assertEquals("Alex", model2.getSelectedItem().getName());
|
||||
assertNotNull(reference.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItemPropertyBidirectional1() {
|
||||
ObjectProperty<Person> property = new SimpleObjectProperty<>();
|
||||
model1.bindItemBidirectional(property, Person -> model1.getUnmodifiableItems().indexOf(Person), (clearing, Person, other) -> other.setValue(Person));
|
||||
|
||||
property.set(new Person("Linda"));
|
||||
assertEquals(2, model1.getSelectedIndex());
|
||||
assertEquals("Linda", model1.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItemPropertyBidirectional2() {
|
||||
AtomicReference<Throwable> reference = new AtomicReference<>();
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
|
||||
|
||||
ObjectProperty<Person> property = new SimpleObjectProperty<>();
|
||||
model1.bindItemBidirectional(property, Person -> model1.getUnmodifiableItems().indexOf(Person), (clearing, Person, other) -> other.setValue(Person));
|
||||
|
||||
property.set(new Person("Unexisting"));
|
||||
assertNotNull(reference.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItemPropertyBidirectional3() {
|
||||
ObjectProperty<Person> property = new SimpleObjectProperty<>();
|
||||
model1.bindItemBidirectional(property, Person -> model1.getUnmodifiableItems().indexOf(Person), (clearing, Person, other) -> other.setValue(Person));
|
||||
|
||||
property.set(new Person("Mark"));
|
||||
assertEquals(1, model1.getSelectedIndex());
|
||||
|
||||
model1.selectItem(new Person("Linda"));
|
||||
assertEquals(2, model1.getSelectedIndex());
|
||||
assertEquals("Linda", model1.getSelectedItem().getName());
|
||||
assertEquals("Linda", property.get().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItemBidirectional1() {
|
||||
model1.bindItemBidirectional(model2);
|
||||
|
||||
model2.selectItem(new Person("Mark"));
|
||||
assertEquals(1, model1.getSelectedIndex());
|
||||
assertEquals(0, model2.getSelectedIndex());
|
||||
|
||||
model1.selectItem(new Person("Sam"));
|
||||
assertEquals(5, model1.getSelectedIndex());
|
||||
assertEquals(6, model2.getSelectedIndex());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItemBidirectional2() {
|
||||
AtomicReference<Throwable> reference = new AtomicReference<>();
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
|
||||
model1.bindItemBidirectional(model2);
|
||||
|
||||
model1.selectItem(new Person("Sam"));
|
||||
assertEquals(5, model1.getSelectedIndex());
|
||||
assertEquals(6, model2.getSelectedIndex());
|
||||
|
||||
model2.selectItem(new Person("Alex"));
|
||||
assertNotNull(reference.get());
|
||||
assertEquals(5, model1.getSelectedIndex());
|
||||
assertEquals("Sam", model1.getSelectedItem().getName());
|
||||
assertEquals(2, model2.getSelectedIndex());
|
||||
assertEquals("Alex", model2.getSelectedItem().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindItemBidirectional3() {
|
||||
AtomicReference<Throwable> reference = new AtomicReference<>();
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> reference.set(e));
|
||||
model1.bindItemBidirectional(model2);
|
||||
|
||||
model2.selectItem(new Person("Alex"));
|
||||
assertNotNull(reference.get());
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
assertEquals(2, model2.getSelectedIndex());
|
||||
assertEquals("Alex", model2.getSelectedItem().getName());
|
||||
|
||||
model1.selectItem(new Person("Sam"));
|
||||
assertEquals(5, model1.getSelectedIndex());
|
||||
assertEquals(6, model2.getSelectedIndex());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearSelection1() {
|
||||
public void testClearSelection() {
|
||||
model1.selectIndex(0);
|
||||
model1.clearSelection();
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearSelection2() {
|
||||
model1.bindIndexBidirectional(model2);
|
||||
model1.selectIndex(0);
|
||||
model1.clearSelection();
|
||||
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
assertEquals(-1, model2.getSelectedIndex());
|
||||
assertNull(model2.getSelectedItem());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearSelection3() {
|
||||
model1.bindIndex(model2);
|
||||
model2.selectIndex(0);
|
||||
assertThrows(IllegalStateException.class, model1::clearSelection);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearSelection4() {
|
||||
model1.bindItemBidirectional(model2);
|
||||
model2.selectItem(new Person("Mark"));
|
||||
|
||||
assertEquals(1, model1.getSelectedIndex());
|
||||
assertEquals("Mark", model1.getSelectedItem().getName());
|
||||
assertEquals(0, model2.getSelectedIndex());
|
||||
assertEquals("Mark", model2.getSelectedItem().getName());
|
||||
|
||||
model1.clearSelection();
|
||||
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
assertEquals(-1, model2.getSelectedIndex());
|
||||
assertNull(model2.getSelectedItem());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearSelection5() {
|
||||
IntegerProperty property = new SimpleIntegerProperty();
|
||||
model1.bindIndexBidirectional(property, i -> model1.getUnmodifiableItems().get(i), (clearing, i, otherProperty) -> property.set(i));
|
||||
model1.selectIndex(1);
|
||||
|
||||
assertEquals(1, property.get());
|
||||
assertEquals("Mark", model1.getSelectedItem().getName());
|
||||
|
||||
model1.clearSelection();
|
||||
assertEquals(-1, property.get());
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
|
||||
model1.selectIndex(1);
|
||||
property.set(-1);
|
||||
assertEquals(-1, property.get());
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearSelection6() {
|
||||
ObjectProperty<Person> property = new SimpleObjectProperty<>();
|
||||
model1.bindItemBidirectional(property, v -> model1.getUnmodifiableItems().indexOf(v), (clearing, v, otherProperty) -> property.set(v));
|
||||
model1.selectIndex(1);
|
||||
|
||||
assertEquals("Mark", property.get().getName());
|
||||
assertEquals(1, model1.getSelectedIndex());
|
||||
|
||||
model1.clearSelection();
|
||||
assertNull(property.get());
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
|
||||
model1.selectIndex(1);
|
||||
property.set(null);
|
||||
assertNull(property.get());
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedItem());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearSelection7() {
|
||||
IntegerProperty property = new SimpleIntegerProperty();
|
||||
model1.bindIndexBidirectional(
|
||||
property,
|
||||
i -> model1.getUnmodifiableItems().get(i),
|
||||
(clearing, i, otherProperty) -> property.set(i)
|
||||
);
|
||||
model1.selectIndex(1);
|
||||
|
||||
assertEquals(1, property.get());
|
||||
assertEquals("Mark", model1.getSelectedItem().getName());
|
||||
|
||||
property.set(-1);
|
||||
assertEquals(-1, property.get());
|
||||
assertEquals(-1, model1.getSelectedIndex());
|
||||
assertNull(model1.getSelectedEntry());
|
||||
assertNull(model1.getSelectedItem());
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,25 @@
|
||||
#--------------------------------------#
|
||||
# Versions #
|
||||
#--------------------------------------#
|
||||
jdk=11
|
||||
testJdk=17
|
||||
materialfx=11.17.0
|
||||
jdk=21
|
||||
materialfx=21.18.0-alpha
|
||||
|
||||
# Plugins
|
||||
jfxPlugin=0.0.13
|
||||
jlink=2.26.0
|
||||
bnd=6.2.0
|
||||
mavenPublish=0.19.0
|
||||
shadowJarPlugin=7.1.2
|
||||
jfxPlugin=0.1.0
|
||||
jlink=3.1.1
|
||||
mavenPublish=0.28.0
|
||||
shadowJarPlugin=9.0.0-beta4
|
||||
|
||||
# Dependencies
|
||||
jfx=21
|
||||
mfxcore=11.8.0
|
||||
mfxresources=11.9.1
|
||||
vfx=11.9.6
|
||||
jfx=23.0.1
|
||||
mfxcore=11.11.4
|
||||
mfxresources=11.12.2
|
||||
vfx=21.7.9
|
||||
cssfx=11+
|
||||
ikonli=12.3.1
|
||||
|
||||
# Test Dependencies
|
||||
junit=5.9.1
|
||||
junit=5.11.4
|
||||
junitSuite=1.8.1
|
||||
testfx=4.0.16-alpha
|
||||
testfx=4.0.17
|
||||
scenicView=17.0.2
|
||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
BIN
jfx-jmods/linux-23.0.1/javafx.base.jmod
Normal file
BIN
jfx-jmods/linux-23.0.1/javafx.base.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/linux-23.0.1/javafx.controls.jmod
Normal file
BIN
jfx-jmods/linux-23.0.1/javafx.controls.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/linux-23.0.1/javafx.fxml.jmod
Normal file
BIN
jfx-jmods/linux-23.0.1/javafx.fxml.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/linux-23.0.1/javafx.graphics.jmod
Normal file
BIN
jfx-jmods/linux-23.0.1/javafx.graphics.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/linux-23.0.1/javafx.media.jmod
Normal file
BIN
jfx-jmods/linux-23.0.1/javafx.media.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/linux-23.0.1/javafx.swing.jmod
Normal file
BIN
jfx-jmods/linux-23.0.1/javafx.swing.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/linux-23.0.1/javafx.web.jmod
Normal file
BIN
jfx-jmods/linux-23.0.1/javafx.web.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/mac-23.0.1/javafx.base.jmod
Normal file
BIN
jfx-jmods/mac-23.0.1/javafx.base.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/mac-23.0.1/javafx.controls.jmod
Normal file
BIN
jfx-jmods/mac-23.0.1/javafx.controls.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/mac-23.0.1/javafx.fxml.jmod
Normal file
BIN
jfx-jmods/mac-23.0.1/javafx.fxml.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/mac-23.0.1/javafx.graphics.jmod
Normal file
BIN
jfx-jmods/mac-23.0.1/javafx.graphics.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/mac-23.0.1/javafx.media.jmod
Normal file
BIN
jfx-jmods/mac-23.0.1/javafx.media.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/mac-23.0.1/javafx.swing.jmod
Normal file
BIN
jfx-jmods/mac-23.0.1/javafx.swing.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/mac-23.0.1/javafx.web.jmod
Normal file
BIN
jfx-jmods/mac-23.0.1/javafx.web.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/win-23.0.1/javafx.base.jmod
Normal file
BIN
jfx-jmods/win-23.0.1/javafx.base.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/win-23.0.1/javafx.controls.jmod
Normal file
BIN
jfx-jmods/win-23.0.1/javafx.controls.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/win-23.0.1/javafx.fxml.jmod
Normal file
BIN
jfx-jmods/win-23.0.1/javafx.fxml.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/win-23.0.1/javafx.graphics.jmod
Normal file
BIN
jfx-jmods/win-23.0.1/javafx.graphics.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/win-23.0.1/javafx.media.jmod
Normal file
BIN
jfx-jmods/win-23.0.1/javafx.media.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/win-23.0.1/javafx.swing.jmod
Normal file
BIN
jfx-jmods/win-23.0.1/javafx.swing.jmod
Normal file
Binary file not shown.
BIN
jfx-jmods/win-23.0.1/javafx.web.jmod
Normal file
BIN
jfx-jmods/win-23.0.1/javafx.web.jmod
Normal file
Binary file not shown.
@ -1,10 +1,9 @@
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
import com.vanniktech.maven.publish.SonatypeHost
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'biz.aQute.bnd.builder' version "$bnd"
|
||||
id 'com.vanniktech.maven.publish' version "$mavenPublish"
|
||||
id 'com.github.johnrengelman.shadow' version "$shadowJarPlugin"
|
||||
id 'com.gradleup.shadow' version "$shadowJarPlugin"
|
||||
}
|
||||
|
||||
// Cross-platform scripts
|
||||
@ -19,54 +18,31 @@ repositories {
|
||||
}
|
||||
}
|
||||
|
||||
compileJava {
|
||||
java {
|
||||
sourceCompatibility = "$jdk"
|
||||
targetCompatibility = "$jdk"
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
tasks.withType(Jar).each { it.archiveBaseName.set("materialfx") }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit"
|
||||
|
||||
api "io.github.palexdev:mfxcore:$mfxcore"
|
||||
api "io.github.palexdev:mfxresources:$mfxresources"
|
||||
api "io.github.palexdev:virtualizedfx:$vfx"
|
||||
|
||||
testImplementation platform("org.junit:junit-bom:$junit")
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
}
|
||||
|
||||
javafx {
|
||||
configuration = 'compileOnly'
|
||||
}
|
||||
|
||||
javadoc {
|
||||
excludes = ['**/*.html', 'META-INF/**']
|
||||
|
||||
options.use = true
|
||||
options.splitIndex = true
|
||||
options.encoding = 'UTF-8'
|
||||
options.author = true
|
||||
options.version = true
|
||||
options.windowTitle = "$project.name $project.version API"
|
||||
options.docTitle = "$project.name $project.version API"
|
||||
options.links = ['https://docs.oracle.com/en/java/javase/11/docs/api',
|
||||
'https://openjfx.io/javadoc/17']
|
||||
}
|
||||
|
||||
tasks.register('javadocJar', Jar) {
|
||||
dependsOn javadoc
|
||||
archiveClassifier.set('javadoc')
|
||||
from javadoc.destinationDir
|
||||
}
|
||||
|
||||
tasks.register('sourcesJarBuild', Jar) {
|
||||
dependsOn classes
|
||||
archiveClassifier.set('sources')
|
||||
from sourceSets.main.allSource
|
||||
modules = ['javafx.controls', 'javafx.fxml']
|
||||
configurations = ["compileOnly", "testImplementation"]
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives javadocJar
|
||||
archives sourcesJarBuild
|
||||
archives jar
|
||||
archives shadowJar
|
||||
}
|
||||
|
||||
jar {
|
||||
@ -89,27 +65,16 @@ shadowJar {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('copyJar', Copy) {
|
||||
from jar
|
||||
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
|
||||
into System.getenv("APPDATA") + '/Scene Builder/Library'
|
||||
} else if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||
into System.getProperty("user.home") + '/Library/Application Support' + '/Scene Builder/Library'
|
||||
} else if (Os.isFamily(Os.FAMILY_UNIX)) {
|
||||
into System.getProperty("user.home") + '/.scenebuilder/Library'
|
||||
mavenPublishing {
|
||||
publishToMavenCentral(SonatypeHost.S01)
|
||||
signAllPublications()
|
||||
}
|
||||
|
||||
configurations {
|
||||
// Remove vanniktech non-sense
|
||||
gradle.taskGraph.whenReady { graph ->
|
||||
if (graph.hasTask(plainJavadocJar)) {
|
||||
plainJavadocJar.enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('removeBnd', Delete) {
|
||||
delete fileTree(project.buildDir) {
|
||||
include '**/*.bnd'
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
dependsOn shadowJar, copyJar, removeBnd
|
||||
}
|
||||
|
||||
mavenPublish {
|
||||
sonatypeHost = "S01"
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
GROUP=io.github.palexdev
|
||||
POM_ARTIFACT_ID=materialfx
|
||||
VERSION_NAME=11.17.0
|
||||
VERSION_NAME=21.18.0-alpha
|
||||
|
||||
POM_NAME=materialfx
|
||||
POM_DESCRIPTION=Material Desgin components for JavaFX
|
||||
|
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.builders.base;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.base.AbstractMFXListView;
|
||||
import io.github.palexdev.materialfx.effects.DepthLevel;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.util.Duration;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
public class BaseListViewBuilder<T, C extends Cell<T>, L extends AbstractMFXListView<T, C>> extends ControlBuilder<L> {
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public BaseListViewBuilder(L control) {
|
||||
super(control);
|
||||
}
|
||||
|
||||
public static <T, C extends Cell<T>> BaseListViewBuilder<T, C, AbstractMFXListView<T, C>> baseList(AbstractMFXListView<T, C> list) {
|
||||
return new BaseListViewBuilder<>(list);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
|
||||
public BaseListViewBuilder<T, C, L> setTrackColor(Paint trackColor) {
|
||||
node.setTrackColor(trackColor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseListViewBuilder<T, C, L> setThumbColor(Paint thumbColor) {
|
||||
node.setThumbColor(thumbColor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseListViewBuilder<T, C, L> setThumbHoverColor(Paint thumbHoverColor) {
|
||||
node.setThumbHoverColor(thumbHoverColor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseListViewBuilder<T, C, L> setHideAfter(Duration hideAfter) {
|
||||
node.setHideAfter(hideAfter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseListViewBuilder<T, C, L> setItems(ObservableList<T> items) {
|
||||
node.setItems(items);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseListViewBuilder<T, C, L> setConverter(StringConverter<T> converter) {
|
||||
node.setConverter(converter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseListViewBuilder<T, C, L> setHideScrollBars(boolean hideScrollBars) {
|
||||
node.setHideScrollBars(hideScrollBars);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BaseListViewBuilder<T, C, L> setDepthLevel(DepthLevel depthLevel) {
|
||||
node.setDepthLevel(depthLevel);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.builders.control;
|
||||
|
||||
import io.github.palexdev.materialfx.builders.base.BaseListViewBuilder;
|
||||
import io.github.palexdev.materialfx.controls.MFXCheckListView;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXCheckListCell;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CheckListBuilder<T> extends BaseListViewBuilder<T, MFXCheckListCell<T>, MFXCheckListView<T>> {
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public CheckListBuilder() {
|
||||
this(new MFXCheckListView<>());
|
||||
}
|
||||
|
||||
public CheckListBuilder(MFXCheckListView<T> listView) {
|
||||
super(listView);
|
||||
}
|
||||
|
||||
public static <T> CheckListBuilder<T> checkList() {
|
||||
return new CheckListBuilder<>();
|
||||
}
|
||||
|
||||
public static <T> CheckListBuilder<T> checkList(MFXCheckListView<T> checkListView) {
|
||||
return new CheckListBuilder<>(checkListView);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
|
||||
public CheckListBuilder<T> scrollBy(double pixels) {
|
||||
node.scrollBy(pixels);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> scrollTo(int index) {
|
||||
node.scrollTo(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> scrollToFirst() {
|
||||
node.scrollToFirst();
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> scrollToLast() {
|
||||
node.scrollToLast();
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> scrollToPixel(double pixel) {
|
||||
node.scrollToPixel(pixel);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> setHSpeed(double unit, double block) {
|
||||
node.setHSpeed(unit, block);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> setVSpeed(double unit, double block) {
|
||||
node.setVSpeed(unit, block);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> setCellFactory(Function<T, MFXCheckListCell<T>> cellFactory) {
|
||||
node.setCellFactory(cellFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> enableSmoothScrolling(double speed) {
|
||||
node.features().enableSmoothScrolling(speed);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> enableSmoothScrolling(double speed, double trackPadAdjustment) {
|
||||
node.features().enableSmoothScrolling(speed, trackPadAdjustment);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> enableSmoothScrolling(double speed, double trackPadAdjustment, double scrollThreshold) {
|
||||
node.features().enableSmoothScrolling(speed, trackPadAdjustment, scrollThreshold);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> enableBounceEffect() {
|
||||
node.features().enableBounceEffect();
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckListBuilder<T> enableBounceEffect(double strength, double maxOverscroll) {
|
||||
node.features().enableBounceEffect(strength, maxOverscroll);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -18,9 +18,13 @@
|
||||
|
||||
package io.github.palexdev.materialfx.builders.control;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.Alignment;
|
||||
import io.github.palexdev.materialfx.controls.MFXComboBox;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import io.github.palexdev.virtualizedfx.cells.base.VFXCell;
|
||||
import javafx.animation.Animation;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.Event;
|
||||
@ -28,10 +32,6 @@ import javafx.event.EventHandler;
|
||||
import javafx.scene.Node;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ComboBuilder<T, C extends MFXComboBox<T>> extends TextFieldBuilder<C> {
|
||||
|
||||
//================================================================================
|
||||
@ -143,7 +143,7 @@ public class ComboBuilder<T, C extends MFXComboBox<T>> extends TextFieldBuilder<
|
||||
return this;
|
||||
}
|
||||
|
||||
public ComboBuilder<T, C> setCellFactory(Function<T, Cell<T>> cellFactory) {
|
||||
public ComboBuilder<T, C> setCellFactory(Function<T, VFXCell<T>> cellFactory) {
|
||||
node.setCellFactory(cellFactory);
|
||||
return this;
|
||||
}
|
||||
|
@ -18,12 +18,12 @@
|
||||
|
||||
package io.github.palexdev.materialfx.builders.control;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXFilterComboBox;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXFilterComboBox;
|
||||
|
||||
public class FilterComboBuilder<T> extends ComboBuilder<T, MFXFilterComboBox<T>> {
|
||||
|
||||
//================================================================================
|
||||
@ -73,9 +73,4 @@ public class FilterComboBuilder<T> extends ComboBuilder<T, MFXFilterComboBox<T>>
|
||||
node.getFilterList().setComparator(comparator);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FilterComboBuilder<T> setComparator(Comparator<T> comparator, boolean isReverse) {
|
||||
node.getFilterList().setComparator(comparator, isReverse);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.builders.control;
|
||||
|
||||
import io.github.palexdev.materialfx.builders.base.BaseListViewBuilder;
|
||||
import io.github.palexdev.materialfx.controls.MFXListView;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXListCell;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ListBuilder<T> extends BaseListViewBuilder<T, MFXListCell<T>, MFXListView<T>> {
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public ListBuilder() {
|
||||
this(new MFXListView<>());
|
||||
}
|
||||
|
||||
public ListBuilder(MFXListView<T> listView) {
|
||||
super(listView);
|
||||
}
|
||||
|
||||
public static <T> ListBuilder<T> list() {
|
||||
return new ListBuilder<>();
|
||||
}
|
||||
|
||||
public static <T> ListBuilder<T> list(MFXListView<T> listView) {
|
||||
return new ListBuilder<>(listView);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
|
||||
public ListBuilder<T> scrollBy(double pixels) {
|
||||
node.scrollBy(pixels);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> scrollTo(int index) {
|
||||
node.scrollTo(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> scrollToFirst() {
|
||||
node.scrollToFirst();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> scrollToLast() {
|
||||
node.scrollToLast();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> scrollToPixel(double pixel) {
|
||||
node.scrollToPixel(pixel);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> setHSpeed(double unit, double block) {
|
||||
node.setHSpeed(unit, block);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> setVSpeed(double unit, double block) {
|
||||
node.setVSpeed(unit, block);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> setCellFactory(Function<T, MFXListCell<T>> cellFactory) {
|
||||
node.setCellFactory(cellFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> enableSmoothScrolling(double speed) {
|
||||
node.features().enableSmoothScrolling(speed);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> enableSmoothScrolling(double speed, double trackPadAdjustment) {
|
||||
node.features().enableSmoothScrolling(speed, trackPadAdjustment);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> enableSmoothScrolling(double speed, double trackPadAdjustment, double scrollThreshold) {
|
||||
node.features().enableSmoothScrolling(speed, trackPadAdjustment, scrollThreshold);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> enableBounceEffect() {
|
||||
node.features().enableBounceEffect();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListBuilder<T> enableBounceEffect(double strength, double maxOverscroll) {
|
||||
node.features().enableBounceEffect(strength, maxOverscroll);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -18,6 +18,9 @@
|
||||
|
||||
package io.github.palexdev.materialfx.builders.control;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import io.github.palexdev.materialfx.builders.base.ControlBuilder;
|
||||
import io.github.palexdev.materialfx.controls.MFXNotificationCenter;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXNotificationCell;
|
||||
@ -27,9 +30,6 @@ import io.github.palexdev.materialfx.notifications.base.INotification;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class NotificationCenterBuilder extends ControlBuilder<MFXNotificationCenter> {
|
||||
|
||||
//================================================================================
|
||||
@ -190,7 +190,8 @@ public class NotificationCenterBuilder extends ControlBuilder<MFXNotificationCen
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationCenterBuilder setHSpeed(double unit, double block) {
|
||||
// TODO fix these
|
||||
/* public NotificationCenterBuilder setHSpeed(double unit, double block) {
|
||||
node.setHSpeed(unit, block);
|
||||
return this;
|
||||
}
|
||||
@ -198,14 +199,15 @@ public class NotificationCenterBuilder extends ControlBuilder<MFXNotificationCen
|
||||
public NotificationCenterBuilder setVSpeed(double unit, double block) {
|
||||
node.setVSpeed(unit, block);
|
||||
return this;
|
||||
}
|
||||
}*/
|
||||
|
||||
public NotificationCenterBuilder setCellFactory(Function<INotification, MFXNotificationCell> cellFactory) {
|
||||
node.setCellFactory(cellFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
public NotificationCenterBuilder enableSmoothScrolling(double speed) {
|
||||
// TODO and these
|
||||
/* public NotificationCenterBuilder enableSmoothScrolling(double speed) {
|
||||
node.features().enableSmoothScrolling(speed);
|
||||
return this;
|
||||
}
|
||||
@ -228,5 +230,5 @@ public class NotificationCenterBuilder extends ControlBuilder<MFXNotificationCen
|
||||
public NotificationCenterBuilder enableBounceEffect(double strength, double maxOverscroll) {
|
||||
node.features().enableBounceEffect(strength, maxOverscroll);
|
||||
return this;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.builders.control;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXPaginatedTableView;
|
||||
|
||||
public class PaginatedTableBuilder<T> extends TableBuilder<T, MFXPaginatedTableView<T>> {
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public PaginatedTableBuilder() {
|
||||
this(new MFXPaginatedTableView<>());
|
||||
}
|
||||
|
||||
public PaginatedTableBuilder(MFXPaginatedTableView<T> tableView) {
|
||||
super(tableView);
|
||||
}
|
||||
|
||||
public static <T> PaginatedTableBuilder<T> paginatedTable() {
|
||||
return new PaginatedTableBuilder<>();
|
||||
}
|
||||
|
||||
public static <T> PaginatedTableBuilder<T> paginatedTable(MFXPaginatedTableView<T> tableView) {
|
||||
return new PaginatedTableBuilder<>(tableView);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
|
||||
public PaginatedTableBuilder<T> goToPage(int index) {
|
||||
node.goToPage(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PaginatedTableBuilder<T> setCurrentPage(int currentPage) {
|
||||
node.setCurrentPage(currentPage);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PaginatedTableBuilder<T> setPagesToShow(int pagesToShow) {
|
||||
node.setPagesToShow(pagesToShow);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PaginatedTableBuilder<T> setRowsPerPage(int rowsPerPage) {
|
||||
node.setRowsPerPage(rowsPerPage);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -18,15 +18,15 @@
|
||||
|
||||
package io.github.palexdev.materialfx.builders.control;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.github.palexdev.materialfx.builders.base.ControlBuilder;
|
||||
import io.github.palexdev.materialfx.controls.MFXPagination;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXPage;
|
||||
import javafx.geometry.Orientation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class PaginationBuilder extends ControlBuilder<MFXPagination> {
|
||||
|
||||
//================================================================================
|
||||
|
@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.builders.control;
|
||||
|
||||
import io.github.palexdev.materialfx.builders.base.ControlBuilder;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableColumn;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableRow;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableView;
|
||||
import io.github.palexdev.materialfx.filter.base.AbstractFilter;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class TableBuilder<T, V extends MFXTableView<T>> extends ControlBuilder<V> {
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
@SuppressWarnings("unchecked")
|
||||
public TableBuilder() {
|
||||
this((V) new MFXTableView<T>());
|
||||
}
|
||||
|
||||
public TableBuilder(V tableView) {
|
||||
super(tableView);
|
||||
}
|
||||
|
||||
public static <T> TableBuilder<T, MFXTableView<T>> table() {
|
||||
return new TableBuilder<>();
|
||||
}
|
||||
|
||||
public static <T> TableBuilder<T, MFXTableView<T>> table(MFXTableView<T> tableView) {
|
||||
return new TableBuilder<>(tableView);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
|
||||
public TableBuilder<T, V> autosizeColumnsOnInitialization() {
|
||||
node.autosizeColumnsOnInitialization();
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> scrollBy(double pixels) {
|
||||
node.scrollBy(pixels);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> scrollTo(int index) {
|
||||
node.scrollTo(index);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> scrollToFirst() {
|
||||
node.scrollToFirst();
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> scrollToLast() {
|
||||
node.scrollToLast();
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> scrollToPixel(double pixel) {
|
||||
node.scrollToPixel(pixel);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> setHSpeed(double unit, double block) {
|
||||
node.setHSpeed(unit, block);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> setVSpeed(double unit, double block) {
|
||||
node.setVSpeed(unit, block);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> enableSmoothScrolling(double speed) {
|
||||
node.features().enableSmoothScrolling(speed);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> enableSmoothScrolling(double speed, double trackPadAdjustment) {
|
||||
node.features().enableSmoothScrolling(speed, trackPadAdjustment);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> enableSmoothScrolling(double speed, double trackPadAdjustment, double scrollThreshold) {
|
||||
node.features().enableSmoothScrolling(speed, trackPadAdjustment, scrollThreshold);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> enableBounceEffect() {
|
||||
node.features().enableBounceEffect();
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> enableBounceEffect(double strength, double maxOverscroll) {
|
||||
node.features().enableBounceEffect(strength, maxOverscroll);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> setItems(ObservableList<T> items) {
|
||||
node.setItems(items);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public TableBuilder<T, V> addColumns(MFXTableColumn<T>... columns) {
|
||||
node.getTableColumns().addAll(columns);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public TableBuilder<T, V> setColumns(MFXTableColumn<T>... columns) {
|
||||
node.getTableColumns().setAll(columns);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> setTableRowFactory(Function<T, MFXTableRow<T>> tableRowFactory) {
|
||||
node.setTableRowFactory(tableRowFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> setFilter(Predicate<T> filter) {
|
||||
node.getTransformableList().setPredicate(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> setComparator(Comparator<T> comparator) {
|
||||
node.getTransformableList().setComparator(comparator);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> setComparator(Comparator<T> comparator, boolean isReverse) {
|
||||
node.getTransformableList().setComparator(comparator, isReverse);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public TableBuilder<T, V> addFilters(AbstractFilter<T, ?>... filters) {
|
||||
node.getFilters().addAll(filters);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public TableBuilder<T, V> setFilters(AbstractFilter<T, ?>... filters) {
|
||||
node.getFilters().setAll(filters);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableBuilder<T, V> setFooterVisible(boolean footerVisible) {
|
||||
node.setFooterVisible(footerVisible);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.builders.control;
|
||||
|
||||
import io.github.palexdev.materialfx.builders.base.LabeledBuilder;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableColumn;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXTableRowCell;
|
||||
import io.github.palexdev.materialfx.enums.SortState;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class TableColumnBuilder<T> extends LabeledBuilder<MFXTableColumn<T>> {
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public TableColumnBuilder() {
|
||||
this(new MFXTableColumn<>());
|
||||
}
|
||||
|
||||
public TableColumnBuilder(MFXTableColumn<T> column) {
|
||||
super(column);
|
||||
}
|
||||
|
||||
public static <T> TableColumnBuilder<T> tableColumn() {
|
||||
return new TableColumnBuilder<>();
|
||||
}
|
||||
|
||||
public static <T> TableColumnBuilder<T> tableColumn(MFXTableColumn<T> column) {
|
||||
return new TableColumnBuilder<>(column);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
|
||||
public TableColumnBuilder<T> setRowCellFactory(Function<T, MFXTableRowCell<T, ?>> rowCellFactory) {
|
||||
node.setRowCellFactory(rowCellFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableColumnBuilder<T> setSortState(SortState sortState) {
|
||||
node.setSortState(sortState);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableColumnBuilder<T> setComparator(Comparator<T> comparator) {
|
||||
node.setComparator(comparator);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TableColumnBuilder<T> setColumnResizable(boolean columnResizable) {
|
||||
node.setColumnResizable(columnResizable);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.collections;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
class ChangeHelper {
|
||||
ChangeHelper() {
|
||||
}
|
||||
|
||||
public static String addRemoveChangeToString(int from, int to, List<?> list, List<?> removed) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (removed.isEmpty()) {
|
||||
sb.append(list.subList(from, to));
|
||||
sb.append(" added at ").append(from);
|
||||
} else {
|
||||
sb.append(removed);
|
||||
if (from == to) {
|
||||
sb.append(" removed at ").append(from);
|
||||
} else {
|
||||
sb.append(" replaced by ");
|
||||
sb.append(list.subList(from, to));
|
||||
sb.append(" at ").append(from);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String permChangeToString(int[] permutation) {
|
||||
return "permutated by " + Arrays.toString(permutation);
|
||||
}
|
||||
|
||||
public static String updateChangeToString(int from, int to) {
|
||||
return "updated at range [" + from + ", " + to + ")";
|
||||
}
|
||||
}
|
@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.collections;
|
||||
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
abstract class NonIterableChange<E> extends ListChangeListener.Change<E> {
|
||||
private final int from;
|
||||
private final int to;
|
||||
private boolean invalid = true;
|
||||
private static final int[] EMPTY_PERM = new int[0];
|
||||
|
||||
protected NonIterableChange(int from, int to, ObservableList<E> list) {
|
||||
super(list);
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public int getFrom() {
|
||||
checkState();
|
||||
return from;
|
||||
}
|
||||
|
||||
public int getTo() {
|
||||
checkState();
|
||||
return to;
|
||||
}
|
||||
|
||||
protected int[] getPermutation() {
|
||||
checkState();
|
||||
return EMPTY_PERM;
|
||||
}
|
||||
|
||||
public boolean next() {
|
||||
if (invalid) {
|
||||
invalid = false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
public void checkState() {
|
||||
if (invalid) {
|
||||
throw new IllegalStateException("Invalid Change state: next() must be called before inspecting the Change.");
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
boolean oldInvalid = invalid;
|
||||
invalid = false;
|
||||
String ret;
|
||||
if (wasPermutated()) {
|
||||
ret = ChangeHelper.permChangeToString(getPermutation());
|
||||
} else if (wasUpdated()) {
|
||||
ret = ChangeHelper.updateChangeToString(from, to);
|
||||
} else {
|
||||
ret = ChangeHelper.addRemoveChangeToString(from, to, getList(), getRemoved());
|
||||
}
|
||||
|
||||
invalid = oldInvalid;
|
||||
return "{ " + ret + " }";
|
||||
}
|
||||
|
||||
public static class SimpleUpdateChange<E> extends NonIterableChange<E> {
|
||||
public SimpleUpdateChange(int position, ObservableList<E> list) {
|
||||
this(position, position + 1, list);
|
||||
}
|
||||
|
||||
public SimpleUpdateChange(int from, int to, ObservableList<E> list) {
|
||||
super(from, to, list);
|
||||
}
|
||||
|
||||
public List<E> getRemoved() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public boolean wasUpdated() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimplePermutationChange<E> extends NonIterableChange<E> {
|
||||
private final int[] permutation;
|
||||
|
||||
public SimplePermutationChange(int from, int to, int[] permutation, ObservableList<E> list) {
|
||||
super(from, to, list);
|
||||
this.permutation = permutation;
|
||||
}
|
||||
|
||||
public List<E> getRemoved() {
|
||||
checkState();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
protected int[] getPermutation() {
|
||||
checkState();
|
||||
return permutation;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimpleAddChange<E> extends NonIterableChange<E> {
|
||||
public SimpleAddChange(int from, int to, ObservableList<E> list) {
|
||||
super(from, to, list);
|
||||
}
|
||||
|
||||
public boolean wasRemoved() {
|
||||
checkState();
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<E> getRemoved() {
|
||||
checkState();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimpleRemovedChange<E> extends NonIterableChange<E> {
|
||||
private final List<E> removed;
|
||||
|
||||
public SimpleRemovedChange(int from, int to, E removed, ObservableList<E> list) {
|
||||
super(from, to, list);
|
||||
this.removed = Collections.singletonList(removed);
|
||||
}
|
||||
|
||||
public boolean wasRemoved() {
|
||||
checkState();
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<E> getRemoved() {
|
||||
checkState();
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GenericAddRemoveChange<E> extends NonIterableChange<E> {
|
||||
private final List<E> removed;
|
||||
|
||||
public GenericAddRemoveChange(int from, int to, List<E> removed, ObservableList<E> list) {
|
||||
super(from, to, list);
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
public List<E> getRemoved() {
|
||||
checkState();
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Parisi Alessandro - alessandro.parisi406@gmail.com
|
||||
* This file is part of ArchitectFX (https://github.com/palexdev/ArchitectFX)
|
||||
*
|
||||
* ArchitectFX is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation; either version 3 of the License,
|
||||
* or (at your option) any later version.
|
||||
*
|
||||
* ArchitectFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with ArchitectFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.collections;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
|
||||
public class RefineList<T> implements ObservableList<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final ObservableList<T> src;
|
||||
private final FilteredList<T> filtered;
|
||||
private final SortedList<T> sorted;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public RefineList(ObservableList<T> src) {
|
||||
this.src = src;
|
||||
filtered = new FilteredList<>(src);
|
||||
sorted = new SortedList<>(filtered);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
public int sourceToView(int index) {
|
||||
return (getPredicate() != null) ?
|
||||
filtered.getViewIndex(index) :
|
||||
sorted.getViewIndex(index);
|
||||
}
|
||||
|
||||
public int viewToSource(int index) {
|
||||
return sorted.getSourceIndexFor(src, index);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
public Predicate<T> getPredicate() {
|
||||
return (Predicate<T>) filtered.getPredicate();
|
||||
}
|
||||
|
||||
public ObjectProperty<Predicate<? super T>> predicateProperty() {
|
||||
return filtered.predicateProperty();
|
||||
}
|
||||
|
||||
public void setPredicate(Predicate<T> predicate) {
|
||||
filtered.setPredicate(predicate);
|
||||
}
|
||||
|
||||
public Comparator<T> getComparator() {
|
||||
return (Comparator<T>) sorted.getComparator();
|
||||
}
|
||||
|
||||
public ObjectProperty<Comparator<? super T>> comparatorProperty() {
|
||||
return sorted.comparatorProperty();
|
||||
}
|
||||
|
||||
public void setComparator(Comparator<T> comparator) {
|
||||
sorted.setComparator(comparator);
|
||||
}
|
||||
|
||||
// Observability (on the "last view")
|
||||
@Override
|
||||
public void addListener(ListChangeListener<? super T> listener) {
|
||||
sorted.addListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(ListChangeListener<? super T> listener) {
|
||||
sorted.removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(InvalidationListener listener) {
|
||||
sorted.addListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(InvalidationListener listener) {
|
||||
sorted.removeListener(listener);
|
||||
}
|
||||
|
||||
// Basic List Operations
|
||||
@Override
|
||||
public boolean add(T t) {
|
||||
return src.add(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return src.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
src.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return src.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return src.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return src.contains(o);
|
||||
}
|
||||
|
||||
// Bulk Operations
|
||||
@Override
|
||||
public boolean addAll(T... elements) {
|
||||
return src.addAll(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> c) {
|
||||
return src.addAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends T> c) {
|
||||
return src.addAll(index, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setAll(T... elements) {
|
||||
return src.setAll(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setAll(Collection<? extends T> col) {
|
||||
return src.setAll(col);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(T... elements) {
|
||||
return src.removeAll(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return src.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(T... elements) {
|
||||
return src.retainAll(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return src.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return src.containsAll(c);
|
||||
}
|
||||
|
||||
// Indexed Operations
|
||||
@Override
|
||||
public T get(int index) {
|
||||
return src.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T set(int index, T element) {
|
||||
return src.set(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, T element) {
|
||||
src.add(index, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T remove(int index) {
|
||||
return src.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(int from, int to) {
|
||||
src.remove(from, to);
|
||||
}
|
||||
|
||||
// Positional Search
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return src.indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return src.lastIndexOf(o);
|
||||
}
|
||||
|
||||
// Iterators
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return src.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<T> listIterator() {
|
||||
return src.listIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<T> listIterator(int index) {
|
||||
return src.listIterator(index);
|
||||
}
|
||||
|
||||
// Sublist Operations
|
||||
@Override
|
||||
public List<T> subList(int fromIndex, int toIndex) {
|
||||
return src.subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
// Array Conversions
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return src.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T1> T1[] toArray(T1[] a) {
|
||||
return src.toArray(a);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters
|
||||
//================================================================================
|
||||
public ObservableList<T> getSrc() {
|
||||
return src;
|
||||
}
|
||||
|
||||
public ObservableList<T> getView() {
|
||||
return sorted;
|
||||
}
|
||||
}
|
@ -1,288 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.collections;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.properties.functional.ComparatorProperty;
|
||||
import io.github.palexdev.materialfx.beans.properties.functional.PredicateProperty;
|
||||
import io.github.palexdev.materialfx.collections.NonIterableChange.GenericAddRemoveChange;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.collections.transformation.TransformationList;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* A {@code TransformableList} is a particular type of List which wraps another
|
||||
* List called "source" and allows manipulations such as: filter and sort, retaining
|
||||
* the original items' index.
|
||||
* <p></p>
|
||||
* Extends {@link TransformationList}, it's basically the same thing of a {@link FilteredList}
|
||||
* and a {@link SortedList} but combined into one.
|
||||
* <p></p>
|
||||
* A more detailed (and hopefully more clear) explanation about the "indexes retention mentioned above":
|
||||
* <p>
|
||||
* Think of this List as a View for the source list. The underlying data provided by the source is
|
||||
* not guaranteed to be what the user sees but the items' properties are maintained.
|
||||
* Let's see a brief example:
|
||||
* <pre>
|
||||
* {@code
|
||||
* // Let's say I have this ObservableList
|
||||
* ObservableList<String> source = FXCollections.observableArrayList("A", "B", "C"):
|
||||
*
|
||||
* // Now let's say I want to sort this list in reverse order (CBA) and that
|
||||
* // for some reason I still want A to be the element at index 0, B-1 and C-2
|
||||
* // This is exactly the purpose of the TransformableList...
|
||||
* TransformableList<String> transformed = new TransformableList<>(source);
|
||||
* transformed.setSorter(Comparator.reverseOrder());
|
||||
*
|
||||
* // Now that the order is (CBA) let's see how the list behaves:
|
||||
* transformed.get(0); // Returns C
|
||||
* transformed.indexOf("C"); // Returns 2, the index is retrieved in the source list
|
||||
* transformed.viewToSource(0); // Returns 2, it maps an index of the transformed list to the index of the source list, at 0 we have C which is at index 2 in the source list
|
||||
* transformed.sourceToView(0); // Also returns 2, it maps an index of the source list to the index of the transformed list, at 0 we have C which is at index 2 in the transformed list
|
||||
*
|
||||
* // To better see its behavior try to sort and filter the list at the same time.
|
||||
* // You'll notice that sometimes sourceToView will return a negative index because the item is not in the transformed list (after a filter operation)
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* Check {@link #computeIndexes()} documentation to see how indexes are calculated.
|
||||
* <p></p>
|
||||
* <b>IMPORTANT:</b> If using a reversed comparator please use {@link #setComparator(Comparator, boolean)} with 'true' as argument,
|
||||
* as {@link #setComparator(Comparator)} will always assume it is a natural order comparator. This is needed to make {@link #sourceToView(int)}
|
||||
* properly work as it uses a binary search algorithm to find the right index.
|
||||
*
|
||||
* @param <T> the items' type
|
||||
*/
|
||||
public class TransformableList<T> extends TransformationList<T, T> {
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
private final List<Integer> indexes = new ArrayList<>();
|
||||
private boolean reversed = false;
|
||||
|
||||
private final PredicateProperty<T> predicate = new PredicateProperty<>() {
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
update();
|
||||
}
|
||||
};
|
||||
|
||||
private final ComparatorProperty<T> comparator = new ComparatorProperty<>() {
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
update();
|
||||
}
|
||||
};
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public TransformableList(ObservableList<? extends T> source) {
|
||||
this(source, null);
|
||||
}
|
||||
|
||||
public TransformableList(ObservableList<? extends T> source, Predicate<T> predicate) {
|
||||
this(source, predicate, null);
|
||||
}
|
||||
|
||||
public TransformableList(ObservableList<? extends T> source, Predicate<T> predicate, Comparator<T> comparator) {
|
||||
super(source);
|
||||
setPredicate(predicate);
|
||||
setComparator(comparator);
|
||||
update();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Calls {@link #getSourceIndex(int)}, just with a different name to be more clear.
|
||||
* <p></p>
|
||||
* Maps an index of the transformed list, to the index of the source list.
|
||||
*/
|
||||
public int viewToSource(int index) {
|
||||
return getSourceIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #getViewIndex(int)}, just with a different name to be more clear.
|
||||
* <p></p>
|
||||
* Maps an index of the source list, to the index of the transformed list.
|
||||
*/
|
||||
public int sourceToView(int index) {
|
||||
return getViewIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for updating the transformed indexes when the
|
||||
* predicate or the comparator change.
|
||||
*/
|
||||
private void update() {
|
||||
indexes.clear();
|
||||
indexes.addAll(computeIndexes());
|
||||
if (this.hasListeners()) {
|
||||
this.fireChange(new GenericAddRemoveChange<>(0, size(), new ArrayList<>(this), this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Core method of TransformableLists. This is responsible for computing
|
||||
* the transformed indexes by creating a {@link SortedMap} and mapping every index from 0 to source size
|
||||
* to its item. Before mapping, items are filtered with the given predicate, {@link #predicateProperty()}.
|
||||
* Before returning, the map's entry set is sorted by its values with the given comparator, {@link #comparatorProperty()}.
|
||||
* Finally, returns the map's key set, this set contains the transformed indexes, filtered and sorted.
|
||||
*/
|
||||
private Collection<Integer> computeIndexes() {
|
||||
Predicate<? super T> filter = this.getPredicate();
|
||||
Comparator<? super T> sorter = this.getComparator();
|
||||
SortedMap<Integer, T> sourceMap;
|
||||
if (filter != null) {
|
||||
sourceMap = IntStream.range(0, getSource().size())
|
||||
.filter((index) -> filter.test(getSource().get(index)))
|
||||
.collect(TreeMap::new, (map, index) -> map.put(index, getSource().get(index)), TreeMap::putAll);
|
||||
} else {
|
||||
sourceMap = IntStream.range(0, getSource().size())
|
||||
.collect(TreeMap::new, (map, index) -> map.put(index, getSource().get(index)), TreeMap::putAll);
|
||||
}
|
||||
|
||||
return sorter != null ? sourceMap.entrySet().stream()
|
||||
.sorted((o1, o2) -> sorter.compare(o1.getValue(), o2.getValue()))
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(Collectors.toList()) : sourceMap.keySet();
|
||||
}
|
||||
|
||||
public Predicate<? super T> getPredicate() {
|
||||
return this.predicate.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the predicate used to filter the source list.
|
||||
*/
|
||||
public PredicateProperty<T> predicateProperty() {
|
||||
return this.predicate;
|
||||
}
|
||||
|
||||
public void setPredicate(Predicate<T> predicate) {
|
||||
this.predicate.set(predicate);
|
||||
}
|
||||
|
||||
public Comparator<T> getComparator() {
|
||||
return this.comparator.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the comparator used to sort the source list.
|
||||
*
|
||||
* @see #setComparator(Comparator, boolean)
|
||||
*/
|
||||
public ComparatorProperty<T> comparatorProperty() {
|
||||
return this.comparator;
|
||||
}
|
||||
|
||||
public void setComparator(Comparator<T> comparator) {
|
||||
this.reversed = false;
|
||||
this.comparator.set(comparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is NECESSARY if using a reversed comparator,
|
||||
* a special flag is set to true and {@link #sourceToView(int)} behaves accordingly.
|
||||
*/
|
||||
public void setComparator(Comparator<T> comparator, boolean reversed) {
|
||||
this.reversed = reversed;
|
||||
this.comparator.set(comparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies if a reversed comparator is being used.
|
||||
*/
|
||||
public boolean isReversed() {
|
||||
return reversed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Communicates to the transformed list, specifically to {@link #getViewIndex(int)},
|
||||
* if the list is sorted in reversed order.
|
||||
*/
|
||||
public void setReversed(boolean reversed) {
|
||||
this.reversed = reversed;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Calls {@link #update()}.
|
||||
*/
|
||||
@Override
|
||||
protected void sourceChanged(ListChangeListener.Change<? extends T> c) {
|
||||
beginChange();
|
||||
update();
|
||||
endChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of items in the transformable list
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return indexes.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves and return the item at the given index in the transformable list.
|
||||
* This means transformations due to {@link #predicateProperty()} or {@link #comparatorProperty()}
|
||||
* are taken into account.
|
||||
*/
|
||||
@Override
|
||||
public T get(int index) {
|
||||
if (index > size()) {
|
||||
throw new IndexOutOfBoundsException(index);
|
||||
} else {
|
||||
return getSource().get(indexes.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceIndex(int index) {
|
||||
if (index > size()) {
|
||||
throw new IndexOutOfBoundsException(index);
|
||||
} else {
|
||||
return indexes.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewIndex(int index) {
|
||||
int viewIndex = reversed ?
|
||||
Collections.binarySearch(indexes, index, Collections.reverseOrder()) :
|
||||
Collections.binarySearch(indexes, index);
|
||||
return viewIndex < 0 ? -1 : viewIndex;
|
||||
}
|
||||
}
|
@ -1,341 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.collections;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.properties.functional.ComparatorProperty;
|
||||
import io.github.palexdev.materialfx.beans.properties.functional.PredicateProperty;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.TransformationList;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* For some idiot reason JavaFX's {@link TransformationList}s do not allow modifying the
|
||||
* source list.
|
||||
* <p>
|
||||
* This class fixes that. It wraps an {@link ObservableList} which is the source list and
|
||||
* the new {@link TransformableList} which is built from that source.
|
||||
* <p>
|
||||
* This way you can benefit of the futures of the new {@link TransformableList} (sorting and filtering)
|
||||
* while also being able to directly modify the source list.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "NullableProblems"})
|
||||
public class TransformableListWrapper<T> extends AbstractList<T> implements ObservableList<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final ObservableList<T> source;
|
||||
private final TransformableList<T> transformableList;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public TransformableListWrapper(ObservableList<T> source) {
|
||||
this.source = source;
|
||||
this.transformableList = new TransformableList<>(source);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Added to the {@link TransformableList}.
|
||||
*/
|
||||
@Override
|
||||
public void addListener(ListChangeListener<? super T> listener) {
|
||||
transformableList.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Removed from the {@link TransformableList}.
|
||||
*/
|
||||
@Override
|
||||
public void removeListener(ListChangeListener<? super T> listener) {
|
||||
transformableList.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Added to the source list.
|
||||
*/
|
||||
@Override
|
||||
public boolean add(T t) {
|
||||
return source.add(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Set on the source list.
|
||||
*/
|
||||
@Override
|
||||
public T set(int index, T element) {
|
||||
return source.set(index, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Added to the source list.
|
||||
*/
|
||||
@Override
|
||||
public void add(int index, T element) {
|
||||
source.add(index, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Removed from the source list.
|
||||
*/
|
||||
@Override
|
||||
public T remove(int index) {
|
||||
return source.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Retrieved from the {@link TransformableList}.
|
||||
*/
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return transformableList.indexOf(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Retrieved from the {@link TransformableList}.
|
||||
*/
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return transformableList.lastIndexOf(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* The source list is cleared.
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
source.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Added to the source list.
|
||||
*/
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends T> c) {
|
||||
return source.addAll(index, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Added to the source list.
|
||||
*/
|
||||
@Override
|
||||
public boolean addAll(T... elements) {
|
||||
return source.addAll(elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Set on the source list.
|
||||
*/
|
||||
@Override
|
||||
public boolean setAll(T... elements) {
|
||||
return source.setAll(elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Set on the source list.
|
||||
*/
|
||||
@Override
|
||||
public boolean setAll(Collection<? extends T> col) {
|
||||
return source.setAll(col);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Removed from the source list.
|
||||
*/
|
||||
@Override
|
||||
public boolean removeAll(T... elements) {
|
||||
return source.removeAll(elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Retained on the source list.
|
||||
*/
|
||||
@Override
|
||||
public boolean retainAll(T... elements) {
|
||||
return source.retainAll(elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Removed from the source list.
|
||||
*/
|
||||
@Override
|
||||
public void remove(int from, int to) {
|
||||
source.remove(from, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Added to the {@link TransformableList}.
|
||||
*/
|
||||
@Override
|
||||
public void addListener(InvalidationListener listener) {
|
||||
transformableList.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Removed from the {@link TransformableList}.
|
||||
*/
|
||||
@Override
|
||||
public void removeListener(InvalidationListener listener) {
|
||||
transformableList.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Retrieved from the {@link TransformableList}.
|
||||
*/
|
||||
@Override
|
||||
public T get(int index) {
|
||||
return transformableList.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* Size of the {@link TransformableList}.
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return transformableList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the source observable list
|
||||
*/
|
||||
public ObservableList<? extends T> getSource() {
|
||||
return transformableList.getSource();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link TransformableList#viewToSource(int)}.
|
||||
*/
|
||||
public int viewToSource(int index) {
|
||||
return transformableList.viewToSource(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link TransformableList#sourceToView(int)}.
|
||||
*/
|
||||
public int sourceToView(int index) {
|
||||
return transformableList.sourceToView(index);
|
||||
}
|
||||
|
||||
public Predicate<? super T> getPredicate() {
|
||||
return transformableList.getPredicate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link TransformableList#predicateProperty()}.
|
||||
*/
|
||||
public PredicateProperty<T> predicateProperty() {
|
||||
return transformableList.predicateProperty();
|
||||
}
|
||||
|
||||
public void setPredicate(Predicate<T> predicate) {
|
||||
transformableList.setPredicate(predicate);
|
||||
}
|
||||
|
||||
public Comparator<T> getComparator() {
|
||||
return transformableList.getComparator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link TransformableList#comparatorProperty()}.
|
||||
*/
|
||||
public ComparatorProperty<T> comparatorProperty() {
|
||||
return transformableList.comparatorProperty();
|
||||
}
|
||||
|
||||
public void setComparator(Comparator<T> comparator) {
|
||||
transformableList.setComparator(comparator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link TransformableList#setComparator(Comparator, boolean)}.
|
||||
*/
|
||||
public void setComparator(Comparator<T> sorter, boolean reversed) {
|
||||
transformableList.setComparator(sorter, reversed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link TransformableList#isReversed()}.
|
||||
*/
|
||||
public boolean isReversed() {
|
||||
return transformableList.isReversed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link TransformableList#setReversed(boolean)}.
|
||||
*/
|
||||
public void setReversed(boolean reversed) {
|
||||
transformableList.setReversed(reversed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the wrapped {@link TransformableList}
|
||||
*/
|
||||
public TransformableList<T> getTransformableList() {
|
||||
return transformableList;
|
||||
}
|
||||
}
|
@ -1,250 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.base.AbstractMFXListView;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXCheckListCell;
|
||||
import io.github.palexdev.materialfx.skins.MFXListViewSkin;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.materialfx.theming.base.Theme;
|
||||
import io.github.palexdev.materialfx.utils.ListChangeHelper;
|
||||
import io.github.palexdev.materialfx.utils.ListChangeProcessor;
|
||||
import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
|
||||
import io.github.palexdev.virtualizedfx.unused.simple.SimpleVirtualFlow;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.scene.control.Skin;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Implementation of a check listview based on VirtualizedFX.
|
||||
* <p>
|
||||
* Extends {@link AbstractMFXListView}.
|
||||
* <p></p>
|
||||
* Default cell: {@link MFXCheckListCell}.
|
||||
* <p>
|
||||
* Default skin: {@link MFXListViewSkin}.
|
||||
* <p></p>
|
||||
* Holds the reference to the VirtualFlow used by the list, this allows to expose some
|
||||
* methods from it as delegate method thus allowing to:
|
||||
* <p> - Manually scroll by a certain amount of pixels
|
||||
* <p> - Manually scroll to a given index (also first and last)
|
||||
* <p> - Manually scroll to the given pixel value
|
||||
* <p> - Set the scrollbar's speed
|
||||
* <p> - Get the vertical or horizontal position of the list
|
||||
* <p> - Configure extra features of the VirtualFlow, {@link #features()}
|
||||
* <p> - Get the currently shown cells, or a specific cell by index
|
||||
* <p></p>
|
||||
* It's also responsible for updating the selection model in case the items list property
|
||||
* changes, or changes occur in the items list.
|
||||
*/
|
||||
public class MFXCheckListView<T> extends AbstractMFXListView<T, MFXCheckListCell<T>> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-check-list-view";
|
||||
private final SimpleVirtualFlow<T, MFXCheckListCell<T>> virtualFlow;
|
||||
private final ListChangeListener<? super T> itemsChanged = this::itemsChanged;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.mfxcore.utils.fx.SceneBuilderIntegration;
|
||||
import io.github.palexdev.virtualizedfx.cells.base.VFXCell;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Orientation;
|
||||
|
||||
public class MFXCheckListView<T, C extends VFXCell<T>> extends MFXListView<T, C> {
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXCheckListView() {
|
||||
virtualFlow = new SimpleVirtualFlow<>(
|
||||
itemsProperty(),
|
||||
null,
|
||||
Orientation.VERTICAL
|
||||
);
|
||||
initialize();
|
||||
}
|
||||
|
||||
public MFXCheckListView(ObservableList<T> items, Function<T, C> cellFactory) {
|
||||
super(items, cellFactory);
|
||||
}
|
||||
|
||||
public MFXCheckListView(ObservableList<T> items) {
|
||||
super(items);
|
||||
virtualFlow = new SimpleVirtualFlow<>(
|
||||
itemsProperty(),
|
||||
null,
|
||||
Orientation.VERTICAL
|
||||
);
|
||||
initialize();
|
||||
public MFXCheckListView(ObservableList<T> items, Function<T, C> cellFactory, Orientation orientation) {
|
||||
super(items, cellFactory, orientation);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected void initialize() {
|
||||
super.initialize();
|
||||
getStyleClass().add(STYLE_CLASS);
|
||||
items.addListener((observable, oldValue, newValue) -> {
|
||||
oldValue.removeListener(itemsChanged);
|
||||
newValue.addListener(itemsChanged);
|
||||
});
|
||||
getItems().addListener(this::itemsChanged);
|
||||
}
|
||||
|
||||
protected void itemsChanged(ListChangeListener.Change<? extends T> change) {
|
||||
if (getSelectionModel().getSelection().isEmpty()) return;
|
||||
|
||||
if (change.getList().isEmpty()) {
|
||||
getSelectionModel().clearSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
ListChangeHelper.Change c = ListChangeHelper.processChange(change, IntegerRange.of(0, Integer.MAX_VALUE));
|
||||
ListChangeProcessor updater = new ListChangeProcessor(new HashSet<>(getSelectionModel().getSelection().keySet()));
|
||||
c.processReplacement((changed, removed) -> getSelectionModel().replaceSelection(changed.toArray(new Integer[0])));
|
||||
c.processAddition((from, to, added) -> {
|
||||
updater.computeAddition(added.size(), from);
|
||||
getSelectionModel().replaceSelection(updater.getIndexes().toArray(new Integer[0]));
|
||||
});
|
||||
c.processRemoval((from, to, removed) -> {
|
||||
updater.computeRemoval(removed, from);
|
||||
getSelectionModel().replaceSelection(updater.getIndexes().toArray(new Integer[0]));
|
||||
});
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getCell(int)}.
|
||||
*/
|
||||
public MFXCheckListCell<T> getCell(int index) {
|
||||
return virtualFlow.getCell(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getCells()}.
|
||||
*/
|
||||
public Map<Integer, MFXCheckListCell<T>> getCells() {
|
||||
return virtualFlow.getCells();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollBy(double)}.
|
||||
*/
|
||||
public void scrollBy(double pixels) {
|
||||
virtualFlow.scrollBy(pixels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollTo(int)}.
|
||||
*/
|
||||
public void scrollTo(int index) {
|
||||
virtualFlow.scrollTo(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollToFirst()}.
|
||||
*/
|
||||
public void scrollToFirst() {
|
||||
virtualFlow.scrollToFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollToLast()}.
|
||||
*/
|
||||
public void scrollToLast() {
|
||||
virtualFlow.scrollToLast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollToPixel(double)}.
|
||||
*/
|
||||
public void scrollToPixel(double pixel) {
|
||||
virtualFlow.scrollToPixel(pixel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#setHSpeed(double, double)}.
|
||||
*/
|
||||
public void setHSpeed(double unit, double block) {
|
||||
virtualFlow.setHSpeed(unit, block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#setVSpeed(double, double)}.
|
||||
*/
|
||||
public void setVSpeed(double unit, double block) {
|
||||
virtualFlow.setVSpeed(unit, block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getVerticalPosition()}.
|
||||
*/
|
||||
public double getVerticalPosition() {
|
||||
return virtualFlow.getVerticalPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getHorizontalPosition()}.
|
||||
*/
|
||||
public double getHorizontalPosition() {
|
||||
return virtualFlow.getHorizontalPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#features()}.
|
||||
*/
|
||||
public SimpleVirtualFlow<T, MFXCheckListCell<T>>.Features features() {
|
||||
return virtualFlow.features();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Override Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public Theme getTheme() {
|
||||
return MaterialFXStylesheets.CHECK_LIST_VIEW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default factory for the cells.
|
||||
*/
|
||||
@Override
|
||||
protected void setDefaultCellFactory() {
|
||||
setCellFactory(item -> new MFXCheckListCell<>(this, item));
|
||||
public void sceneBuilderIntegration() {
|
||||
SceneBuilderIntegration.ifInSceneBuilder(() -> getStylesheets().add(MaterialFXStylesheets.CHECK_LIST_VIEW.toData()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<T, MFXCheckListCell<T>> getCellFactory() {
|
||||
return virtualFlow.getCellFactory();
|
||||
public List<String> defaultStyleClasses() {
|
||||
return List.of("mfx-check-list");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectProperty<Function<T, MFXCheckListCell<T>>> cellFactoryProperty() {
|
||||
return virtualFlow.cellFactoryProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCellFactory(Function<T, MFXCheckListCell<T>> cellFactory) {
|
||||
virtualFlow.setCellFactory(cellFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new MFXListViewSkin<>(this, virtualFlow);
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,11 @@
|
||||
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.Alignment;
|
||||
import io.github.palexdev.materialfx.beans.PositionBean;
|
||||
import io.github.palexdev.materialfx.beans.properties.EventHandlerProperty;
|
||||
@ -33,18 +38,17 @@ import io.github.palexdev.materialfx.selection.ComboBoxSelectionModel;
|
||||
import io.github.palexdev.materialfx.skins.MFXComboBoxSkin;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.materialfx.theming.base.Theme;
|
||||
import io.github.palexdev.materialfx.utils.*;
|
||||
import io.github.palexdev.materialfx.utils.NodeUtils;
|
||||
import io.github.palexdev.materialfx.utils.StyleablePropertiesUtils;
|
||||
import io.github.palexdev.materialfx.utils.others.FunctionalStringConverter;
|
||||
import io.github.palexdev.materialfx.validation.MFXValidator;
|
||||
import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
|
||||
import io.github.palexdev.mfxresources.fonts.MFXFontIcon;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import io.github.palexdev.virtualizedfx.cells.base.VFXCell;
|
||||
import io.github.palexdev.virtualizedfx.events.VFXContainerEvent;
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.Interpolator;
|
||||
import javafx.animation.RotateTransition;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.css.CssMetaData;
|
||||
import javafx.css.PseudoClass;
|
||||
@ -60,13 +64,6 @@ import javafx.scene.control.Skin;
|
||||
import javafx.util.Duration;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A new, completely remade from scratch {@code ComboBox} for JavaFX.
|
||||
* <p>
|
||||
@ -97,11 +94,17 @@ public class MFXComboBox<T> extends MFXTextField implements MFXCombo<T> {
|
||||
private final BiFunctionProperty<Node, Boolean, Animation> animationProvider = new BiFunctionProperty<>();
|
||||
|
||||
private final ObjectProperty<T> value = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<StringConverter<T>> converter = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<ObservableList<T>> items = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<StringConverter<T>> converter = new SimpleObjectProperty<>() {
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
// VFX cells already have the converter but in this case we want to use the one from the combo box
|
||||
// Since the cells do not have it as a property we must send an update request to the cells when this changes
|
||||
fireEvent(new VFXContainerEvent(null, MFXComboBox.this, VFXContainerEvent.UPDATE));
|
||||
}
|
||||
};
|
||||
private final ListProperty<T> items = new SimpleListProperty<>();
|
||||
private final ComboBoxSelectionModel<T> selectionModel = new ComboBoxSelectionModel<>(items);
|
||||
private final FunctionProperty<T, Cell<T>> cellFactory = new FunctionProperty<>(t -> new MFXComboBoxCell<>(this, t));
|
||||
private final ListChangeListener<? super T> itemsChanged = this::itemsChanged;
|
||||
private final FunctionProperty<T, VFXCell<T>> cellFactory = new FunctionProperty<>(t -> new MFXComboBoxCell<>(this, t));
|
||||
private final ConsumerProperty<String> onCommit = new ConsumerProperty<>();
|
||||
private final ConsumerProperty<String> onCancel = new ConsumerProperty<>();
|
||||
|
||||
@ -155,12 +158,6 @@ public class MFXComboBox<T> extends MFXTextField implements MFXCombo<T> {
|
||||
setConverter(FunctionalStringConverter.to(t -> t != null ? t.toString() : ""));
|
||||
|
||||
showing.addListener(invalidated -> pseudoClassStateChanged(POPUP_OPEN_PSEUDO_CLASS, showing.get()));
|
||||
|
||||
items.addListener((observable, oldValue, newValue) -> {
|
||||
oldValue.removeListener(itemsChanged);
|
||||
newValue.addListener(itemsChanged);
|
||||
});
|
||||
getItems().addListener(this::itemsChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -240,40 +237,6 @@ public class MFXComboBox<T> extends MFXTextField implements MFXCombo<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for updating the selection when the items list changes.
|
||||
*/
|
||||
protected void itemsChanged(ListChangeListener.Change<? extends T> change) {
|
||||
if (getSelectedIndex() == -1) return;
|
||||
|
||||
if (change.getList().isEmpty()) {
|
||||
clearSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
ListChangeHelper.Change c = ListChangeHelper.processChange(change, IntegerRange.of(0, Integer.MAX_VALUE));
|
||||
Set<Integer> indexes = new HashSet<>();
|
||||
indexes.add(getSelectedIndex());
|
||||
ListChangeProcessor updater = new ListChangeProcessor(indexes);
|
||||
c.processReplacement((changed, removed) -> {
|
||||
int selected = getSelectedIndex();
|
||||
if (changed.contains(selected) || removed.contains(selected)) {
|
||||
selectItem(getItems().get(selected));
|
||||
}
|
||||
});
|
||||
c.processAddition((from, to, added) -> {
|
||||
updater.computeAddition(added.size(), from);
|
||||
selectIndex(updater.getIndexes().toArray(new Integer[0])[0]);
|
||||
});
|
||||
c.processRemoval((from, to, removed) -> {
|
||||
updater.computeRemoval(removed, from);
|
||||
int index = NumberUtils.clamp(updater.getIndexes().toArray(new Integer[0])[0], 0, getItems().size() - 1);
|
||||
selectIndex(index);
|
||||
});
|
||||
|
||||
setValue(getSelectedItem());
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@ -353,13 +316,6 @@ public class MFXComboBox<T> extends MFXTextField implements MFXCombo<T> {
|
||||
return selectionModel.getSelectedIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link ComboBoxSelectionModel#selectedIndexProperty()}.
|
||||
*/
|
||||
public ReadOnlyIntegerProperty selectedIndexProperty() {
|
||||
return selectionModel.selectedIndexProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link ComboBoxSelectionModel#getSelectedItem()}.
|
||||
*/
|
||||
@ -368,18 +324,10 @@ public class MFXComboBox<T> extends MFXTextField implements MFXCombo<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link ComboBoxSelectionModel#selectedItemProperty()}.
|
||||
* Delegate for {@link ComboBoxSelectionModel#selection()}.
|
||||
*/
|
||||
public ReadOnlyObjectProperty<T> selectedItemProperty() {
|
||||
return selectionModel.selectedItemProperty();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Validation
|
||||
//================================================================================
|
||||
@Override
|
||||
public MFXValidator getValidator() {
|
||||
return validator;
|
||||
public MapProperty<Integer, T> selection() {
|
||||
return selectionModel.selection();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
@ -606,7 +554,7 @@ public class MFXComboBox<T> extends MFXTextField implements MFXCombo<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectProperty<ObservableList<T>> itemsProperty() {
|
||||
public ListProperty<T> itemsProperty() {
|
||||
return items;
|
||||
}
|
||||
|
||||
@ -616,17 +564,17 @@ public class MFXComboBox<T> extends MFXTextField implements MFXCombo<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<T, Cell<T>> getCellFactory() {
|
||||
public Function<T, VFXCell<T>> getCellFactory() {
|
||||
return cellFactory.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectProperty<Function<T, Cell<T>>> cellFactoryProperty() {
|
||||
public ObjectProperty<Function<T, VFXCell<T>>> cellFactoryProperty() {
|
||||
return cellFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCellFactory(Function<T, Cell<T>> cellFactory) {
|
||||
public void setCellFactory(Function<T, VFXCell<T>> cellFactory) {
|
||||
this.cellFactory.set(cellFactory);
|
||||
}
|
||||
|
||||
|
@ -18,9 +18,11 @@
|
||||
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.properties.functional.FunctionProperty;
|
||||
import io.github.palexdev.materialfx.collections.TransformableList;
|
||||
import io.github.palexdev.materialfx.collections.TransformableListWrapper;
|
||||
import io.github.palexdev.materialfx.collections.RefineList;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXComboBoxCell;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXFilterComboBoxCell;
|
||||
import io.github.palexdev.materialfx.skins.MFXFilterComboBoxSkin;
|
||||
@ -34,9 +36,6 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.Skin;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Extends {@link MFXComboBox} and changes the popup's content slightly to
|
||||
* allow filtering the items list.
|
||||
@ -52,6 +51,7 @@ import java.util.function.Predicate;
|
||||
* Note: this combo box do not use {@link MFXComboBoxCell} and while it does allow it it should never be used.
|
||||
* Use {@link MFXFilterComboBoxCell} instead for consistent selection behavior.
|
||||
*/
|
||||
// TODO check this mess
|
||||
public class MFXFilterComboBox<T> extends MFXComboBox<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
@ -59,11 +59,11 @@ public class MFXFilterComboBox<T> extends MFXComboBox<T> {
|
||||
private final String STYLECLASS = "mfx-filter-combo-box";
|
||||
|
||||
private final StringProperty searchText = new SimpleStringProperty();
|
||||
private final TransformableListWrapper<T> filterList = new TransformableListWrapper<>(FXCollections.observableArrayList());
|
||||
private final RefineList<T> refineList = new RefineList<>(FXCollections.observableArrayList());
|
||||
private final FunctionProperty<String, Predicate<T>> filterFunction = new FunctionProperty<>(s -> t -> StringUtils.containsIgnoreCase(t.toString(), s));
|
||||
private boolean resetOnPopupHidden = true;
|
||||
|
||||
private final InvalidationListener itemsChanged = invalidated -> filterList.setAll(getItems());
|
||||
private final InvalidationListener itemsChanged = invalidated -> refineList.setAll(getItems());
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
@ -85,12 +85,12 @@ public class MFXFilterComboBox<T> extends MFXComboBox<T> {
|
||||
getStyleClass().add(STYLECLASS);
|
||||
setCellFactory(t -> new MFXFilterComboBoxCell<>(this, getFilterList(), t));
|
||||
|
||||
filterList.setAll(getItems());
|
||||
refineList.setAll(getItems());
|
||||
itemsProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (oldValue != null) oldValue.removeListener(itemsChanged);
|
||||
if (newValue != null) {
|
||||
newValue.addListener(itemsChanged);
|
||||
filterList.setAll(newValue);
|
||||
refineList.setAll(newValue);
|
||||
}
|
||||
});
|
||||
getItems().addListener(itemsChanged);
|
||||
@ -117,8 +117,8 @@ public class MFXFilterComboBox<T> extends MFXComboBox<T> {
|
||||
this.searchText.set(searchText);
|
||||
}
|
||||
|
||||
public TransformableList<T> getFilterList() {
|
||||
return filterList.getTransformableList();
|
||||
public RefineList<T> getFilterList() {
|
||||
return refineList;
|
||||
}
|
||||
|
||||
public Function<String, Predicate<T>> getFilterFunction() {
|
||||
|
350
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXListView.java
Executable file → Normal file
350
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXListView.java
Executable file → Normal file
@ -1,248 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.base.AbstractMFXListView;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXListCell;
|
||||
import io.github.palexdev.materialfx.skins.MFXListViewSkin;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.materialfx.theming.base.Theme;
|
||||
import io.github.palexdev.materialfx.utils.ListChangeHelper;
|
||||
import io.github.palexdev.materialfx.utils.ListChangeProcessor;
|
||||
import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
|
||||
import io.github.palexdev.virtualizedfx.unused.simple.SimpleVirtualFlow;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.scene.control.Skin;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Implementation of a check listview based on VirtualizedFX.
|
||||
* <p>
|
||||
* Extends {@link AbstractMFXListView}.
|
||||
* <p></p>
|
||||
* Default cell: {@link MFXListCell}.
|
||||
* <p>
|
||||
* Default skin: {@link MFXListViewSkin}.
|
||||
* <p></p>
|
||||
* Holds the reference to the VirtualFlow used by the list, this allows to expose some
|
||||
* methods from it as delegate method thus allowing to:
|
||||
* <p> - Manually scroll by a certain amount of pixels
|
||||
* <p> - Manually scroll to a given index (also first and last)
|
||||
* <p> - Manually scroll to the given pixel value
|
||||
* <p> - Set the scrollbar's speed
|
||||
* <p> - Get the vertical or horizontal position of the list
|
||||
* <p> - Configure extra features of the VirtualFlow, {@link #features()}
|
||||
* <p> - Get the currently shown cells, or a specific cell by index
|
||||
* <p></p>
|
||||
* It's also responsible for updating the selection model in case the items list property
|
||||
* changes, or changes occur in the items list.
|
||||
*/
|
||||
public class MFXListView<T> extends AbstractMFXListView<T, MFXListCell<T>> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-list-view";
|
||||
private final SimpleVirtualFlow<T, MFXListCell<T>> virtualFlow;
|
||||
private final ListChangeListener<? super T> itemsChanged = this::itemsChanged;
|
||||
import io.github.palexdev.materialfx.beans.properties.styleable.StyleableObjectProperty;
|
||||
import io.github.palexdev.materialfx.selection.base.ISelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.SelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.base.WithSelectionModel;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.mfxcore.utils.fx.SceneBuilderIntegration;
|
||||
import io.github.palexdev.mfxcore.utils.fx.StyleUtils;
|
||||
import io.github.palexdev.mfxeffects.enums.ElevationLevel;
|
||||
import io.github.palexdev.virtualizedfx.cells.base.VFXCell;
|
||||
import io.github.palexdev.virtualizedfx.controls.VFXScrollPane;
|
||||
import io.github.palexdev.virtualizedfx.list.VFXList;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.css.CssMetaData;
|
||||
import javafx.css.Styleable;
|
||||
import javafx.css.StyleablePropertyFactory;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXListView() {
|
||||
virtualFlow = new SimpleVirtualFlow<>(
|
||||
itemsProperty(),
|
||||
null,
|
||||
Orientation.VERTICAL
|
||||
);
|
||||
initialize();
|
||||
}
|
||||
public class MFXListView<T, C extends VFXCell<T>> extends VFXList<T, C> implements WithSelectionModel<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final ISelectionModel<T> selectionModel = new SelectionModel<>(itemsProperty());
|
||||
|
||||
public MFXListView(ObservableList<T> items) {
|
||||
super(items);
|
||||
virtualFlow = new SimpleVirtualFlow<>(
|
||||
itemsProperty(),
|
||||
null,
|
||||
Orientation.VERTICAL
|
||||
);
|
||||
initialize();
|
||||
}
|
||||
private VFXScrollPane vsp;
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected void initialize() {
|
||||
super.initialize();
|
||||
getStyleClass().setAll(STYLE_CLASS);
|
||||
items.addListener((observable, oldValue, newValue) -> {
|
||||
if (oldValue != null) oldValue.removeListener(itemsChanged);
|
||||
if (newValue != null) newValue.removeListener(itemsChanged);
|
||||
});
|
||||
getItems().addListener(itemsChanged);
|
||||
}
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXListView() {
|
||||
initialize();
|
||||
}
|
||||
|
||||
protected void itemsChanged(ListChangeListener.Change<? extends T> change) {
|
||||
if (getSelectionModel().getSelection().isEmpty()) return;
|
||||
public MFXListView(ObservableList<T> items, Function<T, C> cellFactory) {
|
||||
super(items, cellFactory);
|
||||
initialize();
|
||||
}
|
||||
|
||||
if (change.getList().isEmpty()) {
|
||||
getSelectionModel().clearSelection();
|
||||
return;
|
||||
}
|
||||
public MFXListView(ObservableList<T> items, Function<T, C> cellFactory, Orientation orientation) {
|
||||
super(items, cellFactory, orientation);
|
||||
initialize();
|
||||
}
|
||||
|
||||
ListChangeHelper.Change c = ListChangeHelper.processChange(change, IntegerRange.of(0, Integer.MAX_VALUE));
|
||||
ListChangeProcessor updater = new ListChangeProcessor(new HashSet<>(getSelectionModel().getSelection().keySet()));
|
||||
c.processReplacement((changed, removed) -> getSelectionModel().replaceSelection(changed.toArray(new Integer[0])));
|
||||
c.processAddition((from, to, added) -> {
|
||||
updater.computeAddition(added.size(), from);
|
||||
getSelectionModel().replaceSelection(updater.getIndexes().toArray(new Integer[0]));
|
||||
});
|
||||
c.processRemoval((from, to, removed) -> {
|
||||
updater.computeRemoval(removed, from);
|
||||
getSelectionModel().replaceSelection(updater.getIndexes().toArray(new Integer[0]));
|
||||
});
|
||||
}
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
private void initialize() {
|
||||
sceneBuilderIntegration();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
protected void findVsp() {
|
||||
Parent parent = getParent();
|
||||
while (parent != null) {
|
||||
if (parent instanceof VFXScrollPane p) {
|
||||
vsp = p;
|
||||
onDepthChanged();
|
||||
break;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getCell(int)}.
|
||||
*/
|
||||
public MFXListCell<T> getCell(int index) {
|
||||
return virtualFlow.getCell(index);
|
||||
}
|
||||
protected void onDepthChanged() {
|
||||
ElevationLevel level = getDepth();
|
||||
DropShadow effect = (level == null || level == ElevationLevel.LEVEL0) ? null : level.toShadow();
|
||||
if (vsp == null) findVsp();
|
||||
if (vsp != null) vsp.setEffect(effect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getCells()}.
|
||||
*/
|
||||
public Map<Integer, MFXListCell<T>> getCells() {
|
||||
return virtualFlow.getCells();
|
||||
}
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public ISelectionModel<T> getSelectionModel() {
|
||||
return selectionModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollBy(double)}.
|
||||
*/
|
||||
public void scrollBy(double pixels) {
|
||||
virtualFlow.scrollBy(pixels);
|
||||
}
|
||||
@Override
|
||||
public void sceneBuilderIntegration() {
|
||||
SceneBuilderIntegration.ifInSceneBuilder(() -> getStylesheets().add(MaterialFXStylesheets.LIST_VIEW.toData()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollTo(int)}.
|
||||
*/
|
||||
public void scrollTo(int index) {
|
||||
virtualFlow.scrollTo(index);
|
||||
}
|
||||
@Override
|
||||
public List<String> defaultStyleClasses() {
|
||||
return List.of("mfx-list-view");
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollToFirst()}.
|
||||
*/
|
||||
public void scrollToFirst() {
|
||||
virtualFlow.scrollToFirst();
|
||||
}
|
||||
//================================================================================
|
||||
// Styleable Properties
|
||||
//================================================================================
|
||||
private final StyleableObjectProperty<ElevationLevel> depth = new StyleableObjectProperty<>(
|
||||
StyleableProperties.DEPTH,
|
||||
this,
|
||||
"depth",
|
||||
ElevationLevel.LEVEL0
|
||||
) {
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
onDepthChanged();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollToLast()}.
|
||||
*/
|
||||
public void scrollToLast() {
|
||||
virtualFlow.scrollToLast();
|
||||
}
|
||||
public ElevationLevel getDepth() {
|
||||
return depth.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollToPixel(double)}.
|
||||
*/
|
||||
public void scrollToPixel(double pixel) {
|
||||
virtualFlow.scrollToPixel(pixel);
|
||||
}
|
||||
public StyleableObjectProperty<ElevationLevel> depthProperty() {
|
||||
return depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#setHSpeed(double, double)}.
|
||||
*/
|
||||
public void setHSpeed(double unit, double block) {
|
||||
virtualFlow.setHSpeed(unit, block);
|
||||
}
|
||||
public void setDepth(ElevationLevel depth) {
|
||||
this.depth.set(depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#setVSpeed(double, double)}.
|
||||
*/
|
||||
public void setVSpeed(double unit, double block) {
|
||||
virtualFlow.setVSpeed(unit, block);
|
||||
}
|
||||
//================================================================================
|
||||
// CssMetaData
|
||||
//================================================================================
|
||||
private static class StyleableProperties {
|
||||
private static final StyleablePropertyFactory<MFXListView<?, ?>> FACTORY = new StyleablePropertyFactory<>(VFXList.getClassCssMetaData());
|
||||
private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getVerticalPosition()}.
|
||||
*/
|
||||
public double getVerticalPosition() {
|
||||
return virtualFlow.getVerticalPosition();
|
||||
}
|
||||
private static final CssMetaData<MFXListView<?, ?>, ElevationLevel> DEPTH =
|
||||
FACTORY.createEnumCssMetaData(
|
||||
ElevationLevel.class,
|
||||
"-mfx-depth",
|
||||
MFXListView::depthProperty,
|
||||
ElevationLevel.LEVEL0
|
||||
);
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getHorizontalPosition()}.
|
||||
*/
|
||||
public double getHorizontalPosition() {
|
||||
return virtualFlow.getHorizontalPosition();
|
||||
}
|
||||
static {
|
||||
cssMetaDataList = StyleUtils.cssMetaDataList(
|
||||
VFXList.getClassCssMetaData(),
|
||||
DEPTH
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#features()}.
|
||||
*/
|
||||
public SimpleVirtualFlow<T, MFXListCell<T>>.Features features() {
|
||||
return virtualFlow.features();
|
||||
}
|
||||
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
|
||||
return StyleableProperties.cssMetaDataList;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Override Methods
|
||||
//================================================================================
|
||||
|
||||
@Override
|
||||
public Theme getTheme() {
|
||||
return MaterialFXStylesheets.LIST_VIEW;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDefaultCellFactory() {
|
||||
setCellFactory(item -> new MFXListCell<>(this, item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<T, MFXListCell<T>> getCellFactory() {
|
||||
return virtualFlow.getCellFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectProperty<Function<T, MFXListCell<T>>> cellFactoryProperty() {
|
||||
return virtualFlow.cellFactoryProperty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCellFactory(Function<T, MFXListCell<T>> cellFactory) {
|
||||
virtualFlow.setCellFactory(cellFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new MFXListViewSkin<>(this, virtualFlow);
|
||||
}
|
||||
@Override
|
||||
protected List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
|
||||
return getClassCssMetaData();
|
||||
}
|
||||
}
|
||||
|
@ -18,15 +18,23 @@
|
||||
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import io.github.palexdev.materialfx.collections.TransformableListWrapper;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import io.github.palexdev.materialfx.collections.RefineList;
|
||||
import io.github.palexdev.materialfx.controls.base.MFXMenuControl;
|
||||
import io.github.palexdev.materialfx.controls.base.Themable;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXNotificationCell;
|
||||
import io.github.palexdev.materialfx.enums.NotificationCounterStyle;
|
||||
import io.github.palexdev.materialfx.enums.NotificationState;
|
||||
import io.github.palexdev.materialfx.i18n.I18N;
|
||||
import io.github.palexdev.materialfx.selection.base.ISelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.SelectionModel;
|
||||
import io.github.palexdev.materialfx.notifications.base.INotification;
|
||||
import io.github.palexdev.materialfx.selection.MultipleSelectionModel;
|
||||
import io.github.palexdev.materialfx.skins.MFXNotificationCenterSkin;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.materialfx.theming.base.Theme;
|
||||
@ -35,7 +43,6 @@ import io.github.palexdev.materialfx.utils.ListChangeHelper.Change;
|
||||
import io.github.palexdev.materialfx.utils.ListChangeProcessor;
|
||||
import io.github.palexdev.materialfx.utils.others.ReusableScheduledExecutor;
|
||||
import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
|
||||
import io.github.palexdev.virtualizedfx.unused.simple.SimpleVirtualFlow;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.LongBinding;
|
||||
import javafx.beans.property.*;
|
||||
@ -49,19 +56,12 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static io.github.palexdev.materialfx.enums.NotificationCounterStyle.NUMBER;
|
||||
|
||||
/**
|
||||
* A quite complex but easy to use implementation of a modern notification center.
|
||||
* <p></p>
|
||||
* For the notifications it uses {@link TransformableListWrapper} as list implementation, this allows
|
||||
* For the notifications it uses {@link RefineList} as list implementation, this allows
|
||||
* not only basic operations such additions, removals and replacements, but also filter and sort operations.
|
||||
* <p></p>
|
||||
* It's composed by an icon and a popup that contains the list of notifications.
|
||||
@ -99,10 +99,10 @@ public class MFXNotificationCenter extends Control implements MFXMenuControl, Th
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-notification-center";
|
||||
|
||||
private final TransformableListWrapper<INotification> notifications = new TransformableListWrapper<>(FXCollections.observableArrayList());
|
||||
private final RefineList<INotification> notifications = new RefineList<>(FXCollections.observableArrayList());
|
||||
|
||||
private final SimpleVirtualFlow<INotification, MFXNotificationCell> virtualFlow;
|
||||
private final MultipleSelectionModel<INotification> selectionModel = new MultipleSelectionModel<>(notifications);
|
||||
private final MFXListView<INotification, MFXNotificationCell> listView;
|
||||
private final ISelectionModel<INotification> selectionModel = new SelectionModel<>(notifications);
|
||||
|
||||
private final BooleanProperty selectionMode = new SimpleBooleanProperty(false);
|
||||
private final ReadOnlyLongWrapper unreadCount = new ReadOnlyLongWrapper(0);
|
||||
@ -136,7 +136,7 @@ public class MFXNotificationCenter extends Control implements MFXMenuControl, Th
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXNotificationCenter() {
|
||||
virtualFlow = new SimpleVirtualFlow<>(
|
||||
listView = new MFXListView<>(
|
||||
notifications,
|
||||
notification -> new MFXNotificationCell(this, notification),
|
||||
Orientation.VERTICAL
|
||||
@ -170,12 +170,12 @@ public class MFXNotificationCenter extends Control implements MFXMenuControl, Th
|
||||
|
||||
unreadCount.bind(unreadCountBinding);
|
||||
notifications.addListener((ListChangeListener<? super INotification>) change -> {
|
||||
if (!selectionModel.getSelection().isEmpty()) {
|
||||
if (!selectionModel.isEmpty()) {
|
||||
if (change.getList().isEmpty()) {
|
||||
selectionModel.clearSelection();
|
||||
} else {
|
||||
Change c = ListChangeHelper.processChange(change, IntegerRange.of(0, Integer.MAX_VALUE));
|
||||
ListChangeProcessor updater = new ListChangeProcessor(selectionModel.getSelection().keySet());
|
||||
ListChangeProcessor updater = new ListChangeProcessor(selectionModel.selection().keySet());
|
||||
c.processReplacement((changed, removed) -> selectionModel.replaceSelection(changed.toArray(new Integer[0])));
|
||||
c.processAddition((from, to, added) -> {
|
||||
updater.computeAddition(added.size(), from);
|
||||
@ -288,7 +288,7 @@ public class MFXNotificationCenter extends Control implements MFXMenuControl, Th
|
||||
.get();
|
||||
|
||||
|
||||
contextMenu = MFXContextMenu.Builder.build(virtualFlow)
|
||||
contextMenu = MFXContextMenu.Builder.build(listView)
|
||||
.addSeparator(new Label(I18N.getOrDefault("notificationCenter.contextMenu.selectionSeparator")))
|
||||
.addItems(selectAll, selectRead, selectUnread, clearSelection)
|
||||
.addSeparator(new Label(I18N.getOrDefault("notificationCenter.contextMenu.sortingSeparator")))
|
||||
@ -340,7 +340,7 @@ public class MFXNotificationCenter extends Control implements MFXMenuControl, Th
|
||||
markNotificationsAs(
|
||||
state,
|
||||
getCells().values().stream()
|
||||
.map(MFXNotificationCell::getNotification)
|
||||
.map(MFXNotificationCell::getItem)
|
||||
.toArray(INotification[]::new)
|
||||
);
|
||||
}
|
||||
@ -349,7 +349,7 @@ public class MFXNotificationCenter extends Control implements MFXMenuControl, Th
|
||||
* Sets all the selected notifications' state to the given state.
|
||||
*/
|
||||
public void markSelectedNotificationsAs(NotificationState state) {
|
||||
markNotificationsAs(state, selectionModel.getSelection().values().toArray(INotification[]::new));
|
||||
markNotificationsAs(state, selectionModel.selection().values().toArray(INotification[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -373,14 +373,14 @@ public class MFXNotificationCenter extends Control implements MFXMenuControl, Th
|
||||
* Sets all the visible notifications' state to READ, then removes them from the notifications list.
|
||||
*/
|
||||
public void dismissVisible() {
|
||||
dismiss(getCells().values().stream().map(MFXNotificationCell::getNotification).toArray(INotification[]::new));
|
||||
dismiss(getCells().values().stream().map(MFXNotificationCell::getItem).toArray(INotification[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all the selected notifications' state to READ, then removes them from the notifications list.
|
||||
*/
|
||||
public void dismissSelected() {
|
||||
dismiss(getSelectionModel().getSelection().values().toArray(INotification[]::new));
|
||||
dismiss(getSelectionModel().selection().values().toArray(INotification[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -397,14 +397,14 @@ public class MFXNotificationCenter extends Control implements MFXMenuControl, Th
|
||||
/**
|
||||
* @return the list of notifications
|
||||
*/
|
||||
public TransformableListWrapper<INotification> getNotifications() {
|
||||
public RefineList<INotification> getNotifications() {
|
||||
return notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the selection model instance used to keep track of selected notifications
|
||||
*/
|
||||
public MultipleSelectionModel<INotification> getSelectionModel() {
|
||||
public ISelectionModel<INotification> getSelectionModel() {
|
||||
return selectionModel;
|
||||
}
|
||||
|
||||
@ -615,95 +615,40 @@ public class MFXNotificationCenter extends Control implements MFXMenuControl, Th
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getCell(int)}.
|
||||
*/
|
||||
public MFXNotificationCell getCell(int index) {
|
||||
return virtualFlow.getCell(index);
|
||||
return listView.getCellsByIndexUnmodifiable().get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getCells()}.
|
||||
*/
|
||||
public Map<Integer, MFXNotificationCell> getCells() {
|
||||
return virtualFlow.getCells();
|
||||
return listView.getCellsByIndexUnmodifiable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollBy(double)}.
|
||||
*/
|
||||
public void scrollBy(double pixels) {
|
||||
virtualFlow.scrollBy(pixels);
|
||||
listView.scrollBy(pixels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollTo(int)}.
|
||||
*/
|
||||
public void scrollTo(int index) {
|
||||
virtualFlow.scrollTo(index);
|
||||
listView.scrollToIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollToFirst()}.
|
||||
*/
|
||||
public void scrollToFirst() {
|
||||
virtualFlow.scrollToFirst();
|
||||
listView.scrollToFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollToLast()}.
|
||||
*/
|
||||
public void scrollToLast() {
|
||||
virtualFlow.scrollToLast();
|
||||
listView.scrollToLast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#scrollToPixel(double)}.
|
||||
*/
|
||||
public void scrollToPixel(double pixel) {
|
||||
virtualFlow.scrollToPixel(pixel);
|
||||
listView.scrollToPixel(pixel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#setHSpeed(double, double)}.
|
||||
*/
|
||||
public void setHSpeed(double unit, double block) {
|
||||
virtualFlow.setHSpeed(unit, block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#setVSpeed(double, double)}.
|
||||
*/
|
||||
public void setVSpeed(double unit, double block) {
|
||||
virtualFlow.setVSpeed(unit, block);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getVerticalPosition()}.
|
||||
*/
|
||||
public double getVerticalPosition() {
|
||||
return virtualFlow.getVerticalPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#getHorizontalPosition()}.
|
||||
*/
|
||||
public double getHorizontalPosition() {
|
||||
return virtualFlow.getHorizontalPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#setCellFactory(Function)}.
|
||||
*/
|
||||
public void setCellFactory(Function<INotification, MFXNotificationCell> cellFactory) {
|
||||
virtualFlow.setCellFactory(cellFactory);
|
||||
listView.setCellFactory(cellFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SimpleVirtualFlow#features()}.
|
||||
*/
|
||||
public SimpleVirtualFlow<INotification, MFXNotificationCell>.Features features() {
|
||||
return virtualFlow.features();
|
||||
public void setCellHeight(double height) {
|
||||
listView.setCellSize(height);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
@ -722,6 +667,6 @@ public class MFXNotificationCenter extends Control implements MFXMenuControl, Th
|
||||
|
||||
@Override
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new MFXNotificationCenterSkin(this, virtualFlow);
|
||||
return new MFXNotificationCenterSkin(this, listView);
|
||||
}
|
||||
}
|
||||
|
@ -18,19 +18,6 @@
|
||||
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import io.github.palexdev.materialfx.skins.MFXPaginatedTableViewSkin;
|
||||
import io.github.palexdev.materialfx.utils.NumberUtils;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||
import javafx.beans.property.ReadOnlyIntegerWrapper;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.event.Event;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.input.ScrollEvent;
|
||||
|
||||
/**
|
||||
* This is the implementation of a paginated {@link MFXTableView}.
|
||||
* <p>
|
||||
@ -47,7 +34,7 @@ import javafx.scene.input.ScrollEvent;
|
||||
* @param <T> The type of the data within the table.
|
||||
*/
|
||||
public class MFXPaginatedTableView<T> extends MFXTableView<T> {
|
||||
//================================================================================
|
||||
/* //================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-paginated-table-view";
|
||||
@ -96,20 +83,20 @@ public class MFXPaginatedTableView<T> extends MFXTableView<T> {
|
||||
currentPageProperty().addListener(invalidated -> goToPage(getCurrentPage()));
|
||||
}
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Goes to the given page index.
|
||||
* <p>
|
||||
* The given integer is clamped between 1 and the max page index.
|
||||
*/
|
||||
*//*
|
||||
public void goToPage(int index) {
|
||||
int page = NumberUtils.clamp(index, 1, getMaxPage());
|
||||
double pos = (page - 1) * getRowsPerPage() * rowsFlow.getCellHeight();
|
||||
rowsFlow.getVBar().setValue(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Responsible for updating the max page index when needed.
|
||||
*/
|
||||
*//*
|
||||
private void updateMaxPages() {
|
||||
int size = getTransformableList().size();
|
||||
int rowsPerPage = getRowsPerPage();
|
||||
@ -121,41 +108,41 @@ public class MFXPaginatedTableView<T> extends MFXTableView<T> {
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Unsupported by the paginated table view.
|
||||
*/
|
||||
*//*
|
||||
@Override
|
||||
public void scrollBy(double pixels) {
|
||||
throw new UnsupportedOperationException("The paginated table view cannot scroll ny pixels");
|
||||
}
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Calls {@link #goToPage(int)}.
|
||||
*/
|
||||
*//*
|
||||
@Override
|
||||
public void scrollTo(int index) {
|
||||
goToPage(index);
|
||||
}
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Goes to the first page.
|
||||
*/
|
||||
*//*
|
||||
@Override
|
||||
public void scrollToFirst() {
|
||||
goToPage(1);
|
||||
}
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Goes to the last page.
|
||||
*/
|
||||
*//*
|
||||
@Override
|
||||
public void scrollToLast() {
|
||||
goToPage(getMaxPage());
|
||||
}
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Unsupported by the paginated table view.
|
||||
*/
|
||||
*//*
|
||||
@Override
|
||||
public void scrollToPixel(double pixel) {
|
||||
throw new UnsupportedOperationException("The paginated table view cannot scroll to pixel");
|
||||
@ -173,9 +160,9 @@ public class MFXPaginatedTableView<T> extends MFXTableView<T> {
|
||||
return currentPage.get();
|
||||
}
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Specifies the current shown page.
|
||||
*/
|
||||
*//*
|
||||
public IntegerProperty currentPageProperty() {
|
||||
return currentPage;
|
||||
}
|
||||
@ -188,9 +175,9 @@ public class MFXPaginatedTableView<T> extends MFXTableView<T> {
|
||||
return maxPage.get();
|
||||
}
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Specifies the last page index.
|
||||
*/
|
||||
*//*
|
||||
public ReadOnlyIntegerProperty maxPageProperty() {
|
||||
return maxPage.getReadOnlyProperty();
|
||||
}
|
||||
@ -203,10 +190,10 @@ public class MFXPaginatedTableView<T> extends MFXTableView<T> {
|
||||
return pagesToShow.get();
|
||||
}
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Specifies how many pages can be shown at a time by the
|
||||
* {@link MFXPagination} control used in the skin.
|
||||
*/
|
||||
*//*
|
||||
public IntegerProperty pagesToShowProperty() {
|
||||
return pagesToShow;
|
||||
}
|
||||
@ -219,9 +206,9 @@ public class MFXPaginatedTableView<T> extends MFXTableView<T> {
|
||||
return rowsPerPage.get();
|
||||
}
|
||||
|
||||
/**
|
||||
*//**
|
||||
* Specifies how many rows the table can show per page.
|
||||
*/
|
||||
*//*
|
||||
public IntegerProperty rowsPerPageProperty() {
|
||||
return rowsPerPage;
|
||||
}
|
||||
@ -229,4 +216,5 @@ public class MFXPaginatedTableView<T> extends MFXTableView<T> {
|
||||
public void setRowsPerPage(int rowsPerPage) {
|
||||
this.rowsPerPage.set(rowsPerPage);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -18,6 +18,12 @@
|
||||
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.properties.functional.FunctionProperty;
|
||||
import io.github.palexdev.materialfx.beans.properties.functional.SupplierProperty;
|
||||
import io.github.palexdev.materialfx.controls.base.Themable;
|
||||
@ -32,12 +38,6 @@ import javafx.scene.Parent;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.scene.control.Skin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* This is the implementation of a smart, material pagination control in JavaFX.
|
||||
* <p>
|
||||
@ -187,7 +187,7 @@ public class MFXPagination extends Control implements Themable {
|
||||
lastMax++;
|
||||
after = false;
|
||||
} else {
|
||||
indexes.add(0, lastMin);
|
||||
indexes.addFirst(lastMin);
|
||||
lastMin--;
|
||||
after = true;
|
||||
}
|
||||
|
@ -1,285 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.PositionBean;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXTableRowCell;
|
||||
import io.github.palexdev.materialfx.effects.ripple.MFXCircleRippleGenerator;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import io.github.palexdev.virtualizedfx.unused.simple.SimpleVirtualFlow;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is the HBox that contains the table row cells built by each column.
|
||||
* <p></p>
|
||||
* The new implementation of the table view makes use of {@link SimpleVirtualFlow} to contain
|
||||
* the rows, this makes the table view super efficient, that's also why the new {@code MFXTableRow}
|
||||
* implements {@link Cell}.
|
||||
* <p></p>
|
||||
* {@link MFXTableRowCell} though, are not reusable {@link Cell}s. So, to keep things efficient table rows
|
||||
* now build the cells only once (or when needed by the table view) and simply updates them when the {@link #dataProperty()} changes,
|
||||
* by using {@link #updateCells(Object)}. This mechanism should also simplify working with non JavaFX models (which do not use observables).
|
||||
*/
|
||||
public class MFXTableRow<T> extends HBox implements Cell<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-table-row";
|
||||
|
||||
private final MFXTableView<T> tableView;
|
||||
private final ObservableList<MFXTableRowCell<T, ?>> cells = FXCollections.observableArrayList();
|
||||
private final ReadOnlyIntegerWrapper index = new ReadOnlyIntegerWrapper();
|
||||
private final ReadOnlyObjectWrapper<T> data = new ReadOnlyObjectWrapper<>();
|
||||
|
||||
protected final MFXCircleRippleGenerator rippleGenerator = new MFXCircleRippleGenerator(this);
|
||||
|
||||
private final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper();
|
||||
protected static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected");
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXTableRow(MFXTableView<T> tableView, T data) {
|
||||
this.tableView = tableView;
|
||||
setData(data);
|
||||
setMinHeight(USE_PREF_SIZE);
|
||||
setPrefHeight(32);
|
||||
setMaxHeight(USE_PREF_SIZE);
|
||||
initialize();
|
||||
buildCells();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
private void initialize() {
|
||||
getStyleClass().add(STYLE_CLASS);
|
||||
|
||||
setBehavior();
|
||||
setupRippleGenerator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the needed listeners/handlers to manage the selection state.
|
||||
*
|
||||
* @see #updateSelection(MouseEvent).
|
||||
*/
|
||||
private void setBehavior() {
|
||||
selected.addListener(invalidated -> pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, selected.get()));
|
||||
selected.bind(Bindings.createBooleanBinding(
|
||||
() -> tableView.getSelectionModel().getSelection().containsKey(getIndex()),
|
||||
tableView.getSelectionModel().selectionProperty(), index
|
||||
));
|
||||
|
||||
addEventFilter(MouseEvent.MOUSE_CLICKED, this::updateSelection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the ripple generator.
|
||||
*/
|
||||
protected void setupRippleGenerator() {
|
||||
rippleGenerator.setManaged(false);
|
||||
rippleGenerator.setRipplePositionFunction(event -> PositionBean.of(event.getX(), event.getY()));
|
||||
rippleGenerator.rippleRadiusProperty().bind(widthProperty().divide(2.0));
|
||||
addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
|
||||
if (event.getButton() == MouseButton.PRIMARY) {
|
||||
rippleGenerator.generateRipple(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API to update the row's cells.
|
||||
* <p>
|
||||
* Simply calls {@link #updateRow(Object)} given that
|
||||
* the current {@link #dataProperty()} is not null.
|
||||
*/
|
||||
public void updateRow() {
|
||||
T data = getData();
|
||||
if (data == null) return;
|
||||
updateRow(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the {@link #updateItem(Object)} method.
|
||||
* Responsible for updating the cells, {@link #updateCells(Object)}, or building them
|
||||
* if not yet done, {@link #buildCells()}.
|
||||
*/
|
||||
protected void updateRow(T data) {
|
||||
if (cells.isEmpty()) {
|
||||
buildCells();
|
||||
} else {
|
||||
updateCells(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for updating the row cells by calling {@link MFXTableRowCell#update(Object)}.
|
||||
*/
|
||||
protected void updateCells(T data) {
|
||||
cells.forEach(cell -> cell.update(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for building the row's cells when needed.
|
||||
* <p>
|
||||
* For each column specified by the table view, {@link MFXTableView#getTableColumns()}, retrieves the
|
||||
* {@link MFXTableColumn#rowCellFactoryProperty()}, build the cell with the row's data, {@link #dataProperty()},
|
||||
* updates the cell, {@link MFXTableRowCell#update(Object)}, then adds to the list.
|
||||
* At the end calls {@link #updateChildren(List)} with the built cells list.
|
||||
* <p></p>
|
||||
* If the row's data is null, exits immediately.
|
||||
*/
|
||||
public void buildCells() {
|
||||
T data = getData();
|
||||
if (data == null) return;
|
||||
|
||||
if (!cells.isEmpty()) cells.clear();
|
||||
ObservableList<MFXTableColumn<T>> columns = tableView.getTableColumns();
|
||||
for (MFXTableColumn<T> column : columns) {
|
||||
MFXTableRowCell<T, ?> cell = column.getRowCellFactory().apply(data);
|
||||
cell.update(data);
|
||||
cells.add(cell);
|
||||
}
|
||||
updateChildren(cells);
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for populating the row with the given children list.
|
||||
* Since the row also has a ripple generator, this is added at the start of the given list.
|
||||
*/
|
||||
private void updateChildren(List<MFXTableRowCell<T, ?>> children) {
|
||||
List<Node> finalList = new LinkedList<>(children);
|
||||
finalList.add(0, rippleGenerator);
|
||||
getChildren().setAll(finalList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for handling the selection triggered by a {@link MouseEvent}.
|
||||
* <p>
|
||||
* According to the index and the selection state this can: deselect the row,
|
||||
* add the row to the selection model, replace the selection with only this row.
|
||||
*/
|
||||
private void updateSelection(MouseEvent event) {
|
||||
if (event.getButton() != MouseButton.PRIMARY) return;
|
||||
|
||||
int index = getIndex();
|
||||
if (event.isControlDown()) {
|
||||
if (isSelected()) {
|
||||
tableView.getSelectionModel().deselectIndex(index);
|
||||
} else {
|
||||
tableView.getSelectionModel().selectIndex(index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.isShiftDown()) {
|
||||
tableView.getSelectionModel().expandSelection(index);
|
||||
return;
|
||||
}
|
||||
|
||||
tableView.getSelectionModel().replaceSelection(index);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public Node getNode() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(T data) {
|
||||
setData(data);
|
||||
updateRow(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateIndex(int index) {
|
||||
setIndex(tableView.getTransformableList().viewToSource(index));
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* @return the row's cells as an unmodifiable observable list
|
||||
*/
|
||||
public ObservableList<MFXTableRowCell<T, ?>> getCells() {
|
||||
return FXCollections.unmodifiableObservableList(cells);
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the row's index.
|
||||
*/
|
||||
public ReadOnlyIntegerProperty indexProperty() {
|
||||
return index.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setIndex(int index) {
|
||||
this.index.set(index);
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the item represented by the row.
|
||||
*/
|
||||
public ReadOnlyObjectProperty<T> dataProperty() {
|
||||
return data.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setData(T data) {
|
||||
this.data.set(data);
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the selection state of the row.
|
||||
*/
|
||||
public ReadOnlyBooleanProperty selectedProperty() {
|
||||
return selected.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setSelected(boolean selected) {
|
||||
this.selected.set(selected);
|
||||
}
|
||||
}
|
728
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTableView.java
Executable file → Normal file
728
materialfx/src/main/java/io/github/palexdev/materialfx/controls/MFXTableView.java
Executable file → Normal file
@ -1,412 +1,446 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.properties.functional.FunctionProperty;
|
||||
import io.github.palexdev.materialfx.collections.TransformableList;
|
||||
import io.github.palexdev.materialfx.collections.TransformableListWrapper;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SequencedMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import io.github.palexdev.materialfx.collections.RefineList;
|
||||
import io.github.palexdev.materialfx.controls.base.Themable;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXTableRowCell;
|
||||
import io.github.palexdev.materialfx.filter.base.AbstractFilter;
|
||||
import io.github.palexdev.materialfx.selection.MultipleSelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.base.IMultipleSelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.base.ISelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.SelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.base.WithSelectionModel;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXTableRow;
|
||||
import io.github.palexdev.materialfx.skins.MFXTableViewSkin;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.materialfx.theming.base.Theme;
|
||||
import io.github.palexdev.materialfx.utils.ListChangeHelper;
|
||||
import io.github.palexdev.materialfx.utils.ListChangeProcessor;
|
||||
import io.github.palexdev.materialfx.utils.others.observables.When;
|
||||
import io.github.palexdev.mfxcore.base.beans.Size;
|
||||
import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
|
||||
import io.github.palexdev.virtualizedfx.unused.simple.SimpleVirtualFlow;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.property.*;
|
||||
import io.github.palexdev.mfxcore.base.properties.functional.FunctionProperty;
|
||||
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableDoubleProperty;
|
||||
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableIntegerProperty;
|
||||
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableObjectProperty;
|
||||
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableSizeProperty;
|
||||
import io.github.palexdev.virtualizedfx.base.VFXStyleable;
|
||||
import io.github.palexdev.virtualizedfx.cells.base.VFXTableCell;
|
||||
import io.github.palexdev.virtualizedfx.enums.BufferSize;
|
||||
import io.github.palexdev.virtualizedfx.enums.ColumnsLayoutMode;
|
||||
import io.github.palexdev.virtualizedfx.properties.CellFactory;
|
||||
import io.github.palexdev.virtualizedfx.table.*;
|
||||
import javafx.beans.property.DoubleProperty;
|
||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
// TODO prefer composition over inheritance for virtualized components
|
||||
public class MFXTableView<T> extends Control implements WithSelectionModel<T>, Themable, VFXStyleable {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
protected final VFXTable<T> vfxTable;
|
||||
|
||||
/**
|
||||
* This is the implementation of a table view following Google's material design guidelines in JavaFX.
|
||||
* <p>
|
||||
* Extends {@code Control} and provides a new skin since it is built from scratch.
|
||||
*
|
||||
* @param <T> The type of the data within the table.
|
||||
* @see MFXTableViewSkin
|
||||
*/
|
||||
public class MFXTableView<T> extends Control implements Themable {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-table-view";
|
||||
protected final SimpleVirtualFlow<T, MFXTableRow<T>> rowsFlow;
|
||||
protected final ReadOnlyBooleanWrapper virtualFlowInitialized = new ReadOnlyBooleanWrapper();
|
||||
private final RefineList<T> refineList;
|
||||
private final ObservableList<AbstractFilter<T, ?>> filters = FXCollections.observableArrayList();
|
||||
|
||||
private final ObjectProperty<ObservableList<T>> items = new SimpleObjectProperty<>();
|
||||
private final ListChangeListener<? super T> itemsChanged = this::itemsChanged;
|
||||
private final ISelectionModel<T> selectionModel;
|
||||
|
||||
private final IMultipleSelectionModel<T> selectionModel = new MultipleSelectionModel<>(items);
|
||||
private final ObservableList<MFXTableColumn<T>> tableColumns = FXCollections.observableArrayList();
|
||||
private final FunctionProperty<T, MFXTableRow<T>> tableRowFactory = new FunctionProperty<>(item -> new MFXTableRow<>(this, item));
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXTableView() {
|
||||
this(FXCollections.observableArrayList());
|
||||
}
|
||||
|
||||
private final TransformableListWrapper<T> transformableList = new TransformableListWrapper<>(FXCollections.observableArrayList());
|
||||
private final ObservableList<AbstractFilter<T, ?>> filters = FXCollections.observableArrayList();
|
||||
private final InvalidationListener itemsInvalid = invalidated -> transformableList.setAll(getItems());
|
||||
private final BooleanProperty footerVisible = new SimpleBooleanProperty(true);
|
||||
public MFXTableView(ObservableList<T> items) {
|
||||
refineList = new RefineList<>(items);
|
||||
selectionModel = new SelectionModel<>(refineList);
|
||||
vfxTable = new VFXTable<>(refineList) {
|
||||
@Override
|
||||
protected Function<T, VFXTableRow<T>> defaultRowFactory() {
|
||||
return MFXTableRow::new;
|
||||
}
|
||||
};
|
||||
initialize();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXTableView() {
|
||||
this(FXCollections.observableArrayList());
|
||||
}
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
private void initialize() {
|
||||
getStyleClass().setAll(defaultStyleClasses());
|
||||
sceneBuilderIntegration();
|
||||
autosizeColumns();
|
||||
}
|
||||
|
||||
public MFXTableView(ObservableList<T> items) {
|
||||
setItems(items);
|
||||
rowsFlow = new SimpleVirtualFlow<>(
|
||||
transformableList,
|
||||
getTableRowFactory(),
|
||||
Orientation.VERTICAL
|
||||
);
|
||||
rowsFlow.cellFactoryProperty().bind(tableRowFactoryProperty());
|
||||
VBox.setVgrow(rowsFlow, Priority.ALWAYS);
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public ISelectionModel<T> getSelectionModel() {
|
||||
return selectionModel;
|
||||
}
|
||||
|
||||
initialize();
|
||||
}
|
||||
@Override
|
||||
public Parent toParent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
private void initialize() {
|
||||
getStyleClass().add(STYLE_CLASS);
|
||||
@Override
|
||||
public Theme getTheme() {
|
||||
return MaterialFXStylesheets.TABLE_VIEW;
|
||||
}
|
||||
|
||||
transformableList.setAll(getItems());
|
||||
itemsProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (oldValue != null) {
|
||||
oldValue.removeListener(itemsChanged);
|
||||
oldValue.removeListener(itemsInvalid);
|
||||
}
|
||||
if (newValue != null) {
|
||||
newValue.addListener(itemsChanged);
|
||||
newValue.addListener(itemsInvalid);
|
||||
transformableList.setAll(newValue);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public List<String> defaultStyleClasses() {
|
||||
return List.of("mfx-table-view");
|
||||
}
|
||||
|
||||
getItems().addListener(itemsChanged);
|
||||
getItems().addListener(itemsInvalid);
|
||||
sceneBuilderIntegration();
|
||||
}
|
||||
@Override
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new MFXTableViewSkin<>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for updating the selection when the items list changes.
|
||||
*/
|
||||
protected void itemsChanged(ListChangeListener.Change<? extends T> change) {
|
||||
IMultipleSelectionModel<T> selectionModel = getSelectionModel();
|
||||
if (selectionModel.getSelection().isEmpty()) return;
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
public VFXTable<T> getVirtualizedContainer() {
|
||||
return vfxTable;
|
||||
}
|
||||
|
||||
if (change.getList().isEmpty()) {
|
||||
selectionModel.clearSelection();
|
||||
return;
|
||||
}
|
||||
public void autosizeColumn(int index) {
|
||||
vfxTable.autosizeColumn(index);
|
||||
}
|
||||
|
||||
ListChangeHelper.Change c = ListChangeHelper.processChange(change, IntegerRange.of(0, Integer.MAX_VALUE));
|
||||
ListChangeProcessor updater = new ListChangeProcessor(new HashSet<>(selectionModel.getSelection().keySet()));
|
||||
c.processReplacement((changed, removed) -> selectionModel.replaceSelection(changed.toArray(Integer[]::new)));
|
||||
c.processAddition((from, to, added) -> {
|
||||
updater.computeAddition(added.size(), from);
|
||||
selectionModel.replaceSelection(updater.getIndexes().toArray(Integer[]::new));
|
||||
});
|
||||
c.processRemoval((from, to, removed) -> {
|
||||
updater.computeRemoval(removed, from);
|
||||
getSelectionModel().replaceSelection(updater.getIndexes().toArray(Integer[]::new));
|
||||
});
|
||||
}
|
||||
public void setVPos(double vPos) {
|
||||
vfxTable.setVPos(vPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to programmatically update the table.
|
||||
* <p>
|
||||
* Uses {@link MFXTableRow#updateRow()} on the currently built rows, {@link SimpleVirtualFlow#getCells()}.
|
||||
*/
|
||||
public void update() {
|
||||
rowsFlow.getCells().values().forEach(MFXTableRow::updateRow);
|
||||
}
|
||||
public ReadOnlyDoubleProperty virtualMaxYProperty() {
|
||||
return vfxTable.virtualMaxYProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Autosize all the table columns.
|
||||
*/
|
||||
public void autosizeColumns() {
|
||||
tableColumns.forEach(this::autosizeColumn);
|
||||
}
|
||||
public BufferSize getRowsBufferSize() {
|
||||
return vfxTable.getRowsBufferSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Autosizes the column at the given index.
|
||||
* <p>
|
||||
* This method fails silently if it can not get the column at index.
|
||||
*/
|
||||
public void autosizeColumn(int index) {
|
||||
try {
|
||||
MFXTableColumn<T> column = tableColumns.get(index);
|
||||
autosizeColumn(column);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
public void scrollToLastColumn() {
|
||||
vfxTable.scrollToLastColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Autosizes the given column.
|
||||
*/
|
||||
public void autosizeColumn(MFXTableColumn<T> column) {
|
||||
int index = tableColumns.indexOf(column);
|
||||
if (index == -1) return;
|
||||
public void autosizeColumns() {
|
||||
vfxTable.autosizeColumns();
|
||||
}
|
||||
|
||||
Collection<MFXTableRow<T>> rows = rowsFlow.getCells().values();
|
||||
List<Double> minSizes = new ArrayList<>();
|
||||
minSizes.add(column.getWidth());
|
||||
rows.forEach(row -> {
|
||||
ObservableList<MFXTableRowCell<T, ?>> rowCells = row.getCells();
|
||||
if (rowCells.isEmpty()) return;
|
||||
MFXTableRowCell<T, ?> rowCell = rowCells.get(index);
|
||||
rowCell.requestLayout();
|
||||
minSizes.add(rowCell.computePrefWidth(-1));
|
||||
});
|
||||
double max = minSizes.stream().max(Double::compareTo).orElse(-1.0);
|
||||
if (max != -1.0) {
|
||||
column.setMinWidth(max);
|
||||
}
|
||||
}
|
||||
public Function<ColumnsLayoutMode, VFXTableHelper<T>> getHelperFactory() {
|
||||
return vfxTable.getHelperFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called only if you need to autosize the columns
|
||||
* before the table is laid out/initialized.
|
||||
* <p>
|
||||
* Calling this afterwards won't have any effect.
|
||||
*/
|
||||
public void autosizeColumnsOnInitialization() {
|
||||
if (isVirtualFlowInitialized()) return;
|
||||
When.onChanged(virtualFlowInitialized)
|
||||
.then((oldValue, newValue) -> autosizeColumns())
|
||||
.oneShot()
|
||||
.listen();
|
||||
}
|
||||
public StyleableObjectProperty<BufferSize> columnsBufferSizeProperty() {
|
||||
return vfxTable.columnsBufferSizeProperty();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
public ReadOnlyObjectWrapper<VFXTableHelper<T>> helperProperty() {
|
||||
return vfxTable.helperProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link SimpleVirtualFlow#getCell(int)}.
|
||||
*/
|
||||
public MFXTableRow<T> getCell(int index) {
|
||||
return rowsFlow.getCell(index);
|
||||
}
|
||||
public ViewportLayoutRequest<T> getViewportLayoutRequest() {
|
||||
return vfxTable.getViewportLayoutRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link SimpleVirtualFlow#getCells()}.
|
||||
*/
|
||||
public Map<Integer, MFXTableRow<T>> getCells() {
|
||||
return rowsFlow.getCells();
|
||||
}
|
||||
public void scrollToPixelHorizontal(double pixel) {
|
||||
vfxTable.scrollToPixelHorizontal(pixel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link SimpleVirtualFlow#scrollBy(double)}.
|
||||
*/
|
||||
public void scrollBy(double pixels) {
|
||||
rowsFlow.scrollBy(pixels);
|
||||
}
|
||||
public int indexOf(VFXTableColumn<T, ?> column) {
|
||||
return vfxTable.indexOf(column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link SimpleVirtualFlow#scrollTo(int)}.
|
||||
*/
|
||||
public void scrollTo(int index) {
|
||||
rowsFlow.scrollTo(index);
|
||||
}
|
||||
public IntegerRange getColumnsRange() {
|
||||
return vfxTable.getColumnsRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link SimpleVirtualFlow#scrollToFirst()}.
|
||||
*/
|
||||
public void scrollToFirst() {
|
||||
rowsFlow.scrollToFirst();
|
||||
}
|
||||
public void setExtraAutosizeWidth(double extraAutosizeWidth) {
|
||||
vfxTable.setExtraAutosizeWidth(extraAutosizeWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link SimpleVirtualFlow#scrollToLast()}.
|
||||
*/
|
||||
public void scrollToLast() {
|
||||
rowsFlow.scrollToLast();
|
||||
}
|
||||
public BufferSize getBufferSize() {
|
||||
return vfxTable.getBufferSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link SimpleVirtualFlow#scrollToPixel(double)}.
|
||||
*/
|
||||
public void scrollToPixel(double pixel) {
|
||||
rowsFlow.scrollToPixel(pixel);
|
||||
}
|
||||
public double getClipBorderRadius() {
|
||||
return vfxTable.getClipBorderRadius();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link SimpleVirtualFlow#setHSpeed(double, double)}.
|
||||
*/
|
||||
public void setHSpeed(double unit, double block) {
|
||||
rowsFlow.setHSpeed(unit, block);
|
||||
}
|
||||
public double getRowsHeight() {
|
||||
return vfxTable.getRowsHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link SimpleVirtualFlow#setVSpeed(double, double)}.
|
||||
*/
|
||||
public void setVSpeed(double unit, double block) {
|
||||
rowsFlow.setVSpeed(unit, block);
|
||||
}
|
||||
public VFXTable<T> populateCacheAll() {
|
||||
return vfxTable.populateCacheAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for {@link SimpleVirtualFlow#features()}.
|
||||
*/
|
||||
public SimpleVirtualFlow<T, MFXTableRow<T>>.Features features() {
|
||||
return rowsFlow.features();
|
||||
}
|
||||
public void scrollToFirstColumn() {
|
||||
vfxTable.scrollToFirstColumn();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
public VFXTableState<T> getState() {
|
||||
return vfxTable.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parent toParent() {
|
||||
return this;
|
||||
}
|
||||
public void scrollToRow(int index) {
|
||||
vfxTable.scrollToRow(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Theme getTheme() {
|
||||
return MaterialFXStylesheets.TABLE_VIEW;
|
||||
}
|
||||
public void setRowsCacheCapacity(int rowsCacheCapacity) {
|
||||
vfxTable.setRowsCacheCapacity(rowsCacheCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new MFXTableViewSkin<>(this, rowsFlow);
|
||||
}
|
||||
public BufferSize getColumnsBufferSize() {
|
||||
return vfxTable.getColumnsBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layoutChildren() {
|
||||
super.layoutChildren();
|
||||
if (!isVirtualFlowInitialized() && rowsFlow.getCellHeight() > 0) virtualFlowInitialized.set(true);
|
||||
}
|
||||
public double getHPos() {
|
||||
return vfxTable.getHPos();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
public ObservableList<T> getItems() {
|
||||
return items.get();
|
||||
}
|
||||
public ReadOnlyDoubleProperty virtualMaxXProperty() {
|
||||
return vfxTable.virtualMaxXProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the table's {@link ObservableList} containing the items.
|
||||
*/
|
||||
public ObjectProperty<ObservableList<T>> itemsProperty() {
|
||||
return items;
|
||||
}
|
||||
public void setColumnsWidth(double w) {
|
||||
vfxTable.setColumnsWidth(w);
|
||||
}
|
||||
|
||||
public void setItems(ObservableList<T> items) {
|
||||
this.items.set(items);
|
||||
}
|
||||
public VFXTableHelper<T> getHelper() {
|
||||
return vfxTable.getHelper();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the selection model used by the table to handle row selection
|
||||
*/
|
||||
public IMultipleSelectionModel<T> getSelectionModel() {
|
||||
return selectionModel;
|
||||
}
|
||||
public IntegerRange getRowsRange() {
|
||||
return vfxTable.getRowsRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list containing the table's columns
|
||||
*/
|
||||
public ObservableList<MFXTableColumn<T>> getTableColumns() {
|
||||
return tableColumns;
|
||||
}
|
||||
public StyleableDoubleProperty extraAutosizeWidthProperty() {
|
||||
return vfxTable.extraAutosizeWidthProperty();
|
||||
}
|
||||
|
||||
public Function<T, MFXTableRow<T>> getTableRowFactory() {
|
||||
return tableRowFactory.get();
|
||||
}
|
||||
public void setColumnsSize(Size columnsSize) {
|
||||
vfxTable.setColumnsSize(columnsSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the {@link Function} used to generate the table rows.
|
||||
*/
|
||||
public FunctionProperty<T, MFXTableRow<T>> tableRowFactoryProperty() {
|
||||
return tableRowFactory;
|
||||
}
|
||||
public void setRowFactory(Function<T, VFXTableRow<T>> rowFactory) {
|
||||
vfxTable.setRowFactory(rowFactory);
|
||||
}
|
||||
|
||||
public void setTableRowFactory(Function<T, MFXTableRow<T>> tableRowFactory) {
|
||||
this.tableRowFactory.set(tableRowFactory);
|
||||
}
|
||||
public VFXTable<T> populateCache() {
|
||||
return vfxTable.populateCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list that is effectively used by the {@link SimpleVirtualFlow} (which contains the table rows).
|
||||
* This list is capable of filtering and sorting.
|
||||
* @see TransformableListWrapper
|
||||
* @see TransformableList
|
||||
*/
|
||||
public TransformableListWrapper<T> getTransformableList() {
|
||||
return transformableList;
|
||||
}
|
||||
public ReadOnlyObjectProperty<VFXTableState<T>> stateProperty() {
|
||||
return vfxTable.stateProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list containing the filters' information used by the
|
||||
* {@link MFXFilterPane} to filter the table
|
||||
*/
|
||||
public ObservableList<AbstractFilter<T, ?>> getFilters() {
|
||||
return filters;
|
||||
}
|
||||
public void scrollVerticalBy(double pixels) {
|
||||
vfxTable.scrollVerticalBy(pixels);
|
||||
}
|
||||
|
||||
public boolean isFooterVisible() {
|
||||
return footerVisible.get();
|
||||
}
|
||||
public boolean isNeedsViewportLayout() {
|
||||
return vfxTable.isNeedsViewportLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies whether the table's footer is visible
|
||||
*/
|
||||
public BooleanProperty footerVisibleProperty() {
|
||||
return footerVisible;
|
||||
}
|
||||
public void scrollToLastRow() {
|
||||
vfxTable.scrollToLastRow();
|
||||
}
|
||||
|
||||
public void setFooterVisible(boolean footerVisible) {
|
||||
this.footerVisible.set(footerVisible);
|
||||
}
|
||||
public void setColumnsHeight(double h) {
|
||||
vfxTable.setColumnsHeight(h);
|
||||
}
|
||||
|
||||
public boolean isVirtualFlowInitialized() {
|
||||
return virtualFlowInitialized.get();
|
||||
}
|
||||
public Function<T, VFXTableRow<T>> getRowFactory() {
|
||||
return vfxTable.getRowFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful property to inform that the table layout
|
||||
* has been initialized/is ready.
|
||||
* <p>
|
||||
* For example it is used by {@link #autosizeColumnsOnInitialization()}
|
||||
* to autosize the columns before the table is even laid out by using a
|
||||
* listener.
|
||||
* <p>
|
||||
* It is considered initialized as soon as the {@link SimpleVirtualFlow}
|
||||
* retrieves the cells' height.
|
||||
*/
|
||||
public ReadOnlyBooleanProperty virtualFlowInitializedProperty() {
|
||||
return virtualFlowInitialized.getReadOnlyProperty();
|
||||
}
|
||||
public double getVPos() {
|
||||
return vfxTable.getVPos();
|
||||
}
|
||||
|
||||
public void setRowsBufferSize(BufferSize rowsBufferSize) {
|
||||
vfxTable.setRowsBufferSize(rowsBufferSize);
|
||||
}
|
||||
|
||||
public DoubleProperty hPosProperty() {
|
||||
return vfxTable.hPosProperty();
|
||||
}
|
||||
|
||||
public ReadOnlyDoubleProperty maxHScrollProperty() {
|
||||
return vfxTable.maxHScrollProperty();
|
||||
}
|
||||
|
||||
public void setColumnsSize(double w, double h) {
|
||||
vfxTable.setColumnsSize(w, h);
|
||||
}
|
||||
|
||||
public List<Map.Entry<T, VFXTableRow<T>>> getRowsByItemUnmodifiable() {
|
||||
return vfxTable.getRowsByItemUnmodifiable();
|
||||
}
|
||||
|
||||
public void scrollToColumn(int index) {
|
||||
vfxTable.scrollToColumn(index);
|
||||
}
|
||||
|
||||
public void setHelperFactory(Function<ColumnsLayoutMode, VFXTableHelper<T>> helperFactory) {
|
||||
vfxTable.setHelperFactory(helperFactory);
|
||||
}
|
||||
|
||||
public double getExtraAutosizeWidth() {
|
||||
return vfxTable.getExtraAutosizeWidth();
|
||||
}
|
||||
|
||||
public void setBufferSize(BufferSize bufferSize) {
|
||||
vfxTable.setBufferSize(bufferSize);
|
||||
}
|
||||
|
||||
public int cellsCacheSize() {
|
||||
return vfxTable.cellsCacheSize();
|
||||
}
|
||||
|
||||
public void setClipBorderRadius(double clipBorderRadius) {
|
||||
vfxTable.setClipBorderRadius(clipBorderRadius);
|
||||
}
|
||||
|
||||
public StyleableSizeProperty columnsSizeProperty() {
|
||||
return vfxTable.columnsSizeProperty();
|
||||
}
|
||||
|
||||
public StyleableIntegerProperty rowsCacheCapacityProperty() {
|
||||
return vfxTable.rowsCacheCapacityProperty();
|
||||
}
|
||||
|
||||
public StyleableObjectProperty<BufferSize> bufferSizeProperty() {
|
||||
return vfxTable.bufferSizeProperty();
|
||||
}
|
||||
|
||||
public void setColumnsLayoutMode(ColumnsLayoutMode columnsLayoutMode) {
|
||||
vfxTable.setColumnsLayoutMode(columnsLayoutMode);
|
||||
}
|
||||
|
||||
public CellFactory<T, VFXTableRow<T>> rowFactoryProperty() {
|
||||
return vfxTable.rowFactoryProperty();
|
||||
}
|
||||
|
||||
public ObservableList<VFXTableColumn<T, ? extends VFXTableCell<T>>> getColumns() {
|
||||
return vfxTable.getColumns();
|
||||
}
|
||||
|
||||
public void scrollHorizontalBy(double pixels) {
|
||||
vfxTable.scrollHorizontalBy(pixels);
|
||||
}
|
||||
|
||||
public void setRowsHeight(double rowsHeight) {
|
||||
vfxTable.setRowsHeight(rowsHeight);
|
||||
}
|
||||
|
||||
public void scrollToFirstRow() {
|
||||
vfxTable.scrollToFirstRow();
|
||||
}
|
||||
|
||||
public DoubleProperty vPosProperty() {
|
||||
return vfxTable.vPosProperty();
|
||||
}
|
||||
|
||||
public void update(int... indexes) {
|
||||
vfxTable.update(indexes);
|
||||
}
|
||||
|
||||
public ReadOnlyDoubleProperty maxVScrollProperty() {
|
||||
return vfxTable.maxVScrollProperty();
|
||||
}
|
||||
|
||||
public StyleableObjectProperty<BufferSize> rowsBufferSizeProperty() {
|
||||
return vfxTable.rowsBufferSizeProperty();
|
||||
}
|
||||
|
||||
public ColumnsLayoutMode getColumnsLayoutMode() {
|
||||
return vfxTable.getColumnsLayoutMode();
|
||||
}
|
||||
|
||||
public SequencedMap<Integer, VFXTableRow<T>> getRowsByIndexUnmodifiable() {
|
||||
return vfxTable.getRowsByIndexUnmodifiable();
|
||||
}
|
||||
|
||||
public void setHPos(double hPos) {
|
||||
vfxTable.setHPos(hPos);
|
||||
}
|
||||
|
||||
public void autosizeColumn(VFXTableColumn<T, ?> column) {
|
||||
vfxTable.autosizeColumn(column);
|
||||
}
|
||||
|
||||
public void setColumnsBufferSize(BufferSize columnsBufferSize) {
|
||||
vfxTable.setColumnsBufferSize(columnsBufferSize);
|
||||
}
|
||||
|
||||
public FunctionProperty<ColumnsLayoutMode, VFXTableHelper<T>> helperFactoryProperty() {
|
||||
return vfxTable.helperFactoryProperty();
|
||||
}
|
||||
|
||||
public int getRowsCacheCapacity() {
|
||||
return vfxTable.getRowsCacheCapacity();
|
||||
}
|
||||
|
||||
public void scrollToPixelVertical(double pixel) {
|
||||
vfxTable.scrollToPixelVertical(pixel);
|
||||
}
|
||||
|
||||
public Size getColumnsSize() {
|
||||
return vfxTable.getColumnsSize();
|
||||
}
|
||||
|
||||
public void setHelper(VFXTableHelper<T> helper) {
|
||||
vfxTable.setHelper(helper);
|
||||
}
|
||||
|
||||
public ReadOnlyObjectProperty<ViewportLayoutRequest<T>> needsViewportLayoutProperty() {
|
||||
return vfxTable.needsViewportLayoutProperty();
|
||||
}
|
||||
|
||||
public StyleableObjectProperty<ColumnsLayoutMode> columnsLayoutModeProperty() {
|
||||
return vfxTable.columnsLayoutModeProperty();
|
||||
}
|
||||
|
||||
public void requestViewportLayout() {
|
||||
vfxTable.requestViewportLayout();
|
||||
}
|
||||
|
||||
public StyleableDoubleProperty rowsHeightProperty() {
|
||||
return vfxTable.rowsHeightProperty();
|
||||
}
|
||||
|
||||
public void switchColumnsLayoutMode() {
|
||||
vfxTable.switchColumnsLayoutMode();
|
||||
}
|
||||
|
||||
public int rowsCacheSize() {
|
||||
return vfxTable.rowsCacheSize();
|
||||
}
|
||||
|
||||
public StyleableDoubleProperty clipBorderRadiusProperty() {
|
||||
return vfxTable.clipBorderRadiusProperty();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters
|
||||
//================================================================================
|
||||
public ObservableList<T> getItems() {
|
||||
return refineList;
|
||||
}
|
||||
|
||||
public ObservableList<AbstractFilter<T, ?>> getFilters() {
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
|
@ -1,308 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls.base;
|
||||
|
||||
import io.github.palexdev.materialfx.effects.DepthLevel;
|
||||
import io.github.palexdev.materialfx.selection.MultipleSelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.base.IMultipleSelectionModel;
|
||||
import io.github.palexdev.materialfx.utils.ColorUtils;
|
||||
import io.github.palexdev.materialfx.utils.StyleablePropertiesUtils;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.css.*;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.util.Duration;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Base class for all list views based on VirtualizedFX, defines common properties and behavior.
|
||||
*
|
||||
* @param <T> the type of data within the ListView
|
||||
*/
|
||||
public abstract class AbstractMFXListView<T, C extends Cell<T>> extends Control implements IListView<T, C>, Themable {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
protected final ObjectProperty<ObservableList<T>> items = new SimpleObjectProperty<>(FXCollections.observableArrayList());
|
||||
protected final ObjectProperty<StringConverter<T>> converter = new SimpleObjectProperty<>();
|
||||
protected final IMultipleSelectionModel<T> selectionModel = new MultipleSelectionModel<>(items);
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public AbstractMFXListView() {}
|
||||
|
||||
public AbstractMFXListView(ObservableList<T> items) {
|
||||
setItems(items);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Abstract Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Abstract method called automatically to set a default factory for the cells.
|
||||
*/
|
||||
protected abstract void setDefaultCellFactory();
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
protected void initialize() {
|
||||
setDefaultCellFactory();
|
||||
addBarsListeners();
|
||||
sceneBuilderIntegration();
|
||||
}
|
||||
|
||||
protected void addBarsListeners() {
|
||||
this.trackColor.addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue.equals(oldValue)) {
|
||||
setColors();
|
||||
}
|
||||
});
|
||||
|
||||
this.thumbColor.addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue.equals(oldValue)) {
|
||||
setColors();
|
||||
}
|
||||
});
|
||||
|
||||
this.thumbHoverColor.addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue.equals(oldValue)) {
|
||||
setColors();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CSS looked-up colors
|
||||
*/
|
||||
protected void setColors() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("-mfx-track-color: ").append(ColorUtils.toCss(trackColor.get()))
|
||||
.append(";\n-mfx-thumb-color: ").append(ColorUtils.toCss(thumbColor.get()))
|
||||
.append(";\n-mfx-thumb-hover-color: ").append(ColorUtils.toCss(thumbHoverColor.get()))
|
||||
.append(";");
|
||||
setStyle(sb.toString());
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// ScrollBars Properties
|
||||
//================================================================================
|
||||
private final ObjectProperty<Paint> trackColor = new SimpleObjectProperty<>(Color.rgb(230, 230, 230));
|
||||
private final ObjectProperty<Paint> thumbColor = new SimpleObjectProperty<>(Color.rgb(137, 137, 137));
|
||||
private final ObjectProperty<Paint> thumbHoverColor = new SimpleObjectProperty<>(Color.rgb(89, 88, 91));
|
||||
private final ObjectProperty<Duration> hideAfter = new SimpleObjectProperty<>(Duration.seconds(1));
|
||||
|
||||
public Paint getTrackColor() {
|
||||
return trackColor.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the color of the scrollbars' track.
|
||||
*/
|
||||
public ObjectProperty<Paint> trackColorProperty() {
|
||||
return trackColor;
|
||||
}
|
||||
|
||||
public void setTrackColor(Paint trackColor) {
|
||||
this.trackColor.set(trackColor);
|
||||
}
|
||||
|
||||
public Paint getThumbColor() {
|
||||
return thumbColor.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the color of the scrollbars' thumb.
|
||||
*/
|
||||
public ObjectProperty<Paint> thumbColorProperty() {
|
||||
return thumbColor;
|
||||
}
|
||||
|
||||
public void setThumbColor(Paint thumbColor) {
|
||||
this.thumbColor.set(thumbColor);
|
||||
}
|
||||
|
||||
public Paint getThumbHoverColor() {
|
||||
return thumbHoverColor.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the color of the scrollbars' thumb when mouse hover.
|
||||
*/
|
||||
public ObjectProperty<Paint> thumbHoverColorProperty() {
|
||||
return thumbHoverColor;
|
||||
}
|
||||
|
||||
public void setThumbHoverColor(Paint thumbHoverColor) {
|
||||
this.thumbHoverColor.set(thumbHoverColor);
|
||||
}
|
||||
|
||||
public Duration getHideAfter() {
|
||||
return hideAfter.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the time after which the scrollbars are hidden.
|
||||
*/
|
||||
public ObjectProperty<Duration> hideAfterProperty() {
|
||||
return hideAfter;
|
||||
}
|
||||
|
||||
public void setHideAfter(Duration hideAfter) {
|
||||
this.hideAfter.set(hideAfter);
|
||||
}
|
||||
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
@Override
|
||||
public Parent toParent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObservableList<T> getItems() {
|
||||
return items.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectProperty<ObservableList<T>> itemsProperty() {
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItems(ObservableList<T> items) {
|
||||
this.items.set(items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringConverter<T> getConverter() {
|
||||
return converter.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectProperty<StringConverter<T>> converterProperty() {
|
||||
return converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConverter(StringConverter<T> converter) {
|
||||
this.converter.set(converter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMultipleSelectionModel<T> getSelectionModel() {
|
||||
return selectionModel;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Styleable Properties
|
||||
//================================================================================
|
||||
private final StyleableBooleanProperty hideScrollBars = new SimpleStyleableBooleanProperty(
|
||||
StyleableProperties.HIDE_SCROLLBARS,
|
||||
this,
|
||||
"hideScrollBars",
|
||||
false
|
||||
);
|
||||
|
||||
private final StyleableObjectProperty<DepthLevel> depthLevel = new SimpleStyleableObjectProperty<>(
|
||||
StyleableProperties.DEPTH_LEVEL,
|
||||
this,
|
||||
"depthLevel",
|
||||
DepthLevel.LEVEL2
|
||||
);
|
||||
|
||||
public boolean isHideScrollBars() {
|
||||
return hideScrollBars.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies if the scrollbars should be hidden when the mouse is not on the list.
|
||||
*/
|
||||
public StyleableBooleanProperty hideScrollBarsProperty() {
|
||||
return hideScrollBars;
|
||||
}
|
||||
|
||||
public void setHideScrollBars(boolean hideScrollBars) {
|
||||
this.hideScrollBars.set(hideScrollBars);
|
||||
}
|
||||
|
||||
public DepthLevel getDepthLevel() {
|
||||
return depthLevel.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the shadow strength around the control.
|
||||
*/
|
||||
public StyleableObjectProperty<DepthLevel> depthLevelProperty() {
|
||||
return depthLevel;
|
||||
}
|
||||
|
||||
public void setDepthLevel(DepthLevel depthLevel) {
|
||||
this.depthLevel.set(depthLevel);
|
||||
}
|
||||
|
||||
private static class StyleableProperties {
|
||||
private static final StyleablePropertyFactory<AbstractMFXListView<?, ?>> FACTORY = new StyleablePropertyFactory<>(Control.getClassCssMetaData());
|
||||
private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;
|
||||
|
||||
private static final CssMetaData<AbstractMFXListView<?, ?>, Boolean> HIDE_SCROLLBARS =
|
||||
FACTORY.createBooleanCssMetaData(
|
||||
"-mfx-hide-scrollbars",
|
||||
AbstractMFXListView::hideScrollBarsProperty,
|
||||
false
|
||||
);
|
||||
|
||||
private static final CssMetaData<AbstractMFXListView<?, ?>, DepthLevel> DEPTH_LEVEL =
|
||||
FACTORY.createEnumCssMetaData(
|
||||
DepthLevel.class,
|
||||
"-mfx-depth-level",
|
||||
AbstractMFXListView::depthLevelProperty,
|
||||
DepthLevel.LEVEL2
|
||||
);
|
||||
|
||||
|
||||
static {
|
||||
cssMetaDataList = StyleablePropertiesUtils.cssMetaDataList(
|
||||
Control.getClassCssMetaData(),
|
||||
HIDE_SCROLLBARS, DEPTH_LEVEL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
|
||||
return StyleableProperties.cssMetaDataList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
|
||||
return AbstractMFXListView.getClassCssMetaData();
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls.base;
|
||||
|
||||
import io.github.palexdev.materialfx.selection.base.IMultipleSelectionModel;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Interface that defines the public API for all the listviews based on VirtualizedFX.
|
||||
*
|
||||
* @param <T> the type of data within the ListView
|
||||
* @param <C> the type of cells that will be used
|
||||
*/
|
||||
public interface IListView<T, C extends Cell<T>> {
|
||||
|
||||
/**
|
||||
* @return the items observable list
|
||||
*/
|
||||
ObservableList<T> getItems();
|
||||
|
||||
/**
|
||||
* The items list property.
|
||||
*/
|
||||
ObjectProperty<ObservableList<T>> itemsProperty();
|
||||
|
||||
/**
|
||||
* Replaces the items list with the given one.
|
||||
*/
|
||||
void setItems(ObservableList<T> items);
|
||||
|
||||
StringConverter<T> getConverter();
|
||||
|
||||
/**
|
||||
* Specifies the {@link StringConverter} used to convert a generic
|
||||
* item to a String. It is used by the list cells.
|
||||
*/
|
||||
ObjectProperty<StringConverter<T>> converterProperty();
|
||||
|
||||
void setConverter(StringConverter<T> converter);
|
||||
|
||||
/**
|
||||
* @return the function used to build the list cells
|
||||
*/
|
||||
Function<T, C> getCellFactory();
|
||||
|
||||
/**
|
||||
* @return the cell factory property
|
||||
*/
|
||||
ObjectProperty<Function<T, C>> cellFactoryProperty();
|
||||
|
||||
/**
|
||||
* Replaces the cell factory with the given one
|
||||
*/
|
||||
void setCellFactory(Function<T, C> cellFactory);
|
||||
|
||||
/**
|
||||
* @return the listview selection model
|
||||
*/
|
||||
IMultipleSelectionModel<T> getSelectionModel();
|
||||
}
|
@ -18,11 +18,15 @@
|
||||
|
||||
package io.github.palexdev.materialfx.controls.base;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.properties.EventHandlerProperty;
|
||||
import io.github.palexdev.materialfx.beans.properties.functional.ConsumerProperty;
|
||||
import io.github.palexdev.materialfx.selection.base.ISingleSelectionModel;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import io.github.palexdev.materialfx.selection.base.ISelectionModel;
|
||||
import io.github.palexdev.virtualizedfx.cells.base.VFXCell;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ListProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.ObservableList;
|
||||
@ -31,9 +35,6 @@ import javafx.event.Event;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Public API every combo box must implement.
|
||||
*/
|
||||
@ -123,24 +124,24 @@ public interface MFXCombo<T> {
|
||||
/**
|
||||
* Specifies the combo box's items list.
|
||||
*/
|
||||
ObjectProperty<ObservableList<T>> itemsProperty();
|
||||
ListProperty<T> itemsProperty();
|
||||
|
||||
void setItems(ObservableList<T> items);
|
||||
|
||||
Function<T, Cell<T>> getCellFactory();
|
||||
Function<T, VFXCell<T>> getCellFactory();
|
||||
|
||||
/**
|
||||
* Specifies the function used to create the items cells
|
||||
* in the popup.
|
||||
*/
|
||||
ObjectProperty<Function<T, Cell<T>>> cellFactoryProperty();
|
||||
ObjectProperty<Function<T, VFXCell<T>>> cellFactoryProperty();
|
||||
|
||||
void setCellFactory(Function<T, Cell<T>> cellFactory);
|
||||
void setCellFactory(Function<T, VFXCell<T>> cellFactory);
|
||||
|
||||
/**
|
||||
* @return the combo box' selection model
|
||||
*/
|
||||
ISingleSelectionModel<T> getSelectionModel();
|
||||
ISelectionModel<T> getSelectionModel();
|
||||
|
||||
EventHandler<ActionEvent> getOnAction();
|
||||
|
||||
|
197
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXCheckListCell.java
Executable file → Normal file
197
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXCheckListCell.java
Executable file → Normal file
@ -1,177 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls.cell;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.PositionBean;
|
||||
import io.github.palexdev.materialfx.controls.MFXCheckListView;
|
||||
import io.github.palexdev.materialfx.controls.MFXCheckbox;
|
||||
import io.github.palexdev.materialfx.controls.cell.base.AbstractMFXListCell;
|
||||
import io.github.palexdev.materialfx.effects.ripple.MFXCircleRippleGenerator;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.palexdev.materialfx.skins.MFXCheckListCellSkin;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.materialfx.theming.base.Theme;
|
||||
import io.github.palexdev.materialfx.utils.NodeUtils;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectExpression;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import io.github.palexdev.mfxcore.controls.SkinBase;
|
||||
import io.github.palexdev.mfxcore.utils.fx.SceneBuilderIntegration;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
/**
|
||||
* Implementation of an {@link AbstractMFXListCell} which has a combo box
|
||||
* for usage in {@link MFXCheckListView}.
|
||||
* <p></p>
|
||||
* The label used to display the data is built in the constructor
|
||||
* only if the given T data is not a Node, otherwise it's null.
|
||||
* <p></p>
|
||||
* The label's text is bound to the data property and converted to a String
|
||||
* using {@link ObjectExpression#asString()}.
|
||||
*/
|
||||
public class MFXCheckListCell<T> extends AbstractMFXListCell<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-check-list-cell";
|
||||
protected final MFXCircleRippleGenerator rippleGenerator = new MFXCircleRippleGenerator(this);
|
||||
public class MFXCheckListCell<T> extends MFXListCell<T> {
|
||||
|
||||
private final MFXCheckListView<T> listView;
|
||||
protected final MFXCheckbox checkbox;
|
||||
protected final Label label;
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXCheckListCell(T item) {
|
||||
super(item);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXCheckListCell(MFXCheckListView<T> listView, T data) {
|
||||
super(listView, data);
|
||||
this.listView = listView;
|
||||
checkbox = new MFXCheckbox("");
|
||||
public MFXCheckListCell(T item, StringConverter<T> converter) {
|
||||
super(item, converter);
|
||||
}
|
||||
|
||||
if (!(data instanceof Node)) {
|
||||
label = new Label();
|
||||
label.textProperty().bind(Bindings.createStringBinding(
|
||||
() -> listView.getConverter() != null ? listView.getConverter().toString(getData()) : getData().toString(),
|
||||
dataProperty(), listView.converterProperty()
|
||||
));
|
||||
label.getStyleClass().add("data-label");
|
||||
} else {
|
||||
label = null;
|
||||
}
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
|
||||
initialize();
|
||||
}
|
||||
@Override
|
||||
protected SkinBase<?, ?> buildSkin() {
|
||||
return new MFXCheckListCellSkin<>(this);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected void sceneBuilderIntegration() {
|
||||
SceneBuilderIntegration.ifInSceneBuilder(() -> getStylesheets().add(MaterialFXStylesheets.CHECK_LIST_CELL.toData()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to add the style class, setup the ripple generator and call {@link #render(Object)}
|
||||
* for the first time.
|
||||
*/
|
||||
@Override
|
||||
protected void initialize() {
|
||||
super.initialize();
|
||||
getStyleClass().add(STYLE_CLASS);
|
||||
setupRippleGenerator();
|
||||
render(getData());
|
||||
sceneBuilderIntegration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the properties of the ripple generator and adds the mouse pressed filter.
|
||||
*/
|
||||
protected void setupRippleGenerator() {
|
||||
rippleGenerator.setManaged(false);
|
||||
rippleGenerator.setRipplePositionFunction(event -> PositionBean.of(event.getX(), event.getY()));
|
||||
rippleGenerator.rippleRadiusProperty().bind(widthProperty().divide(2.0));
|
||||
addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
|
||||
if (NodeUtils.inHierarchy(event, checkbox)) {
|
||||
rippleGenerator.generateRipple(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for updating the selection state according to the checkbox' state.
|
||||
* <p>
|
||||
* If checked is true then the cell should be selected, otherwise it is deselected.
|
||||
*/
|
||||
private void updateSelection(boolean checked) {
|
||||
int index = getIndex();
|
||||
if (checked) {
|
||||
listView.getSelectionModel().selectIndex(index);
|
||||
} else {
|
||||
listView.getSelectionModel().deselectIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden/Implemented Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Overridden as the selected state depends on the checkbox' state.
|
||||
*/
|
||||
@Override
|
||||
protected void setBehavior() {
|
||||
selected.addListener(invalidated -> pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, selected.get()));
|
||||
selected.bind(Bindings.createBooleanBinding(
|
||||
() -> {
|
||||
boolean contained = listView.getSelectionModel().getSelection().containsKey(index.get());
|
||||
checkbox.setSelected(contained);
|
||||
return contained;
|
||||
},
|
||||
listView.getSelectionModel().selectionProperty(), index
|
||||
));
|
||||
checkbox.selectedProperty().addListener((observable, oldValue, newValue) -> updateSelection(newValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for rendering the cell's content.
|
||||
* <p>
|
||||
* If the given data type is a Node, it is added to the children list,
|
||||
* otherwise a label is used to display the data.
|
||||
* <p>
|
||||
* At the end adds a ripple generator at index 0.
|
||||
*/
|
||||
@Override
|
||||
protected void render(T data) {
|
||||
if (data instanceof Node) {
|
||||
getChildren().setAll(rippleGenerator, checkbox, (Node) data);
|
||||
} else {
|
||||
getChildren().setAll(rippleGenerator, checkbox, label);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the data property of the cell. If the data is a Node
|
||||
* {@link #render(Object)} is called.
|
||||
* <p>
|
||||
* This is called after {@link #updateIndex(int)}.
|
||||
*/
|
||||
@Override
|
||||
public void updateItem(T item) {
|
||||
super.updateItem(item);
|
||||
if (item instanceof Node) render(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Theme getTheme() {
|
||||
return MaterialFXStylesheets.CHECK_LIST_CELL;
|
||||
}
|
||||
@Override
|
||||
public List<String> defaultStyleClasses() {
|
||||
return List.of("cell-base", "cell", "mfx-list-cell", "mfx-check-list-cell");
|
||||
}
|
||||
}
|
||||
|
@ -1,231 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls.cell;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXComboBox;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.base.MFXCombo;
|
||||
import io.github.palexdev.materialfx.controls.base.Themable;
|
||||
import io.github.palexdev.materialfx.selection.base.ISelectionModel;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.materialfx.theming.base.Theme;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
import io.github.palexdev.mfxcore.utils.fx.SceneBuilderIntegration;
|
||||
import io.github.palexdev.virtualizedfx.cells.VFXSimpleCell;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
/**
|
||||
* Cells used by default by {@link MFXComboBox}
|
||||
* <p></p>
|
||||
* The cell's structure is pretty similar to the {@link MFXListCell} one, but doesn't include
|
||||
* a ripple generator as it is not necessary (the popup is closed on selection, the ripple effect is
|
||||
* barely noticeable).
|
||||
* <p></p>
|
||||
* The label used to display the data (in case it's not a Node), uses the combo box's
|
||||
* {@link StringConverter} to convert the data to a String. In case it's null, toString() is
|
||||
* called on the data.
|
||||
*/
|
||||
public class MFXComboBoxCell<T> extends HBox implements Cell<T>, Themable {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-combo-box-cell";
|
||||
public class MFXComboBoxCell<T> extends MFXListCell<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private MFXCombo<T> combo;
|
||||
|
||||
protected final ReadOnlyObjectWrapper<T> data = new ReadOnlyObjectWrapper<>();
|
||||
protected final ReadOnlyIntegerWrapper index = new ReadOnlyIntegerWrapper();
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXComboBoxCell(MFXCombo<T> combo, T item) {
|
||||
super(item);
|
||||
this.combo = combo;
|
||||
}
|
||||
|
||||
protected final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper();
|
||||
protected final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected");
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public Optional<ISelectionModel<T>> getSelectionModel() {
|
||||
return Optional.ofNullable(combo.getSelectionModel());
|
||||
}
|
||||
|
||||
protected final MFXCombo<T> comboBox;
|
||||
private final Label label;
|
||||
@Override
|
||||
protected void sceneBuilderIntegration() {
|
||||
SceneBuilderIntegration.ifInSceneBuilder(() -> getStylesheets().add(MaterialFXStylesheets.COMBO_BOX_CELL.toData()));
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXComboBoxCell(MFXCombo<T> combo, T data) {
|
||||
this.comboBox = combo;
|
||||
@Override
|
||||
public List<String> defaultStyleClasses() {
|
||||
return List.of("mfx-combo-box-cell");
|
||||
}
|
||||
|
||||
setPrefHeight(32);
|
||||
setMaxHeight(USE_PREF_SIZE);
|
||||
setAlignment(Pos.CENTER_LEFT);
|
||||
setSpacing(5);
|
||||
@Override
|
||||
public VFXSimpleCell<T> setConverter(StringConverter<T> converter) {
|
||||
combo.setConverter(converter);
|
||||
return this;
|
||||
}
|
||||
|
||||
if (!(data instanceof Node)) {
|
||||
label = new Label();
|
||||
label.getStyleClass().add("data-label");
|
||||
label.textProperty().bind(Bindings.createStringBinding(
|
||||
() -> {
|
||||
StringConverter<T> converter = combo.getConverter();
|
||||
return converter != null ? converter.toString(getData()) : getData().toString();
|
||||
}, dataProperty(), combo.converterProperty()
|
||||
));
|
||||
} else {
|
||||
label = null;
|
||||
}
|
||||
@Override
|
||||
public StringConverter<T> getConverter() {
|
||||
return combo.getConverter();
|
||||
}
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
protected void initialize() {
|
||||
getStyleClass().add(STYLE_CLASS);
|
||||
setBehavior();
|
||||
render(getData());
|
||||
sceneBuilderIntegration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the following behaviors:
|
||||
* <p>
|
||||
* - Binds the selected property to the combo' selection model (checks for index). <p>
|
||||
* - Updates the selected PseudoClass state when selected property changes.<p>
|
||||
* - Adds and handler for MOUSE_PRESSED events to call {@link #updateSelection(MouseEvent)}.
|
||||
*/
|
||||
protected void setBehavior() {
|
||||
selected.addListener(invalidated -> pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, selected.get()));
|
||||
selected.bind(Bindings.createBooleanBinding(
|
||||
() -> comboBox.getSelectionModel().getSelectedIndex() == index.get(),
|
||||
comboBox.getSelectionModel().selectedIndexProperty(), index
|
||||
));
|
||||
addEventFilter(MouseEvent.MOUSE_PRESSED, this::updateSelection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for rendering the cell's content.
|
||||
* <p>
|
||||
* If the given data type is a Node, it is added to the children list,
|
||||
* otherwise a label is used to display the data.
|
||||
*/
|
||||
protected void render(T data) {
|
||||
if (data instanceof Node) {
|
||||
getChildren().setAll((Node) data);
|
||||
} else {
|
||||
getChildren().setAll(label);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the pressed mouse button is not the primary, exits immediately.
|
||||
* <p>
|
||||
* Orders the combo's selection model to select the index of this cell.
|
||||
*/
|
||||
protected void updateSelection(MouseEvent event) {
|
||||
if (event.getButton() != MouseButton.PRIMARY) {
|
||||
return;
|
||||
}
|
||||
|
||||
int index = getIndex();
|
||||
comboBox.getSelectionModel().selectIndex(index);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public Parent toParent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Theme getTheme() {
|
||||
return MaterialFXStylesheets.COMBO_BOX_CELL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the index property of the cell.
|
||||
* <p>
|
||||
* This is called before {@link #updateItem(Object)}.
|
||||
*/
|
||||
@Override
|
||||
public void updateIndex(int index) {
|
||||
setIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the data property of the cell.
|
||||
* <p>
|
||||
* This is called after {@link #updateIndex(int)}.
|
||||
*/
|
||||
@Override
|
||||
public void updateItem(T item) {
|
||||
setData(item);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
public T getData() {
|
||||
return data.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data property of the cell.
|
||||
*/
|
||||
public ReadOnlyObjectProperty<T> dataProperty() {
|
||||
return data.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setData(T data) {
|
||||
this.data.set(data);
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the cell's index.
|
||||
*/
|
||||
public ReadOnlyIntegerProperty indexProperty() {
|
||||
return index.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setIndex(int index) {
|
||||
this.index.set(index);
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the selection state of the cell.
|
||||
*/
|
||||
public ReadOnlyBooleanProperty selectedProperty() {
|
||||
return selected.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setSelected(boolean selected) {
|
||||
this.selected.set(selected);
|
||||
}
|
||||
@Override
|
||||
public void dispose() {
|
||||
combo = null;
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
244
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXDateCell.java
Executable file → Normal file
244
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXDateCell.java
Executable file → Normal file
@ -1,168 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls.cell;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXDatePicker;
|
||||
import io.github.palexdev.materialfx.controls.base.Themable;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.materialfx.theming.base.Theme;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Simple implementation of a {@link Cell} capable of representing {@link LocalDate} values.
|
||||
* <p></p>
|
||||
* It has three main states:
|
||||
* <p> - selected: when the cell's value is equal to {@link MFXDatePicker#valueProperty()}
|
||||
* <p> - current: when the cell's value is equal to {@link MFXDatePicker#currentDateProperty()}
|
||||
* <p> - extra: to mark this cells as belonging to a different month
|
||||
*/
|
||||
public class MFXDateCell extends Label implements Cell<LocalDate>, Themable {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-date-cell";
|
||||
import io.github.palexdev.materialfx.controls.MFXDatePicker;
|
||||
import io.github.palexdev.materialfx.skins.MFXListCellSkin;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.materialfx.theming.PseudoClasses;
|
||||
import io.github.palexdev.mfxcore.controls.SkinBase;
|
||||
import io.github.palexdev.mfxcore.utils.fx.SceneBuilderIntegration;
|
||||
import io.github.palexdev.virtualizedfx.cells.CellBaseBehavior;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
private final MFXDatePicker datePicker;
|
||||
private final ReadOnlyObjectWrapper<LocalDate> date = new ReadOnlyObjectWrapper<>();
|
||||
public class MFXDateCell extends MFXListCell<LocalDate> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private MFXDatePicker datePicker;
|
||||
|
||||
private final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper();
|
||||
protected static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected");
|
||||
private final ReadOnlyBooleanWrapper current = new ReadOnlyBooleanWrapper(false) {
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
PseudoClasses.setOn(MFXDateCell.this, PseudoClasses.CURRENT, get());
|
||||
}
|
||||
};
|
||||
|
||||
private final ReadOnlyBooleanWrapper current = new ReadOnlyBooleanWrapper();
|
||||
protected static final PseudoClass CURRENT_PSEUDO_CLASS = PseudoClass.getPseudoClass("current");
|
||||
private boolean extra = false;
|
||||
|
||||
private boolean extra = false;
|
||||
protected static final PseudoClass EXTRA_PSEUDO_CLASS = PseudoClass.getPseudoClass("extra");
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXDateCell(MFXDatePicker datePicker, LocalDate item) {
|
||||
super(item);
|
||||
this.datePicker = datePicker;
|
||||
initialize();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXDateCell(MFXDatePicker datePicker, LocalDate date) {
|
||||
this.datePicker = datePicker;
|
||||
updateItem(date);
|
||||
initialize();
|
||||
}
|
||||
public MFXDateCell(MFXDatePicker datePicker, LocalDate item, StringConverter<LocalDate> converter) {
|
||||
super(item, converter);
|
||||
this.datePicker = datePicker;
|
||||
initialize();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
private void initialize() {
|
||||
getStyleClass().add(STYLE_CLASS);
|
||||
setAlignment(Pos.CENTER);
|
||||
setBehavior();
|
||||
sceneBuilderIntegration();
|
||||
}
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
private void initialize() {
|
||||
selected.bind(datePicker.valueProperty().isEqualTo(itemProperty()));
|
||||
current.bind(datePicker.currentDateProperty().isEqualTo(itemProperty()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the behavior for selected and current states. Binds the text to {@link LocalDate#getDayOfMonth()} (from the current value),
|
||||
* binds the visible property to the cell's text (hidden if text is empty, visible if text is not empty)
|
||||
* <p>
|
||||
* Also handles MOUSE_PRESSED events to change the date picker's value.
|
||||
*/
|
||||
protected void setBehavior() {
|
||||
selected.addListener((observable, oldValue, newValue) -> pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, selected.get()));
|
||||
current.addListener((observable, oldValue, newValue) -> pseudoClassStateChanged(CURRENT_PSEUDO_CLASS, current.get()));
|
||||
/**
|
||||
* Marks/unmarks this cell as an extra cell.
|
||||
*/
|
||||
public void setExtra(boolean isExtra) {
|
||||
extra = isExtra;
|
||||
PseudoClasses.setOn(this, PseudoClasses.EXTRA, isExtra);
|
||||
}
|
||||
|
||||
textProperty().bind(Bindings.createStringBinding(
|
||||
() -> getDate() != null ? String.valueOf(getDate().getDayOfMonth()) : "",
|
||||
dateProperty()
|
||||
));
|
||||
visibleProperty().bind(textProperty().isNotEmpty());
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected void sceneBuilderIntegration() {
|
||||
SceneBuilderIntegration.ifInSceneBuilder(() -> getStylesheets().add(MaterialFXStylesheets.DATE_CELL.toData()));
|
||||
}
|
||||
|
||||
selected.bind(datePicker.valueProperty().isEqualTo(dateProperty()));
|
||||
current.bind(datePicker.currentDateProperty().isEqualTo(dateProperty()));
|
||||
addEventHandler(MouseEvent.MOUSE_PRESSED, event -> datePicker.setValue(getDate()));
|
||||
}
|
||||
@Override
|
||||
protected SkinBase<?, ?> buildSkin() {
|
||||
return new MFXListCellSkin<>(this) {
|
||||
{
|
||||
visibleProperty().bind(label.textProperty().isNotEmpty());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this cell as an extra cell.
|
||||
*/
|
||||
public void markAsExtra() {
|
||||
extra = true;
|
||||
pseudoClassStateChanged(EXTRA_PSEUDO_CLASS, true);
|
||||
}
|
||||
@Override
|
||||
public Supplier<CellBaseBehavior<LocalDate>> defaultBehaviorProvider() {
|
||||
return () -> new MFXListCellBehavior<>(this) {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getButton() == MouseButton.PRIMARY) {
|
||||
datePicker.setValue(getItem());
|
||||
}
|
||||
mouseClicked(e, null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Un-marks this cell as extra.
|
||||
*/
|
||||
public void unmarkAsExtra() {
|
||||
extra = false;
|
||||
pseudoClassStateChanged(EXTRA_PSEUDO_CLASS, false);
|
||||
}
|
||||
@Override
|
||||
public List<String> defaultStyleClasses() {
|
||||
return List.of("mfx-date-cell");
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public Parent toParent() {
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public void dispose() {
|
||||
datePicker = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Theme getTheme() {
|
||||
return MaterialFXStylesheets.DATE_CELL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(LocalDate date) {
|
||||
setDate(date);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
public LocalDate getDate() {
|
||||
return date.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the cell's represented date.
|
||||
*/
|
||||
public ReadOnlyObjectProperty<LocalDate> dateProperty() {
|
||||
return date.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setDate(LocalDate date) {
|
||||
this.date.set(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the cell is an extra cell
|
||||
*/
|
||||
public boolean isExtra() {
|
||||
return extra;
|
||||
}
|
||||
//================================================================================
|
||||
// Getters
|
||||
//================================================================================
|
||||
public boolean isExtra() {
|
||||
return extra;
|
||||
}
|
||||
}
|
||||
|
@ -18,39 +18,55 @@
|
||||
|
||||
package io.github.palexdev.materialfx.controls.cell;
|
||||
|
||||
import io.github.palexdev.materialfx.collections.TransformableList;
|
||||
import io.github.palexdev.materialfx.collections.RefineList;
|
||||
import io.github.palexdev.materialfx.controls.base.MFXCombo;
|
||||
import io.github.palexdev.mfxcore.builders.bindings.BooleanBindingBuilder;
|
||||
import io.github.palexdev.virtualizedfx.base.VFXContainer;
|
||||
|
||||
/**
|
||||
* Extends {@link MFXComboBoxCell} to modify the {@link #updateIndex(int)} method.
|
||||
*/
|
||||
public class MFXFilterComboBoxCell<T> extends MFXComboBoxCell<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final TransformableList<T> filterList;
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final RefineList<T> refineList;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXFilterComboBoxCell(MFXCombo<T> combo, TransformableList<T> filterList, T data) {
|
||||
super(combo, data);
|
||||
this.filterList = filterList;
|
||||
}
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXFilterComboBoxCell(MFXCombo<T> combo, RefineList<T> refineList, T data) {
|
||||
super(combo, data);
|
||||
this.refineList = refineList;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p></p>
|
||||
* A filter combo box uses a {@link TransformableList} to display the filtered items
|
||||
* in the list. The thing is, when items are filtered their index changes as well. For
|
||||
* selection to work properly the index must be converted using {@link TransformableList#viewToSource(int)}.
|
||||
*/
|
||||
@Override
|
||||
public void updateIndex(int index) {
|
||||
super.updateIndex(filterList.viewToSource(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreated(VFXContainer<T> container) {
|
||||
super.onCreated(container);
|
||||
|
||||
getSelectionModel().ifPresent(sm ->
|
||||
selected.bind(BooleanBindingBuilder.build()
|
||||
.setMapper(() -> {
|
||||
int index = getIndex();
|
||||
if (refineList.getPredicate() != null) {
|
||||
try {
|
||||
int toSource = refineList.viewToSource(index);
|
||||
return sm.contains(toSource);
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return sm.contains(index);
|
||||
}
|
||||
})
|
||||
.addSources(sm.selection(), indexProperty(), refineList.getView())
|
||||
.get()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
353
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXListCell.java
Executable file → Normal file
353
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXListCell.java
Executable file → Normal file
@ -1,164 +1,231 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls.cell;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.PositionBean;
|
||||
import io.github.palexdev.materialfx.controls.MFXListView;
|
||||
import io.github.palexdev.materialfx.controls.cell.base.AbstractMFXListCell;
|
||||
import io.github.palexdev.materialfx.effects.ripple.MFXCircleRippleGenerator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.github.palexdev.materialfx.selection.base.ISelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.base.WithSelectionModel;
|
||||
import io.github.palexdev.materialfx.skins.MFXListCellSkin;
|
||||
import io.github.palexdev.materialfx.theming.MaterialFXStylesheets;
|
||||
import io.github.palexdev.materialfx.theming.base.Theme;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectExpression;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.control.Label;
|
||||
import io.github.palexdev.materialfx.theming.PseudoClasses;
|
||||
import io.github.palexdev.mfxcore.base.properties.styleable.StyleableDoubleProperty;
|
||||
import io.github.palexdev.mfxcore.builders.bindings.BooleanBindingBuilder;
|
||||
import io.github.palexdev.mfxcore.controls.SkinBase;
|
||||
import io.github.palexdev.mfxcore.utils.fx.SceneBuilderIntegration;
|
||||
import io.github.palexdev.mfxcore.utils.fx.StyleUtils;
|
||||
import io.github.palexdev.virtualizedfx.base.VFXContainer;
|
||||
import io.github.palexdev.virtualizedfx.cells.CellBaseBehavior;
|
||||
import io.github.palexdev.virtualizedfx.cells.VFXSimpleCell;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.css.CssMetaData;
|
||||
import javafx.css.Styleable;
|
||||
import javafx.css.StyleablePropertyFactory;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link AbstractMFXListCell},
|
||||
* includes a ripple generator for ripple effects on mouse pressed.
|
||||
* <p></p>
|
||||
* The label used to display the data is built in the constructor
|
||||
* only if the given T data is not a Node, otherwise it's null.
|
||||
* <p></p>
|
||||
* The label's text is bound to the data property and converted to a String
|
||||
* using {@link ObjectExpression#asString()}.
|
||||
*/
|
||||
public class MFXListCell<T> extends AbstractMFXListCell<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-list-cell";
|
||||
protected final MFXCircleRippleGenerator rippleGenerator = new MFXCircleRippleGenerator(this);
|
||||
public class MFXListCell<T> extends VFXSimpleCell<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
protected final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper(false) {
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
PseudoClasses.setOn(MFXListCell.this, PseudoClasses.SELECTED, get());
|
||||
}
|
||||
};
|
||||
|
||||
private final Label label;
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXListCell(T item) {
|
||||
super(item);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXListCell(MFXListView<T> listView, T data) {
|
||||
super(listView, data);
|
||||
public MFXListCell(T item, StringConverter<T> converter) {
|
||||
super(item, converter);
|
||||
}
|
||||
|
||||
if (!(data instanceof Node)) {
|
||||
label = new Label();
|
||||
label.textProperty().bind(Bindings.createStringBinding(
|
||||
() -> listView.getConverter() != null ? listView.getConverter().toString(getData()) : getData().toString(),
|
||||
dataProperty(), listView.converterProperty()
|
||||
));
|
||||
label.getStyleClass().add("data-label");
|
||||
} else {
|
||||
label = null;
|
||||
}
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public void onCreated(VFXContainer<T> container) {
|
||||
super.onCreated(container);
|
||||
|
||||
initialize();
|
||||
}
|
||||
getSelectionModel().ifPresent(sm ->
|
||||
selected.bind(BooleanBindingBuilder.build()
|
||||
.setMapper(() -> sm.contains(getIndex()))
|
||||
.addSources(sm.selection(), indexProperty())
|
||||
.get()
|
||||
)
|
||||
);
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
sceneBuilderIntegration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overridden to add the style class, setup the ripple generator and call {@link #render(Object)}
|
||||
* for the first time.
|
||||
*/
|
||||
@Override
|
||||
protected void initialize() {
|
||||
super.initialize();
|
||||
getStyleClass().add(STYLE_CLASS);
|
||||
setupRippleGenerator();
|
||||
render(getData());
|
||||
sceneBuilderIntegration();
|
||||
}
|
||||
@Override
|
||||
protected SkinBase<?, ?> buildSkin() {
|
||||
return new MFXListCellSkin<>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the properties of the ripple generator and adds the mouse pressed filter.
|
||||
*/
|
||||
protected void setupRippleGenerator() {
|
||||
rippleGenerator.setManaged(false);
|
||||
rippleGenerator.setRipplePositionFunction(event -> PositionBean.of(event.getX(), event.getY()));
|
||||
rippleGenerator.rippleRadiusProperty().bind(widthProperty().divide(2.0));
|
||||
addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
|
||||
if (event.getButton() == MouseButton.PRIMARY) {
|
||||
rippleGenerator.generateRipple(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public Supplier<CellBaseBehavior<T>> defaultBehaviorProvider() {
|
||||
return () -> new MFXListCellBehavior<>(this);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden/Implemented Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public List<String> defaultStyleClasses() {
|
||||
return List.of("cell-base", "cell", "mfx-list-cell");
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for rendering the cell's content.
|
||||
* <p>
|
||||
* If the given data type is a Node, it is added to the children list,
|
||||
* otherwise a label is used to display the data.
|
||||
* <p>
|
||||
* At the end adds a ripple generator at index 0.
|
||||
*/
|
||||
@Override
|
||||
protected void render(T data) {
|
||||
if (data instanceof Node) {
|
||||
getChildren().setAll(rippleGenerator, (Node) data);
|
||||
} else {
|
||||
getChildren().setAll(rippleGenerator, label);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void sceneBuilderIntegration() {
|
||||
SceneBuilderIntegration.ifInSceneBuilder(() -> getStylesheets().add(MaterialFXStylesheets.LIST_CELL.toData()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the data property of the cell. If the data is a Node
|
||||
* {@link #render(Object)} is called.
|
||||
* <p>
|
||||
* This is called after {@link #updateIndex(int)}.
|
||||
*/
|
||||
@Override
|
||||
public void updateItem(T item) {
|
||||
super.updateItem(item);
|
||||
if (item instanceof Node) render(item);
|
||||
}
|
||||
//================================================================================
|
||||
// Styleable Properties
|
||||
//================================================================================
|
||||
private final StyleableDoubleProperty hGap = new StyleableDoubleProperty(
|
||||
StyleableProperties.HGAP,
|
||||
this,
|
||||
"hGap",
|
||||
10.0
|
||||
);
|
||||
|
||||
@Override
|
||||
public Parent toParent() {
|
||||
return this;
|
||||
}
|
||||
public double getHGap() {
|
||||
return hGap.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Theme getTheme() {
|
||||
return MaterialFXStylesheets.LIST_CELL;
|
||||
}
|
||||
public StyleableDoubleProperty hGapProperty() {
|
||||
return hGap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String className = getClass().getName();
|
||||
String simpleName = className.substring(className.lastIndexOf('.') + 1);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[").append(simpleName);
|
||||
sb.append('@');
|
||||
sb.append(Integer.toHexString(hashCode()));
|
||||
sb.append("]");
|
||||
sb.append("[Data:").append(getData()).append("]");
|
||||
if (getId() != null) {
|
||||
sb.append("[id:").append(getId()).append("]");
|
||||
}
|
||||
public void setHGap(double hGap) {
|
||||
this.hGap.set(hGap);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
//================================================================================
|
||||
// CssMetaData
|
||||
//================================================================================
|
||||
private static class StyleableProperties {
|
||||
private static final StyleablePropertyFactory<MFXListCell<?>> FACTORY = new StyleablePropertyFactory<>(VFXSimpleCell.getClassCssMetaData());
|
||||
private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;
|
||||
|
||||
private static final CssMetaData<MFXListCell<?>, Number> HGAP =
|
||||
FACTORY.createSizeCssMetaData(
|
||||
"-fx-hgap",
|
||||
MFXListCell::hGapProperty,
|
||||
10.0
|
||||
);
|
||||
|
||||
static {
|
||||
cssMetaDataList = StyleUtils.cssMetaDataList(
|
||||
VFXSimpleCell.getClassCssMetaData(),
|
||||
HGAP
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
|
||||
return StyleableProperties.cssMetaDataList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
|
||||
return getClassCssMetaData();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters
|
||||
//================================================================================
|
||||
@SuppressWarnings("unchecked")
|
||||
public Optional<ISelectionModel<T>> getSelectionModel() {
|
||||
if (getContainer() instanceof WithSelectionModel<?>) {
|
||||
return Optional.of(((WithSelectionModel<T>) getContainer()).getSelectionModel());
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected.get();
|
||||
}
|
||||
|
||||
public ReadOnlyBooleanProperty selectedProperty() {
|
||||
return selected.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Inner Classes
|
||||
//================================================================================
|
||||
public static class MFXListCellBehavior<T> extends CellBaseBehavior<T> {
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXListCellBehavior(MFXListCell<T> cell) {
|
||||
super(cell);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
protected void updateSelection(SelectionMode mode) {
|
||||
MFXListCell<T> cell = getNode();
|
||||
cell.getSelectionModel().ifPresent(sm -> {
|
||||
int index = cell.getIndex();
|
||||
switch (mode) {
|
||||
case STANDARD -> {
|
||||
if (cell.isSelected()) {
|
||||
sm.deselectIndex(index);
|
||||
} else {
|
||||
sm.selectIndex(index);
|
||||
}
|
||||
}
|
||||
case EXTEND -> sm.expandSelection(index, true);
|
||||
case REPLACE -> sm.replaceSelection(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e, Consumer<MouseEvent> callback) {
|
||||
// We use a null event to signal the selection update comes from another node
|
||||
if (e == null) {
|
||||
updateSelection(SelectionMode.STANDARD);
|
||||
return;
|
||||
} else if (e.getButton() == MouseButton.PRIMARY) {
|
||||
SelectionMode sm = SelectionMode.forEvent(e);
|
||||
updateSelection(sm);
|
||||
}
|
||||
if (callback != null) callback.accept(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MFXListCell<T> getNode() {
|
||||
return (MFXListCell<T>) super.getNode();
|
||||
}
|
||||
|
||||
public enum SelectionMode {
|
||||
STANDARD,
|
||||
EXTEND,
|
||||
REPLACE,
|
||||
;
|
||||
|
||||
public static SelectionMode forEvent(MouseEvent me) {
|
||||
if (me.isControlDown())
|
||||
return SelectionMode.STANDARD;
|
||||
if (me.isShiftDown())
|
||||
return EXTEND;
|
||||
return REPLACE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
281
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXNotificationCell.java
Executable file → Normal file
281
materialfx/src/main/java/io/github/palexdev/materialfx/controls/cell/MFXNotificationCell.java
Executable file → Normal file
@ -1,251 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls.cell;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXCheckbox;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXNotificationCenter;
|
||||
import io.github.palexdev.materialfx.effects.Interpolators;
|
||||
import io.github.palexdev.materialfx.selection.base.ISelectionModel;
|
||||
import io.github.palexdev.materialfx.notifications.base.INotification;
|
||||
import io.github.palexdev.materialfx.utils.AnimationUtils.KeyFrames;
|
||||
import io.github.palexdev.materialfx.utils.AnimationUtils.ParallelBuilder;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
|
||||
/**
|
||||
* Implementation of a {@link Cell} for usage with {@link MFXNotificationCenter}.
|
||||
* <p></p>
|
||||
* Includes a checkbox to allow selecting notifications.
|
||||
*/
|
||||
public class MFXNotificationCell extends HBox implements Cell<INotification> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-notification-cell";
|
||||
private final MFXNotificationCenter notificationCenter;
|
||||
private final ReadOnlyObjectWrapper<INotification> notification = new ReadOnlyObjectWrapper<>();
|
||||
private final ReadOnlyIntegerWrapper index = new ReadOnlyIntegerWrapper();
|
||||
private final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper();
|
||||
public class MFXNotificationCell extends MFXCheckListCell<INotification> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private MFXNotificationCenter notificationCenter;
|
||||
|
||||
protected final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected");
|
||||
protected final StackPane container;
|
||||
protected final MFXCheckbox checkbox;
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXNotificationCell(MFXNotificationCenter notificationCenter, INotification item) {
|
||||
super(item);
|
||||
this.notificationCenter = notificationCenter;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXNotificationCell(MFXNotificationCenter notificationCenter, INotification notification) {
|
||||
this.notificationCenter = notificationCenter;
|
||||
setNotification(notification);
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public Optional<ISelectionModel<INotification>> getSelectionModel() {
|
||||
return Optional.ofNullable(notificationCenter.getSelectionModel());
|
||||
}
|
||||
|
||||
setPrefHeight(80);
|
||||
setMaxHeight(USE_PREF_SIZE);
|
||||
setAlignment(Pos.CENTER_LEFT);
|
||||
@Override
|
||||
protected void sceneBuilderIntegration() {
|
||||
}
|
||||
|
||||
checkbox = new MFXCheckbox("");
|
||||
checkbox.setId("check");
|
||||
@Override
|
||||
public List<String> defaultStyleClasses() {
|
||||
return List.of("mfx-notification-cell");
|
||||
}
|
||||
|
||||
container = new StackPane(checkbox);
|
||||
container.setMinWidth(USE_PREF_SIZE);
|
||||
container.setPrefWidth(0);
|
||||
container.setMaxWidth(USE_PREF_SIZE);
|
||||
@Override
|
||||
public void dispose() {
|
||||
this.notificationCenter = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Rectangle clip = new Rectangle();
|
||||
clip.widthProperty().bind(container.widthProperty());
|
||||
clip.heightProperty().bind(container.heightProperty());
|
||||
container.setClip(clip);
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Adds the style class, calls {@link #setBehavior()} then {@link #render(INotification)}
|
||||
* for the first time.
|
||||
*/
|
||||
private void initialize() {
|
||||
getStyleClass().add(STYLE_CLASS);
|
||||
setBehavior();
|
||||
render(getNotification());
|
||||
if (notificationCenter.isSelectionMode()) expand(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the following behaviors:
|
||||
* <p>
|
||||
* - Binds the selected property to the notification center' selection model (checks for index). <p>
|
||||
* - Updates the selected PseudoClass state when selected property changes. <p>
|
||||
* - Adds a listener to the checkbox' selection state to call {@link #updateSelection(boolean)}. <p>
|
||||
* - Adds a listener to the notification center's {@link MFXNotificationCenter#selectionModeProperty()} to call {@link #expand(boolean)}.
|
||||
*/
|
||||
protected void setBehavior() {
|
||||
selected.addListener(invalidated -> pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, selected.get()));
|
||||
selected.bind(Bindings.createBooleanBinding(() -> {
|
||||
boolean contained = notificationCenter.getSelectionModel().getSelection().containsKey(getIndex());
|
||||
checkbox.setSelected(contained);
|
||||
return contained;
|
||||
}, notificationCenter.getSelectionModel().selectionProperty(), index));
|
||||
|
||||
checkbox.selectedProperty().addListener((observable, oldValue, newValue) -> updateSelection(newValue));
|
||||
notificationCenter.selectionModeProperty().addListener((observable, oldValue, newValue) -> expand(newValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for rendering the cell's content.
|
||||
*/
|
||||
protected void render(INotification notification) {
|
||||
if (notificationCenter.isSelectionMode()) {
|
||||
checkbox.setOpacity(1.0);
|
||||
checkbox.setPrefWidth(45);
|
||||
}
|
||||
getChildren().setAll(container, notification.getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for updating the selection state according to the checkbox' state.
|
||||
* <p>
|
||||
* If checked is true then the cell should be selected, otherwise it is deselected.
|
||||
*/
|
||||
protected void updateSelection(boolean checked) {
|
||||
int index = getIndex();
|
||||
if (checked) {
|
||||
notificationCenter.getSelectionModel().selectIndex(index);
|
||||
} else {
|
||||
notificationCenter.getSelectionModel().deselectIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for showing/hiding the checkbox.
|
||||
*/
|
||||
protected void expand(boolean selectionMode) {
|
||||
double width = selectionMode ? 45 : 0;
|
||||
double opacity = selectionMode ? 1 : 0;
|
||||
if (notificationCenter.isAnimated()) {
|
||||
ParallelBuilder.build()
|
||||
.add(
|
||||
KeyFrames.of(150, checkbox.opacityProperty(), opacity, Interpolators.EASE_OUT),
|
||||
KeyFrames.of(250, container.prefWidthProperty(), width, Interpolators.EASE_OUT_SINE)
|
||||
).getAnimation().play();
|
||||
} else {
|
||||
container.setPrefWidth(width);
|
||||
checkbox.setOpacity(opacity);
|
||||
}
|
||||
if (!selectionMode) {
|
||||
notificationCenter.getSelectionModel().clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
|
||||
@Override
|
||||
public Node getNode() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the notification property of the cell, then calls {@link #render(INotification)}.
|
||||
* <p>
|
||||
* This is called after {@link #updateIndex(int)}.
|
||||
*/
|
||||
@Override
|
||||
public void updateItem(INotification notification) {
|
||||
setNotification(notification);
|
||||
render(notification);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the index property of the cell.
|
||||
* <p>
|
||||
* This is called before {@link #updateItem(INotification)}.
|
||||
*/
|
||||
@Override
|
||||
public void updateIndex(int index) {
|
||||
setIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the combobox container is properly expanded
|
||||
* after the cell has been laid out.
|
||||
*/
|
||||
@Override
|
||||
public void afterLayout() {
|
||||
expand(notificationCenter.isSelectionMode());
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
|
||||
public INotification getNotification() {
|
||||
return this.notification.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the current shown notification (in other words the cell's content).
|
||||
*/
|
||||
public ReadOnlyObjectProperty<INotification> notificationProperty() {
|
||||
return this.notification.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setNotification(INotification notification) {
|
||||
this.notification.set(notification);
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return this.index.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the cell's index.
|
||||
*/
|
||||
protected ReadOnlyIntegerProperty indexProperty() {
|
||||
return this.index.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setIndex(int index) {
|
||||
this.index.set(index);
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return this.selected.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the selection state of the cell.
|
||||
*/
|
||||
public ReadOnlyBooleanProperty selectedProperty() {
|
||||
return this.selected.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setSelected(boolean selected) {
|
||||
this.selected.set(selected);
|
||||
}
|
||||
//================================================================================
|
||||
// Getters
|
||||
//================================================================================
|
||||
public MFXNotificationCenter getNotificationCenter() {
|
||||
return notificationCenter;
|
||||
}
|
||||
}
|
||||
|
@ -1,191 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls.cell;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.Alignment;
|
||||
import io.github.palexdev.materialfx.beans.NumberRange;
|
||||
import io.github.palexdev.materialfx.controls.MFXListView;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXPagination;
|
||||
import io.github.palexdev.materialfx.controls.MFXPopup;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||
import javafx.beans.property.ReadOnlyIntegerWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import io.github.palexdev.materialfx.skins.MFXPageSkin;
|
||||
import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
|
||||
import io.github.palexdev.mfxcore.builders.bindings.BooleanBindingBuilder;
|
||||
import io.github.palexdev.mfxcore.controls.SkinBase;
|
||||
import io.github.palexdev.virtualizedfx.base.VFXContainer;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
|
||||
/**
|
||||
* This is the default cell used by {@link MFXPagination} to show the page indexes.
|
||||
* <p></p>
|
||||
* It's a very basic cell that show's the page's index as text (since it extends {@link Label}),
|
||||
* handles the selection state (according to {@link MFXPagination#currentPageProperty()}).
|
||||
* <p>
|
||||
* If the cells represents a truncated page, the text is specified by {@link MFXPagination#ellipseStringProperty()}.
|
||||
* In this case the cell also allows to show a popup containing the hidden pages, for faster and easier navigation.
|
||||
*/
|
||||
public class MFXPage extends Label implements Cell<Integer> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final String STYLE_CLASS = "mfx-page";
|
||||
public class MFXPage extends MFXListCell<Integer> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private MFXPagination pagination;
|
||||
private IntegerRange between;
|
||||
|
||||
private final MFXPagination pagination;
|
||||
private final ReadOnlyIntegerWrapper index = new ReadOnlyIntegerWrapper();
|
||||
private NumberRange<Integer> between;
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXPage(MFXPagination pagination, int item) {
|
||||
super(item);
|
||||
this.pagination = pagination;
|
||||
setDefaultConverter();
|
||||
}
|
||||
|
||||
private final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper();
|
||||
protected static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected");
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
public void setDefaultConverter() {
|
||||
setConverter(i -> i == -1 ? pagination.getEllipseString() : String.valueOf(i));
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXPage(MFXPagination pagination, int index) {
|
||||
this.pagination = pagination;
|
||||
updateItem(index);
|
||||
initialize();
|
||||
addListeners();
|
||||
}
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public void onCreated(VFXContainer<Integer> container) {
|
||||
super.onCreated(container);
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
private void initialize() {
|
||||
getStyleClass().add(STYLE_CLASS);
|
||||
setAlignment(Pos.CENTER);
|
||||
}
|
||||
selected.bind(BooleanBindingBuilder.build()
|
||||
.setMapper(() -> pagination.getCurrentPage() == getItem())
|
||||
.addSources(itemProperty(), pagination.currentPageProperty())
|
||||
.get()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles selection, text property and updates the {@link MFXPagination#currentPageProperty()} on click.
|
||||
* <p>
|
||||
* If truncated and enabled, {@link #showPopup()} on click.
|
||||
*/
|
||||
private void addListeners() {
|
||||
selected.addListener(invalidated -> pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, selected.get()));
|
||||
selected.bind(Bindings.createBooleanBinding(
|
||||
() -> pagination.getCurrentPage() == getIndex(),
|
||||
indexProperty(), pagination.currentPageProperty()
|
||||
));
|
||||
@Override
|
||||
protected SkinBase<?, ?> buildSkin() {
|
||||
return new MFXPageSkin(this);
|
||||
}
|
||||
|
||||
textProperty().bind(Bindings.createStringBinding(
|
||||
() -> getIndex() == -1 ? pagination.getEllipseString() : String.valueOf(getIndex()),
|
||||
indexProperty()
|
||||
));
|
||||
@Override
|
||||
public void dispose() {
|
||||
this.pagination = null;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
|
||||
if (event.getButton() != MouseButton.PRIMARY) return;
|
||||
if (getIndex() != -1) {
|
||||
pagination.setCurrentPage(getIndex());
|
||||
} else {
|
||||
showPopup();
|
||||
}
|
||||
});
|
||||
}
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
public MFXPagination getPagination() {
|
||||
return pagination;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the page is truncated, shows a popup containing the hidden pages' indexes.
|
||||
*/
|
||||
protected void showPopup() {
|
||||
if (!pagination.isShowPopupForTruncatedPages() || between == null) return;
|
||||
public IntegerRange getBetween() {
|
||||
return between;
|
||||
}
|
||||
|
||||
ObservableList<Integer> indexes = FXCollections.observableArrayList(NumberRange.expandRange(between));
|
||||
MFXListView<Integer> listView = new MFXListView<>(indexes);
|
||||
public void setBetween(IntegerRange between) {
|
||||
this.between = between;
|
||||
}
|
||||
|
||||
MFXPopup popup = new MFXPopup(listView);
|
||||
popup.getStyleClass().add("pages-popup");
|
||||
popup.setPopupStyleableParent(pagination);
|
||||
//================================================================================
|
||||
// Inner Classes
|
||||
//================================================================================
|
||||
public static class MFXPageBehavior extends MFXListCellBehavior<Integer> {
|
||||
|
||||
listView.setCellFactory(integer -> {
|
||||
MFXListCell<Integer> cell = new MFXListCell<>(listView, integer);
|
||||
cell.setOnMouseClicked(event -> {
|
||||
pagination.setCurrentPage(cell.getData());
|
||||
popup.hide();
|
||||
});
|
||||
return cell;
|
||||
});
|
||||
public MFXPageBehavior(MFXPage cell) {
|
||||
super(cell);
|
||||
}
|
||||
|
||||
popup.show(this, Alignment.of(HPos.CENTER, VPos.BOTTOM), 0, 5);
|
||||
}
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e, Consumer<MouseEvent> callback) {
|
||||
MFXPage cell = getNode();
|
||||
if (e.getButton() == MouseButton.PRIMARY) {
|
||||
MFXPagination pagination = cell.getPagination();
|
||||
Integer item = cell.getItem();
|
||||
if (item != -1) {
|
||||
pagination.setCurrentPage(item);
|
||||
} else {
|
||||
super.mouseClicked(e, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public Node getNode() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateItem(Integer index) {
|
||||
setIndex(index);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
public int getIndex() {
|
||||
return index.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the page's index.
|
||||
*/
|
||||
public ReadOnlyIntegerProperty indexProperty() {
|
||||
return index.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setIndex(int index) {
|
||||
this.index.set(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the range of hidden pages, if truncated otherwise null
|
||||
*/
|
||||
public NumberRange<Integer> getBetween() {
|
||||
return between;
|
||||
}
|
||||
|
||||
public void setBetween(NumberRange<Integer> between) {
|
||||
this.between = between;
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the selection state of the page.
|
||||
*/
|
||||
public ReadOnlyBooleanProperty selectedProperty() {
|
||||
return selected.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setSelected(boolean selected) {
|
||||
this.selected.set(selected);
|
||||
}
|
||||
@Override
|
||||
public MFXPage getNode() {
|
||||
return (MFXPage) super.getNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,137 @@
|
||||
package io.github.palexdev.materialfx.controls.cell;
|
||||
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import io.github.palexdev.materialfx.collections.RefineList;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXListCell.MFXListCellBehavior.SelectionMode;
|
||||
import io.github.palexdev.materialfx.selection.base.ISelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.base.WithSelectionModel;
|
||||
import io.github.palexdev.materialfx.theming.PseudoClasses;
|
||||
import io.github.palexdev.mfxcore.builders.bindings.BooleanBindingBuilder;
|
||||
import io.github.palexdev.mfxcore.events.WhenEvent;
|
||||
import io.github.palexdev.mfxeffects.ripple.MFXRippleGenerator;
|
||||
import io.github.palexdev.virtualizedfx.base.VFXContainer;
|
||||
import io.github.palexdev.virtualizedfx.table.VFXTable;
|
||||
import io.github.palexdev.virtualizedfx.table.defaults.VFXDefaultTableRow;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
|
||||
public class MFXTableRow<T> extends VFXDefaultTableRow<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
protected final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper(false) {
|
||||
@Override
|
||||
protected void invalidated() {
|
||||
PseudoClasses.setOn(MFXTableRow.this, PseudoClasses.SELECTED, get());
|
||||
}
|
||||
};
|
||||
|
||||
private final MFXRippleGenerator rg = new MFXRippleGenerator(this);
|
||||
private WhenEvent<?> wMouseClicked;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXTableRow(T item) {
|
||||
super(item);
|
||||
|
||||
rg.enable();
|
||||
wMouseClicked = WhenEvent.intercept(this, MouseEvent.MOUSE_CLICKED)
|
||||
.condition(e -> e.getButton() == MouseButton.PRIMARY)
|
||||
.process(e -> updateSelection(SelectionMode.forEvent(e)))
|
||||
.register();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
protected void updateSelection(SelectionMode mode) {
|
||||
getSelectionModel().ifPresent(sm -> {
|
||||
int index = getIndex();
|
||||
switch (mode) {
|
||||
case STANDARD -> {
|
||||
if (isSelected()) {
|
||||
sm.deselectIndex(index);
|
||||
} else {
|
||||
sm.selectIndex(index);
|
||||
}
|
||||
}
|
||||
case EXTEND -> sm.expandSelection(index, true);
|
||||
case REPLACE -> sm.replaceSelection(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected void onCellsChanged() {
|
||||
super.onCellsChanged();
|
||||
getChildren().addFirst(rg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreated(VFXContainer<T> container) {
|
||||
super.onCreated(container);
|
||||
|
||||
getSelectionModel().ifPresent(sm ->
|
||||
selected.bind(BooleanBindingBuilder.build()
|
||||
.setMapper(() -> sm.contains(getIndex()))
|
||||
.addSources(sm.selection(), indexProperty())
|
||||
.get()
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateIndex(int index) {
|
||||
ObservableList<T> items = getTable().getItems();
|
||||
if (items instanceof RefineList<T> rl) {
|
||||
setIndex(rl.viewToSource(index));
|
||||
return;
|
||||
}
|
||||
super.updateIndex(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layoutChildren() {
|
||||
super.layoutChildren();
|
||||
|
||||
rg.resizeRelocate(0, 0, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (wMouseClicked != null) {
|
||||
wMouseClicked.dispose();
|
||||
wMouseClicked = null;
|
||||
}
|
||||
rg.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters
|
||||
//================================================================================
|
||||
@SuppressWarnings("unchecked")
|
||||
public Optional<ISelectionModel<T>> getSelectionModel() {
|
||||
VFXTable<T> table = getTable();
|
||||
if (table instanceof WithSelectionModel<?>) {
|
||||
return Optional.of(((WithSelectionModel<T>) table).getSelectionModel());
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected.get();
|
||||
}
|
||||
|
||||
public ReadOnlyBooleanProperty selectedProperty() {
|
||||
return selected.getReadOnlyProperty();
|
||||
}
|
||||
}
|
@ -19,8 +19,6 @@
|
||||
package io.github.palexdev.materialfx.controls.cell;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXTableColumn;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableRow;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableView;
|
||||
import io.github.palexdev.materialfx.skins.MFXTableRowCellSkin;
|
||||
import io.github.palexdev.materialfx.utils.others.FunctionalStringConverter;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
|
@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.controls.cell.base;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.base.AbstractMFXListView;
|
||||
import io.github.palexdev.materialfx.controls.base.Themable;
|
||||
import io.github.palexdev.materialfx.selection.MultipleSelectionModel;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.*;
|
||||
import javafx.css.PseudoClass;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.input.MouseButton;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
/**
|
||||
* Base class for all cells used in listviews based on VirtualizedFX,
|
||||
* defines common properties and behavior (e.g. selection), has the selected property
|
||||
* and PseudoClass ":selected" for usage in CSS.
|
||||
* <p>
|
||||
* Extends {@link HBox} and implements {@link Cell}.
|
||||
*
|
||||
* @param <T> the type of data within the ListView
|
||||
*/
|
||||
public abstract class AbstractMFXListCell<T> extends HBox implements Cell<T>, Themable {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
protected final AbstractMFXListView<T, ?> listView;
|
||||
protected final ReadOnlyObjectWrapper<T> data = new ReadOnlyObjectWrapper<>();
|
||||
protected final ReadOnlyIntegerWrapper index = new ReadOnlyIntegerWrapper();
|
||||
|
||||
protected final ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper();
|
||||
protected final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected");
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public AbstractMFXListCell(AbstractMFXListView<T, ?> listView, T data) {
|
||||
this.listView = listView;
|
||||
setData(data);
|
||||
setPrefHeight(32);
|
||||
setMaxHeight(USE_PREF_SIZE);
|
||||
setAlignment(Pos.CENTER_LEFT);
|
||||
setSpacing(5);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Abstract Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Abstract method which defines how the cell should process and show the given data.
|
||||
*/
|
||||
protected abstract void render(T data);
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
protected void initialize() {
|
||||
setBehavior();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the following behaviors:
|
||||
* <p>
|
||||
* - Binds the selected property to the list' selection model (checks for index). <p>
|
||||
* - Updates the selected PseudoClass state when selected property changes.<p>
|
||||
* - Adds and handler for MOUSE_PRESSED events to call {@link #updateSelection(MouseEvent)}.
|
||||
*/
|
||||
protected void setBehavior() {
|
||||
selected.addListener(invalidated -> pseudoClassStateChanged(SELECTED_PSEUDO_CLASS, selected.get()));
|
||||
selected.bind(Bindings.createBooleanBinding(
|
||||
() -> listView.getSelectionModel().getSelection().containsKey(index.get()),
|
||||
listView.getSelectionModel().selectionProperty(), index
|
||||
));
|
||||
|
||||
addEventFilter(MouseEvent.MOUSE_PRESSED, this::updateSelection);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the pressed mouse button is not the primary, exits immediately.
|
||||
* <p>
|
||||
* If the cell is already deselected calls {@link MultipleSelectionModel#deselectIndex(int)},
|
||||
* otherwise checks if Shift or Ctrl are pressed and updates the selection accordingly,
|
||||
* adds if they were pressed, replaces if not (acting like single selection),
|
||||
* see {@link MultipleSelectionModel#selectIndex(int)}, {@link MultipleSelectionModel#replaceSelection(Integer...)}.
|
||||
*/
|
||||
protected void updateSelection(MouseEvent event) {
|
||||
if (event.getButton() != MouseButton.PRIMARY) return;
|
||||
|
||||
int index = getIndex();
|
||||
if (event.isControlDown()) {
|
||||
if (isSelected()) {
|
||||
listView.getSelectionModel().deselectIndex(index);
|
||||
} else {
|
||||
listView.getSelectionModel().selectIndex(index);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.isShiftDown()) {
|
||||
listView.getSelectionModel().expandSelection(index);
|
||||
return;
|
||||
}
|
||||
|
||||
listView.getSelectionModel().replaceSelection(index);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Override Methods
|
||||
//================================================================================
|
||||
|
||||
@Override
|
||||
public Parent toParent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the index property of the cell.
|
||||
* <p>
|
||||
* This is called before {@link #updateItem(Object)}.
|
||||
*/
|
||||
@Override
|
||||
public void updateIndex(int index) {
|
||||
setIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the data property of the cell.
|
||||
* <p>
|
||||
* This is called after {@link #updateIndex(int)}.
|
||||
*/
|
||||
@Override
|
||||
public void updateItem(T item) {
|
||||
setData(item);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
public T getData() {
|
||||
return data.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data property of the cell.
|
||||
*/
|
||||
public ReadOnlyObjectProperty<T> dataProperty() {
|
||||
return data.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setData(T data) {
|
||||
this.data.set(data);
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the cell's index.
|
||||
*/
|
||||
public ReadOnlyIntegerProperty indexProperty() {
|
||||
return index.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setIndex(int index) {
|
||||
this.index.set(index);
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the selection state of the cell.
|
||||
*/
|
||||
public ReadOnlyBooleanProperty selectedProperty() {
|
||||
return selected.getReadOnlyProperty();
|
||||
}
|
||||
|
||||
protected void setSelected(boolean selected) {
|
||||
this.selected.set(selected);
|
||||
}
|
||||
}
|
@ -18,13 +18,14 @@
|
||||
|
||||
package io.github.palexdev.materialfx.selection;
|
||||
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import java.util.Map;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
/**
|
||||
* Extension of {@link SingleSelectionModel} to implement a few more methods for comboboxes.
|
||||
* Extension of {@link SelectionModel} to implement a few more methods for combo boxes.
|
||||
*/
|
||||
public class ComboBoxSelectionModel<T> extends SingleSelectionModel<T> {
|
||||
public class ComboBoxSelectionModel<T> extends SelectionModel<T> {
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
@ -33,10 +34,6 @@ public class ComboBoxSelectionModel<T> extends SingleSelectionModel<T> {
|
||||
super(items);
|
||||
}
|
||||
|
||||
public ComboBoxSelectionModel(ObjectProperty<ObservableList<T>> items) {
|
||||
super(items);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
@ -78,17 +75,22 @@ public class ComboBoxSelectionModel<T> extends SingleSelectionModel<T> {
|
||||
selectIndex(index);
|
||||
}
|
||||
|
||||
public int getSelectedIndex() {
|
||||
Map.Entry<Integer, T> e = getSelectedEntry();
|
||||
return (e == null) ? -1 : e.getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to get the items list size.
|
||||
*/
|
||||
private int itemsSize() {
|
||||
return items.get().size();
|
||||
return getItems().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to check if the items list is empty.
|
||||
*/
|
||||
private boolean itemsEmpty() {
|
||||
return items.get().isEmpty();
|
||||
return getItems().isEmpty();
|
||||
}
|
||||
}
|
||||
|
@ -1,329 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.selection;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.NumberRange;
|
||||
import io.github.palexdev.materialfx.selection.base.AbstractMultipleSelectionModel;
|
||||
import javafx.beans.property.MapProperty;
|
||||
import javafx.beans.property.SimpleMapProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableMap;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
// TODO introduce bindings eventually
|
||||
|
||||
/**
|
||||
* Helper class that is capable of managing/update MultipleSelectionModels.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class MultipleSelectionManager<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final AbstractMultipleSelectionModel<T> selectionModel;
|
||||
private final MapProperty<Integer, T> selection = new SimpleMapProperty<>(getMap());
|
||||
private boolean allowsMultipleSelection = true;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MultipleSelectionManager(AbstractMultipleSelectionModel<T> selectionModel) {
|
||||
this.selectionModel = selectionModel;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Clears the selection by setting it to an empty map.
|
||||
*/
|
||||
public void clearSelection() {
|
||||
selection.set(getMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given index from the selection map.
|
||||
*/
|
||||
public void deselectIndex(int index) {
|
||||
selection.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the index of the given item from the items list and if it's not -1
|
||||
* removes it from the selection map.
|
||||
*/
|
||||
public void deselectItem(T item) {
|
||||
int index = selectionModel.getItems().indexOf(item);
|
||||
if (index >= 0) {
|
||||
selection.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all the specified indexes from the selection map, done
|
||||
* by creating a tmp map, updating the tmp map and then replacing the
|
||||
* selection with this new map.
|
||||
*/
|
||||
public void deselectIndexes(int... indexes) {
|
||||
ObservableMap<Integer, T> tmp = getMap(selection);
|
||||
for (int index : indexes) {
|
||||
tmp.remove(index);
|
||||
}
|
||||
selection.set(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the items list to check if the given items exist, then retrieves
|
||||
* their index and collect them to a tmp map, then replaces the
|
||||
* selection with this new map.
|
||||
*/
|
||||
public void deselectItems(T... items) {
|
||||
Map<Integer, T> tmp = Arrays.stream(items)
|
||||
.filter(item -> selectionModel.getItems().contains(item))
|
||||
.collect(Collectors.toMap(
|
||||
item -> selectionModel.getItems().indexOf(item),
|
||||
item -> item
|
||||
));
|
||||
ObservableMap<Integer, T> newSelection = getMap(tmp);
|
||||
selection.set(newSelection);
|
||||
}
|
||||
|
||||
/**
|
||||
* If multiple selection is allowed adds the given index (and the retrieved item) to the selection map,
|
||||
* otherwise creates a new tmp map containing only the given index-item entry and replaces the selection.
|
||||
*/
|
||||
public void updateSelection(int index) {
|
||||
T item = selectionModel.getItems().get(index);
|
||||
if (allowsMultipleSelection) {
|
||||
selection.put(index, item);
|
||||
} else {
|
||||
ObservableMap<Integer, T> map = getMap();
|
||||
map.put(index, item);
|
||||
selection.set(map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If multiple selection is allowed adds the given item (and the retrieved index) to the selection map,
|
||||
* otherwise creates a new tmp map containing only the given index-item entry and replaces the selection.
|
||||
*/
|
||||
public void updateSelection(T item) {
|
||||
int index = selectionModel.getItems().indexOf(item);
|
||||
if (allowsMultipleSelection) {
|
||||
selection.put(index, item);
|
||||
} else {
|
||||
ObservableMap<Integer, T> map = getMap();
|
||||
map.put(index, item);
|
||||
selection.set(map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If multiple selection is allowed adds all the given indexes to the selection
|
||||
* (and the retrieved items), otherwise replaces the selection with the first index given in the list.
|
||||
*/
|
||||
public void updateSelectionByIndexes(List<Integer> indexes) {
|
||||
if (indexes.isEmpty()) return;
|
||||
|
||||
if (allowsMultipleSelection) {
|
||||
Set<Integer> indexesSet = new LinkedHashSet<>(indexes);
|
||||
Map<Integer, T> newSelection = indexesSet.stream().collect(Collectors.toMap(
|
||||
i -> i,
|
||||
i -> selectionModel.getItems().get(i),
|
||||
(t, t2) -> t2,
|
||||
LinkedHashMap::new
|
||||
));
|
||||
selection.putAll(newSelection);
|
||||
} else {
|
||||
int index = indexes.get(0);
|
||||
T item = selectionModel.getItems().get(index);
|
||||
ObservableMap<Integer, T> map = getMap();
|
||||
map.put(index, item);
|
||||
selection.set(map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If multiple selection is allowed adds all the given items to the selection
|
||||
* (and the retrieved indexes), otherwise replaces the selection with the first item given in the list.
|
||||
*/
|
||||
public void updateSelectionByItems(List<T> items) {
|
||||
if (items.isEmpty()) return;
|
||||
|
||||
if (allowsMultipleSelection) {
|
||||
Set<Integer> indexesSet = items.stream()
|
||||
.mapToInt(item -> selectionModel.getItems().indexOf(item))
|
||||
.boxed()
|
||||
.collect(Collectors.toSet());
|
||||
Map<Integer, T> newSelection = indexesSet.stream().collect(Collectors.toMap(
|
||||
i -> i,
|
||||
items::get
|
||||
));
|
||||
selection.putAll(newSelection);
|
||||
} else {
|
||||
T item = items.get(0);
|
||||
int index = selectionModel.getItems().indexOf(item);
|
||||
ObservableMap<Integer, T> map = getMap();
|
||||
map.put(index, item);
|
||||
selection.set(map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is responsible for expanding the selection in the given index direction.
|
||||
* There are 4 cases to consider:
|
||||
* <p> 1) The selection is empty: the new selection will go from [0 to index]
|
||||
* <p> 2) The minimum selected index is equal to the given index: the new selection will just be [index]
|
||||
* <p> 3) The given index is lesser than the minimum index: the new selection will go from [index to min]
|
||||
* <p> 4) The given index is greater than the minimum index: the new selection will go from [min to index]
|
||||
*/
|
||||
public void expandSelection(int index) {
|
||||
if (selection.isEmpty()) {
|
||||
replaceSelection(NumberRange.expandRangeToArray(0, index));
|
||||
return;
|
||||
}
|
||||
|
||||
int min = selection.keySet().stream().min(Integer::compareTo).orElse(-1);
|
||||
if (index == min) {
|
||||
replaceSelection(index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < min) {
|
||||
replaceSelection(NumberRange.expandRangeToArray(index, min));
|
||||
} else {
|
||||
replaceSelection(NumberRange.expandRangeToArray(min, index));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If multiple selection is allowed replaces the selection with all the given indexes
|
||||
* (and the retrieved items), otherwise replaces the selection with the first given index.
|
||||
*/
|
||||
public void replaceSelection(Integer... indexes) {
|
||||
ObservableMap<Integer, T> newSelection = getMap();
|
||||
if (allowsMultipleSelection) {
|
||||
newSelection.putAll(
|
||||
Arrays.stream(indexes).collect(Collectors.toMap(
|
||||
i -> i,
|
||||
i -> selectionModel.getItems().get(i)
|
||||
))
|
||||
);
|
||||
} else {
|
||||
int index = indexes[0];
|
||||
newSelection.put(index, selectionModel.getItems().get(index));
|
||||
}
|
||||
selection.set(newSelection);
|
||||
}
|
||||
|
||||
/**
|
||||
* If multiple selection is allowed replaces the selection with all the given items
|
||||
* (and the retrieved indexes), otherwise replaces the selection with the first given item.
|
||||
*/
|
||||
public void replaceSelection(T... items) {
|
||||
ObservableMap<Integer, T> newSelection = getMap();
|
||||
if (allowsMultipleSelection) {
|
||||
newSelection.putAll(
|
||||
Arrays.stream(items).collect(Collectors.toMap(
|
||||
item -> selectionModel.getItems().indexOf(item),
|
||||
item -> item
|
||||
))
|
||||
);
|
||||
} else {
|
||||
T item = items[0];
|
||||
newSelection.put(selectionModel.getItems().indexOf(item), item);
|
||||
}
|
||||
selection.set(newSelection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new observable hash map backed by a {@link LinkedHashMap}.
|
||||
*/
|
||||
protected ObservableMap<Integer, T> getMap() {
|
||||
return FXCollections.observableMap(new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new observable hash map backed by a {@link LinkedHashMap}, initialized with the given map.
|
||||
*/
|
||||
protected ObservableMap<Integer, T> getMap(Map<Integer, T> map) {
|
||||
return FXCollections.observableMap(new LinkedHashMap<>(map));
|
||||
}
|
||||
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* @return the selection {@link ObservableMap}
|
||||
*/
|
||||
public ObservableMap<Integer, T> getSelection() {
|
||||
return selection.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link MapProperty} used to keep track of multiple selection.
|
||||
* <p></p>
|
||||
* We use a {@link MapProperty} to represent multiple selection because this way
|
||||
* we can always update it "atomically", meaning that when the selected indexes changes
|
||||
* the selected items are updated as well (also true viceversa).
|
||||
*/
|
||||
public MapProperty<Integer, T> selectionProperty() {
|
||||
return selection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the selection with the given {@link ObservableMap}.
|
||||
*/
|
||||
public void setSelection(ObservableMap<Integer, T> selection) {
|
||||
this.selection.set(selection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable {@link List} containing all the selected values extracted from
|
||||
* {@link Map#values()}.
|
||||
* The values order is kept since the selection is backed by a {@link LinkedHashMap}.
|
||||
*/
|
||||
public List<T> getSelectedValues() {
|
||||
return List.copyOf(selection.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies if this model allows multiple selection or should act like
|
||||
* a SingleSelectionModel.
|
||||
*/
|
||||
public boolean allowsMultipleSelection() {
|
||||
return allowsMultipleSelection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selection behavior of this model to be multiple (true) or
|
||||
* single (false).
|
||||
* <p>
|
||||
* If it's set to false the selection is cleared.
|
||||
*/
|
||||
public void setAllowsMultipleSelection(boolean allowsMultipleSelection) {
|
||||
if (!allowsMultipleSelection) clearSelection();
|
||||
this.allowsMultipleSelection = allowsMultipleSelection;
|
||||
}
|
||||
}
|
@ -1,197 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.selection;
|
||||
|
||||
import io.github.palexdev.materialfx.selection.base.AbstractMultipleSelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.base.IMultipleSelectionModel;
|
||||
import javafx.beans.property.MapProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.ObservableMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AbstractMultipleSelectionModel} to implement the API
|
||||
* specified by {@link IMultipleSelectionModel}.
|
||||
* <p></p>
|
||||
* The logic is handled by {@link MultipleSelectionManager}, in fact all methods are just delegates.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class MultipleSelectionModel<T> extends AbstractMultipleSelectionModel<T> {
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MultipleSelectionModel(ObservableList<T> items) {
|
||||
super(items);
|
||||
}
|
||||
|
||||
public MultipleSelectionModel(ObjectProperty<ObservableList<T>> items) {
|
||||
super(items);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Override Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#clearSelection()}.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionManager.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#deselectIndex(int)}.
|
||||
*/
|
||||
@Override
|
||||
public void deselectIndex(int index) {
|
||||
selectionManager.deselectIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#deselectItem(Object)}.
|
||||
*/
|
||||
@Override
|
||||
public void deselectItem(T item) {
|
||||
selectionManager.deselectItem(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#deselectIndexes(int...)}.
|
||||
*/
|
||||
@Override
|
||||
public void deselectIndexes(int... indexes) {
|
||||
selectionManager.deselectIndexes(indexes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#deselectItems(Object[])}.
|
||||
*/
|
||||
@Override
|
||||
public void deselectItems(T... items) {
|
||||
selectionManager.deselectItems(items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#updateSelection(int)}.
|
||||
*/
|
||||
@Override
|
||||
public void selectIndex(int index) {
|
||||
selectionManager.updateSelection(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#updateSelection(Object)}.
|
||||
*/
|
||||
@Override
|
||||
public void selectItem(T item) {
|
||||
selectionManager.updateSelection(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#updateSelectionByIndexes(List)}.
|
||||
*/
|
||||
@Override
|
||||
public void selectIndexes(List<Integer> indexes) {
|
||||
selectionManager.updateSelectionByIndexes(indexes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#updateSelectionByItems(List)}.
|
||||
*/
|
||||
@Override
|
||||
public void selectItems(List<T> items) {
|
||||
selectionManager.updateSelectionByItems(items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#expandSelection(int)}.
|
||||
*/
|
||||
@Override
|
||||
public void expandSelection(int index) {
|
||||
selectionManager.expandSelection(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#replaceSelection(Integer...)}.
|
||||
*/
|
||||
@Override
|
||||
public void replaceSelection(Integer... indexes) {
|
||||
selectionManager.replaceSelection(indexes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#replaceSelection(Object[])}.
|
||||
*/
|
||||
@Override
|
||||
public void replaceSelection(T... items) {
|
||||
selectionManager.replaceSelection(items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#getSelection()}.
|
||||
*/
|
||||
@Override
|
||||
public ObservableMap<Integer, T> getSelection() {
|
||||
return selectionManager.getSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#selectionProperty()}.
|
||||
*/
|
||||
@Override
|
||||
public MapProperty<Integer, T> selectionProperty() {
|
||||
return selectionManager.selectionProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#setSelection(ObservableMap)}.
|
||||
*/
|
||||
@Override
|
||||
public void setSelection(ObservableMap<Integer, T> newSelection) {
|
||||
selectionManager.setSelection(newSelection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#getSelectedValues()}.
|
||||
*/
|
||||
@Override
|
||||
public List<T> getSelectedValues() {
|
||||
return selectionManager.getSelectedValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#allowsMultipleSelection()}.
|
||||
*/
|
||||
@Override
|
||||
public boolean allowsMultipleSelection() {
|
||||
return selectionManager.allowsMultipleSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link MultipleSelectionManager#setAllowsMultipleSelection(boolean)}.
|
||||
*/
|
||||
@Override
|
||||
public void setAllowsMultipleSelection(boolean allowsMultipleSelection) {
|
||||
selectionManager.setAllowsMultipleSelection(allowsMultipleSelection);
|
||||
}
|
||||
}
|
@ -0,0 +1,390 @@
|
||||
package io.github.palexdev.materialfx.selection;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.github.palexdev.materialfx.selection.base.ISelectionModel;
|
||||
import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
|
||||
import io.github.palexdev.mfxcore.utils.fx.ListChangeHelper;
|
||||
import io.github.palexdev.virtualizedfx.utils.Utils;
|
||||
import javafx.beans.property.ListProperty;
|
||||
import javafx.beans.property.MapProperty;
|
||||
import javafx.beans.property.SimpleListProperty;
|
||||
import javafx.beans.property.SimpleMapProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.ObservableMap;
|
||||
|
||||
import static java.util.function.Function.identity;
|
||||
|
||||
public class SelectionModel<T> implements ISelectionModel<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final ListProperty<T> items = new SimpleListProperty<>();
|
||||
private final MapProperty<Integer, T> selection = new SimpleMapProperty<>(newMap());
|
||||
private SequencedMap<Integer, T> backingMap;
|
||||
private boolean allowsMultipleSelection = true;
|
||||
private ListChangeHelper<T> lch;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public SelectionModel(ObservableList<T> items) {
|
||||
if (items instanceof ListProperty<T> lp) {
|
||||
this.items.bind(lp);
|
||||
} else {
|
||||
this.items.set(items);
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
protected void init() {
|
||||
lch = new ListChangeHelper<>(items)
|
||||
.setOnClear(selection::clear)
|
||||
.setOnPermutation(p -> replaceSelection(selection.keySet()
|
||||
.stream()
|
||||
.map(p::get)
|
||||
.toArray(Integer[]::new))
|
||||
)
|
||||
.setOnReplace(rep -> {
|
||||
if (!selection.containsKey(rep)) {
|
||||
return;
|
||||
}
|
||||
selection.put(rep, items.get(rep));
|
||||
})
|
||||
.setOnRemoved(rem -> {
|
||||
List<Integer> updated = ListChangeHelper.shiftOnRemove(selection.keySet(), rem, rem.first());
|
||||
replaceSelection(updated.toArray(Integer[]::new));
|
||||
})
|
||||
.setOnAdded(add -> {
|
||||
List<Integer> updated = ListChangeHelper.shiftOnAdd(selection.keySet(), add);
|
||||
replaceSelection(updated.toArray(Integer[]::new));
|
||||
})
|
||||
.init();
|
||||
}
|
||||
|
||||
protected ObservableMap<Integer, T> newMap() {
|
||||
this.backingMap = new LinkedHashMap<>();
|
||||
return FXCollections.observableMap(backingMap);
|
||||
}
|
||||
|
||||
protected ObservableMap<Integer, T> newMap(Map<Integer, T> map) {
|
||||
this.backingMap = new LinkedHashMap<>(map);
|
||||
return FXCollections.observableMap(backingMap);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
public boolean contains(int index) {
|
||||
return selection.containsKey(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(T element) {
|
||||
return selection.containsValue(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selection.set(newMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deselectIndex(int index) {
|
||||
selection.remove(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deselectItem(T item) {
|
||||
int index = items.indexOf(item);
|
||||
if (index != -1) {
|
||||
selection.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deselectIndexes(int... indexes) {
|
||||
ObservableMap<Integer, T> tmp = newMap(selection);
|
||||
for (int index : indexes) {
|
||||
tmp.remove(index);
|
||||
}
|
||||
selection.set(tmp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deselectIndexes(IntegerRange range) {
|
||||
ObservableMap<Integer, T> tmp = newMap(selection);
|
||||
for (Integer index : range) {
|
||||
tmp.remove(index);
|
||||
}
|
||||
selection.set(tmp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deselectItems(T... items) {
|
||||
Map<Integer, T> tmp = Arrays.stream(items)
|
||||
.mapToInt(this.items::indexOf)
|
||||
.filter(i -> i >= 0)
|
||||
.boxed()
|
||||
.collect(Collectors.toMap(
|
||||
identity(),
|
||||
this.items::get
|
||||
));
|
||||
selection.set(newMap(tmp));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectIndex(int index) {
|
||||
T item = items.get(index);
|
||||
if (allowsMultipleSelection) {
|
||||
selection.put(index, item);
|
||||
} else {
|
||||
ObservableMap<Integer, T> map = newMap();
|
||||
map.put(index, item);
|
||||
selection.set(map);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectItem(T item) {
|
||||
int index = items.indexOf(item);
|
||||
if (index < 0)
|
||||
throw new IllegalArgumentException("Item %s is not part of the dataset".formatted(item));
|
||||
if (allowsMultipleSelection) {
|
||||
selection.put(index, item);
|
||||
} else {
|
||||
ObservableMap<Integer, T> map = newMap();
|
||||
map.put(index, item);
|
||||
selection.set(map);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectIndexes(Integer... indexes) {
|
||||
if (indexes.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (allowsMultipleSelection) {
|
||||
Set<Integer> indexesSet = new LinkedHashSet<>(List.of(indexes));
|
||||
Map<Integer, T> newSelection = indexesSet.stream()
|
||||
.collect(Collectors.toMap(
|
||||
identity(),
|
||||
items::get,
|
||||
(t, t2) -> t2,
|
||||
LinkedHashMap::new
|
||||
));
|
||||
selection.putAll(newSelection);
|
||||
} else {
|
||||
int index = indexes[indexes.length - 1];
|
||||
T item = items.get(index);
|
||||
ObservableMap<Integer, T> map = newMap();
|
||||
map.put(index, item);
|
||||
selection.set(map);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectIndexes(IntegerRange range) {
|
||||
if (Utils.INVALID_RANGE.equals(range)) {
|
||||
return;
|
||||
}
|
||||
if (allowsMultipleSelection) {
|
||||
Map<Integer, T> newSelection = range.stream().collect(Collectors.toMap(
|
||||
identity(),
|
||||
items::get,
|
||||
(t1, t2) -> t2,
|
||||
LinkedHashMap::new
|
||||
));
|
||||
selection.putAll(newSelection);
|
||||
} else {
|
||||
int index = range.getMax();
|
||||
T item = items.get(index);
|
||||
ObservableMap<Integer, T> map = newMap();
|
||||
map.put(index, item);
|
||||
selection.set(map);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectItems(T... items) {
|
||||
if (items.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (allowsMultipleSelection) {
|
||||
Set<Integer> indexesSet = Arrays.stream(items)
|
||||
.mapToInt(this.items::indexOf)
|
||||
.filter(i -> i >= 0)
|
||||
.boxed()
|
||||
.collect(Collectors.toSet());
|
||||
Map<Integer, T> newSelection = indexesSet.stream()
|
||||
.collect(Collectors.toMap(
|
||||
identity(),
|
||||
i -> items[i]
|
||||
));
|
||||
selection.putAll(newSelection);
|
||||
} else {
|
||||
T item = items[items.length - 1];
|
||||
int index = this.items.indexOf(item);
|
||||
if (index < 0)
|
||||
throw new IllegalArgumentException("Item %s is not part of the dataset".formatted(item));
|
||||
|
||||
ObservableMap<Integer, T> map = newMap();
|
||||
map.put(index, item);
|
||||
selection.set(map);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expandSelection(int index, boolean fromLast) {
|
||||
if (selection.isEmpty()) {
|
||||
replaceSelection(IntegerRange.of(0, index));
|
||||
return;
|
||||
}
|
||||
|
||||
if (fromLast) {
|
||||
Map.Entry<Integer, T> last = backingMap.lastEntry();
|
||||
Integer lastIndex = last.getKey();
|
||||
int min = Math.min(lastIndex, index);
|
||||
int max = Math.max(lastIndex, index);
|
||||
selectIndexes(IntegerRange.of(min, max));
|
||||
return;
|
||||
}
|
||||
|
||||
int min = selection.keySet().stream()
|
||||
.min(Integer::compareTo)
|
||||
.orElse(-1);
|
||||
if (index == min) {
|
||||
replaceSelection(index);
|
||||
return;
|
||||
}
|
||||
|
||||
IntegerRange range = (index < min) ?
|
||||
IntegerRange.of(index, min) :
|
||||
IntegerRange.of(min, index);
|
||||
replaceSelection(range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceSelection(Integer... indexes) {
|
||||
if (indexes.length == 0) {
|
||||
selection.set(newMap());
|
||||
return;
|
||||
}
|
||||
ObservableMap<Integer, T> newSelection = newMap();
|
||||
if (allowsMultipleSelection) {
|
||||
newSelection.putAll(
|
||||
Arrays.stream(indexes)
|
||||
.collect(Collectors.toMap(
|
||||
identity(),
|
||||
items::get)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
Integer index = indexes[indexes.length - 1];
|
||||
T item = items.get(index);
|
||||
newSelection.put(index, item);
|
||||
}
|
||||
selection.set(newSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceSelection(IntegerRange range) {
|
||||
if (Utils.INVALID_RANGE.equals(range)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObservableMap<Integer, T> newSelection;
|
||||
if (allowsMultipleSelection) {
|
||||
newSelection = range.stream().collect(Collectors.toMap(
|
||||
identity(),
|
||||
items::get,
|
||||
(t1, t2) -> t2,
|
||||
this::newMap
|
||||
));
|
||||
} else {
|
||||
newSelection = newMap();
|
||||
int index = range.getMax();
|
||||
T item = items.get(index);
|
||||
newSelection.put(index, item);
|
||||
}
|
||||
selection.set(newSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceSelection(T... items) {
|
||||
ObservableMap<Integer, T> newSelection = newMap();
|
||||
if (allowsMultipleSelection) {
|
||||
newSelection.putAll(
|
||||
Arrays.stream(items)
|
||||
.mapToInt(this.items::indexOf)
|
||||
.filter(i -> i >= 0)
|
||||
.boxed()
|
||||
.collect(Collectors.toMap(
|
||||
identity(),
|
||||
this.items::get
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
T item = items[items.length - 1];
|
||||
int index = this.items.indexOf(item);
|
||||
if (index < 0)
|
||||
throw new IllegalArgumentException("Item %s is not part of the dataset".formatted(item));
|
||||
|
||||
newSelection.put(index, item);
|
||||
}
|
||||
selection.set(newSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MapProperty<Integer, T> selection() {
|
||||
return selection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getSelectedItems() {
|
||||
return List.copyOf(selection.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowsMultipleSelection() {
|
||||
return allowsMultipleSelection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllowsMultipleSelection(boolean allowsMultipleSelection) {
|
||||
// Clear selection when switching modes
|
||||
if (this.allowsMultipleSelection != allowsMultipleSelection) {
|
||||
if (!allowsMultipleSelection && size() > 1)
|
||||
selection.clear();
|
||||
}
|
||||
this.allowsMultipleSelection = allowsMultipleSelection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<Integer, T> getSelectedEntry() {
|
||||
return (size() == 0 || backingMap == null) ? null : backingMap.lastEntry();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
lch.dispose();
|
||||
lch = null;
|
||||
items.unbind();
|
||||
items.clear();
|
||||
selection.clear();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters
|
||||
//================================================================================
|
||||
public ObservableList<T> getItems() {
|
||||
return FXCollections.unmodifiableObservableList(items);
|
||||
}
|
||||
}
|
@ -1,390 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.selection;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.properties.base.SynchronizedProperty;
|
||||
import io.github.palexdev.materialfx.beans.properties.synced.SynchronizedIntegerProperty;
|
||||
import io.github.palexdev.materialfx.beans.properties.synced.SynchronizedObjectProperty;
|
||||
import io.github.palexdev.materialfx.bindings.BiBindingManager;
|
||||
import io.github.palexdev.materialfx.bindings.BindingManager;
|
||||
import io.github.palexdev.materialfx.selection.base.AbstractSingleSelectionModel;
|
||||
import io.github.palexdev.materialfx.utils.others.TriConsumer;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Helper class for {@link AbstractSingleSelectionModel} models to properly handle the selection
|
||||
* and the bindings with properties or other models.
|
||||
* <p></p>
|
||||
* Both the selectedIndex and selectedItem properties are SynchronizedProperties, see {@link SynchronizedProperty}.
|
||||
* <p>
|
||||
* So when you select an index the item will be automatically updated and only then a change event will be fired,
|
||||
* the same thing happens if you select an item.
|
||||
* <p></p>
|
||||
* Invalid values, like -1 for the index or null for the item, will throw an exception. To clear the selection
|
||||
* use {@link #clearSelection()}, a boolean flag will be set to true thus allowing setting the aforementioned values.
|
||||
*/
|
||||
public class SingleSelectionManager<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final AbstractSingleSelectionModel<T> selectionModel;
|
||||
private final SynchronizedIntegerProperty selectedIndex = new SynchronizedIntegerProperty(-1);
|
||||
private final SynchronizedObjectProperty<T> selectedItem = new SynchronizedObjectProperty<>(null);
|
||||
private boolean clearing;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public SingleSelectionManager(AbstractSingleSelectionModel<T> selectionModel) {
|
||||
this.selectionModel = selectionModel;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Sets the index to -1 and item to null by using {@link SynchronizedProperty#setAndWait(Object, ObservableValue)}.
|
||||
*
|
||||
* @throws IllegalStateException if the selection model is bound, {@link #isBound()}
|
||||
*/
|
||||
public void clearSelection() {
|
||||
if (isBound()) {
|
||||
throw new IllegalStateException("Cannot clear the selection as this selection model is bound to some other property");
|
||||
}
|
||||
|
||||
clearing = true;
|
||||
selectedIndex.setAndWait(-1, selectedItem);
|
||||
selectedItem.set(null);
|
||||
selectedIndex.awake();
|
||||
clearing = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the selection with the given index (and the retrieved item) by using
|
||||
* {@link SynchronizedProperty#setAndWait(Object, ObservableValue)}.
|
||||
*
|
||||
* @throws IllegalStateException if the selection model is bound, {@link #isBound()}
|
||||
*/
|
||||
public void updateSelection(int index) {
|
||||
if (isBound()) {
|
||||
throw new IllegalStateException("Cannot set the selected index as this selection model is bound to some other property");
|
||||
}
|
||||
|
||||
if (clearing) {
|
||||
clearSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
T item = selectionModel.getUnmodifiableItems().get(index);
|
||||
selectedIndex.setAndWait(index, selectedItem);
|
||||
selectedItem.set(item);
|
||||
if (selectedIndex.isWaiting()) selectedIndex.awake();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the selection with the given item (and the retrieved index) by using
|
||||
* {@link SynchronizedProperty#setAndWait(Object, ObservableValue)}.
|
||||
*
|
||||
* @throws IllegalStateException if the selection model is bound, {@link #isBound()}
|
||||
*/
|
||||
public void updateSelection(T item) {
|
||||
if (isBound()) {
|
||||
throw new IllegalStateException("Cannot set the selected item as this selection model is bound to some other property");
|
||||
}
|
||||
|
||||
if (clearing) {
|
||||
clearSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
int index = selectionModel.getUnmodifiableItems().indexOf(item);
|
||||
if (index == -1) {
|
||||
throw new IllegalArgumentException("The given item is not present is this selection model's list");
|
||||
}
|
||||
selectedItem.setAndWait(item, selectedIndex);
|
||||
selectedIndex.set(index);
|
||||
if (selectedItem.isWaiting()) selectedItem.awake();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* @return the current selected index
|
||||
*/
|
||||
public int getSelectedIndex() {
|
||||
return selectedIndex.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* The selected index property.
|
||||
*/
|
||||
public SynchronizedIntegerProperty selectedIndexProperty() {
|
||||
return selectedIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current selected item
|
||||
*/
|
||||
public T getSelectedItem() {
|
||||
return selectedItem.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* The selected item property.
|
||||
*/
|
||||
public SynchronizedObjectProperty<T> selectedItemProperty() {
|
||||
return selectedItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to specify that updateSelection should be ignored as {@link #clearSelection()} was invoked.
|
||||
*/
|
||||
public void setClearing(boolean clearing) {
|
||||
this.clearing = clearing;
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Bindings
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Binds the index property to given source {@link ObservableValue}.
|
||||
* The indexConverter function is used to convert the index values to an item
|
||||
* of the selection model.
|
||||
* <p></p>
|
||||
* By default creates this binding:
|
||||
* <pre>
|
||||
* {@code
|
||||
* BindingManager.instance().bind(selectedIndex)
|
||||
* .with((oldValue, newValue) -> {
|
||||
* T item = indexConverter.apply(newValue.intValue());
|
||||
* selectedIndex.setAndWait(newValue.intValue(), selectedItem);
|
||||
* selectedItem.set(item);
|
||||
* })
|
||||
* .to(source)
|
||||
* .create();
|
||||
* }
|
||||
* </pre>
|
||||
* To change it you should override the {@link SingleSelectionModel} method.
|
||||
*/
|
||||
public void bindIndex(ObservableValue<? extends Number> source, Function<Integer, T> indexConverter) {
|
||||
if (selectedIndex.isBound()) selectedIndex.unbind();
|
||||
BindingManager.instance().bind(selectedIndex)
|
||||
.with((oldValue, newValue) -> {
|
||||
T item = indexConverter.apply(newValue.intValue());
|
||||
selectedIndex.setAndWait(newValue.intValue(), selectedItem);
|
||||
selectedItem.set(item);
|
||||
})
|
||||
.to(source)
|
||||
.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the index property bidirectionally to given other {@link Property}.
|
||||
* The indexConverter function is used to convert the index from the other property
|
||||
* to an item of the selection model.
|
||||
* <p>
|
||||
* The updateOther {@link TriConsumer} is used to customize the way the other
|
||||
* property is updated, the first parameter is the clearing flag of the selection manager,
|
||||
* the second parameter is the new index, the third parameter is the other property reference.
|
||||
* <p></p>
|
||||
* By default creates this binding:
|
||||
* <pre>
|
||||
* {@code
|
||||
* BiBindingManager.instance().bindBidirectional(selectedIndex)
|
||||
* .with((oldValue, newValue) -> {
|
||||
* if (newValue.intValue() == -1) {
|
||||
* clearSelection();
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* if (newValue.intValue() == selectedIndex.getValue()) {
|
||||
* return;
|
||||
* }
|
||||
* T item = indexConverter.apply(newValue.intValue());
|
||||
* selectedIndex.setAndWait(newValue.intValue(), selectedItem);
|
||||
* selectedItem.set(item);
|
||||
* })
|
||||
* .to(other, (oldValue, newValue) -> updateOther.accept(clearing, newValue.intValue(), other))
|
||||
* .create();
|
||||
* }
|
||||
* </pre>
|
||||
* To change it you should override the {@link SingleSelectionModel} method.
|
||||
*/
|
||||
public void bindIndexBidirectional(Property<Number> other, Function<Integer, T> indexConverter, TriConsumer<Boolean, Integer, Property<Number>> updateOther) {
|
||||
if (selectedIndex.isBound()) selectedIndex.unbind();
|
||||
BiBindingManager.instance().bindBidirectional(selectedIndex)
|
||||
.with((oldValue, newValue) -> {
|
||||
if (newValue.intValue() == -1) {
|
||||
clearSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
if (newValue.intValue() == selectedIndex.getValue()) {
|
||||
return;
|
||||
}
|
||||
T item = indexConverter.apply(newValue.intValue());
|
||||
selectedIndex.setAndWait(newValue.intValue(), selectedItem);
|
||||
selectedItem.set(item);
|
||||
})
|
||||
.to(other, (oldValue, newValue) -> updateOther.accept(clearing, newValue.intValue(), other))
|
||||
.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the item property to given source {@link ObservableValue}.
|
||||
* The itemConverter function is used to convert the item values to an index
|
||||
* of the selection model.
|
||||
* <p></p>
|
||||
* By default creates this binding:
|
||||
* <pre>
|
||||
* {@code
|
||||
* BindingManager.instance().bind(selectedItem)
|
||||
* .with((oldValue, newValue) -> {
|
||||
* if (!selectionModel.getUnmodifiableItems().contains(newValue)) {
|
||||
* throw new IllegalArgumentException("The given item is not present is this selection model's list");
|
||||
* }
|
||||
* int index = itemConverter.apply(newValue);
|
||||
* selectedItem.setAndWait(newValue, selectedIndex);
|
||||
* selectedIndex.set(index);
|
||||
* })
|
||||
* .to(source)
|
||||
* .create();
|
||||
* }
|
||||
* </pre>
|
||||
* To change it you should override the {@link SingleSelectionModel} method.
|
||||
*/
|
||||
public void bindItem(ObservableValue<? extends T> source, Function<T, Integer> itemConverter) {
|
||||
if (selectedItem.isBound()) selectedItem.unbind();
|
||||
BindingManager.instance().bind(selectedItem)
|
||||
.with((oldValue, newValue) -> {
|
||||
if (!selectionModel.getUnmodifiableItems().contains(newValue)) {
|
||||
throw new IllegalArgumentException("The given item is not present is this selection model's list");
|
||||
}
|
||||
int index = itemConverter.apply(newValue);
|
||||
selectedItem.setAndWait(newValue, selectedIndex);
|
||||
selectedIndex.set(index);
|
||||
})
|
||||
.to(source)
|
||||
.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the item property bidirectionally to given other {@link Property}.
|
||||
* The itemConverter function is used to convert the item from the other property
|
||||
* to an index of the selection model.
|
||||
* <p>
|
||||
* The updateOther {@link TriConsumer} is used to customize the way the other
|
||||
* property is updated, the first parameter is the clearing flag of the selection manager,
|
||||
* the second parameter is the new item, the third parameter is the other property reference.
|
||||
* <p></p>
|
||||
* By default creates this binding:
|
||||
* <pre>
|
||||
* {@code
|
||||
* BiBindingManager.instance().bindBidirectional(selectedItem)
|
||||
* .with((oldValue, newValue) -> {
|
||||
* if (newValue == null) {
|
||||
* clearSelection();
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* if (!selectionModel.getUnmodifiableItems().contains(newValue)) {
|
||||
* throw new IllegalArgumentException("The given item is not present is this selection model's list");
|
||||
* }
|
||||
*
|
||||
* int index = itemConverter.apply(newValue);
|
||||
* selectedItem.setAndWait(newValue, selectedIndex);
|
||||
* selectedIndex.set(index);
|
||||
* })
|
||||
* .to(other, (oldValue, newValue) -> updateOther.accept(clearing, newValue, other))
|
||||
* .create();
|
||||
* }
|
||||
* </pre>
|
||||
* To change it you should override the {@link SingleSelectionModel} method.
|
||||
*/
|
||||
public void bindItemBidirectional(Property<T> other, Function<T, Integer> itemConverter, TriConsumer<Boolean, T, Property<T>> updateOther) {
|
||||
if (selectedItem.isBound()) selectedItem.unbind();
|
||||
BiBindingManager.instance().bindBidirectional(selectedItem)
|
||||
.with((oldValue, newValue) -> {
|
||||
if (newValue == null) {
|
||||
clearSelection();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectionModel.getUnmodifiableItems().contains(newValue)) {
|
||||
throw new IllegalArgumentException("The given item is not present is this selection model's list");
|
||||
}
|
||||
|
||||
int index = itemConverter.apply(newValue);
|
||||
selectedItem.setAndWait(newValue, selectedIndex);
|
||||
selectedIndex.set(index);
|
||||
})
|
||||
.to(other, (oldValue, newValue) -> updateOther.accept(clearing, newValue, other))
|
||||
.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbinds the selection.
|
||||
*/
|
||||
public void unbind() {
|
||||
if (selectedIndex.isBound()) {
|
||||
selectedIndex.unbind();
|
||||
}
|
||||
if (selectedItem.isBound()) {
|
||||
selectedItem.unbind();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the bidirectional binding between the index property and the given other property.
|
||||
*/
|
||||
public void unbindIndexBidirectional(Property<Number> other) {
|
||||
selectedIndex.unbindBidirectional(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the bidirectional binding between the item property and the given other property.
|
||||
*/
|
||||
public void unbindItemBidirectional(Property<T> other) {
|
||||
selectedItem.unbindBidirectional(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all bidirectional bindings.
|
||||
*/
|
||||
public void unbindBidirectional() {
|
||||
selectedIndex.clearBidirectional();
|
||||
selectedItem.clearBidirectional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selected index or item properties are bound
|
||||
* unidirectionally.
|
||||
*/
|
||||
public boolean isBound() {
|
||||
return selectedIndex.isBound() || selectedItem.isBound();
|
||||
}
|
||||
}
|
||||
|
@ -1,268 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.selection;
|
||||
|
||||
import io.github.palexdev.materialfx.selection.base.AbstractSingleSelectionModel;
|
||||
import io.github.palexdev.materialfx.selection.base.ISingleSelectionModel;
|
||||
import io.github.palexdev.materialfx.utils.others.TriConsumer;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AbstractSingleSelectionModel} to implement the API
|
||||
* specified by {@link ISingleSelectionModel}.
|
||||
* <p></p>
|
||||
* The logic is handled by {@link SingleSelectionManager}, in fact all methods are just delegates.
|
||||
*/
|
||||
public class SingleSelectionModel<T> extends AbstractSingleSelectionModel<T> {
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public SingleSelectionModel(ObservableList<T> items) {
|
||||
super(items);
|
||||
}
|
||||
|
||||
public SingleSelectionModel(ObjectProperty<ObservableList<T>> items) {
|
||||
super(items);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Delegate Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#clearSelection()}.
|
||||
*/
|
||||
@Override
|
||||
public void clearSelection() {
|
||||
selectionManager.clearSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#updateSelection(int)}.
|
||||
*/
|
||||
@Override
|
||||
public void selectIndex(int index) {
|
||||
selectionManager.updateSelection(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#updateSelection(Object)}.
|
||||
*/
|
||||
@Override
|
||||
public void selectItem(T item) {
|
||||
selectionManager.updateSelection(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#getSelectedIndex()}}.
|
||||
*/
|
||||
@Override
|
||||
public int getSelectedIndex() {
|
||||
return selectionManager.getSelectedIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#selectedIndexProperty()}, but
|
||||
* a read-only property is returned.
|
||||
*/
|
||||
@Override
|
||||
public ReadOnlyIntegerProperty selectedIndexProperty() {
|
||||
return selectionManager.selectedIndexProperty().getReadOnlyProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#getSelectedItem()}.
|
||||
*/
|
||||
@Override
|
||||
public T getSelectedItem() {
|
||||
return selectionManager.getSelectedItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#selectedItemProperty()}, but
|
||||
* a read-only property is returned.
|
||||
*/
|
||||
@Override
|
||||
public ReadOnlyObjectProperty<T> selectedItemProperty() {
|
||||
return selectionManager.selectedItemProperty().getReadOnlyProperty();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Bindings
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Binds this selection model's index to the given selection model's index,
|
||||
* calls {@link SingleSelectionManager#bindIndex(ObservableValue, Function)}.
|
||||
* <p></p>
|
||||
* Default implementation:
|
||||
* <pre>
|
||||
* {@code
|
||||
* selectionManager.bindIndex(selectionModel.selectionManager.selectedIndexProperty(), getItems()::get);
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public void bindIndex(SingleSelectionModel<T> selectionModel) {
|
||||
selectionManager.bindIndex(selectionModel.selectionManager.selectedIndexProperty(), getItems()::get);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds this selection model's index bidirectionally to the given selection model's index,
|
||||
* calls {@link SingleSelectionManager#bindIndexBidirectional(Property, Function, TriConsumer)}.
|
||||
* <p></p>
|
||||
* Default implementation:
|
||||
* <pre>
|
||||
* {@code
|
||||
* selectionManager.bindIndexBidirectional(
|
||||
* selectionModel.selectionManager.selectedIndexProperty(),
|
||||
* getItems()::get,
|
||||
* (clearing, i, other) -> {
|
||||
* selectionModel.selectionManager.setClearing(clearing);
|
||||
* selectionModel.selectionManager.updateSelection(i);
|
||||
* }
|
||||
* );
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public void bindIndexBidirectional(SingleSelectionModel<T> selectionModel) {
|
||||
selectionManager.bindIndexBidirectional(
|
||||
selectionModel.selectionManager.selectedIndexProperty(),
|
||||
getItems()::get,
|
||||
(clearing, i, other) -> {
|
||||
selectionModel.selectionManager.setClearing(clearing);
|
||||
selectionModel.selectionManager.updateSelection(i);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds this selection model's item to the given selection model's item,
|
||||
* calls {@link SingleSelectionManager#bindItem(ObservableValue, Function)}.
|
||||
* <p></p>
|
||||
* Default implementation:
|
||||
* <pre>
|
||||
* {@code
|
||||
* selectionManager.bindItem(selectionModel.selectionManager.selectedItemProperty(), getItems()::indexOf);
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public void bindItem(SingleSelectionModel<T> selectionModel) {
|
||||
selectionManager.bindItem(selectionModel.selectionManager.selectedItemProperty(), getItems()::indexOf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds this selection model's item bidirectionally to the given selection model's item,
|
||||
* calls {@link SingleSelectionManager#bindItemBidirectional(Property, Function, TriConsumer)}.
|
||||
* <p></p>
|
||||
* Default implementation:
|
||||
* <pre>
|
||||
* {@code
|
||||
* selectionManager.bindItemBidirectional(
|
||||
* selectionModel.selectionManager.selectedItemProperty(),
|
||||
* getItems()::indexOf,
|
||||
* (clearing, item, other) -> {
|
||||
* selectionModel.selectionManager.setClearing(clearing);
|
||||
* selectionModel.selectionManager.updateSelection(item);
|
||||
* }
|
||||
* );
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public void bindItemBidirectional(SingleSelectionModel<T> selectionModel) {
|
||||
selectionManager.bindItemBidirectional(
|
||||
selectionModel.selectionManager.selectedItemProperty(),
|
||||
getItems()::indexOf,
|
||||
(clearing, item, other) -> {
|
||||
selectionModel.selectionManager.setClearing(clearing);
|
||||
selectionModel.selectionManager.updateSelection(item);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#bindIndex(ObservableValue, Function)}.
|
||||
*/
|
||||
public void bindIndex(ObservableValue<? extends Number> source, Function<Integer, T> indexConverter) {
|
||||
selectionManager.bindIndex(source, indexConverter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#bindIndexBidirectional(Property, Function, TriConsumer)}.
|
||||
*/
|
||||
public void bindIndexBidirectional(Property<Number> other, Function<Integer, T> indexConverter, TriConsumer<Boolean, Integer, Property<Number>> updateOther) {
|
||||
selectionManager.bindIndexBidirectional(other, indexConverter, updateOther);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#bindItem(ObservableValue, Function)}.
|
||||
*/
|
||||
public void bindItem(ObservableValue<? extends T> source, Function<T, Integer> itemConverter) {
|
||||
selectionManager.bindItem(source, itemConverter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#bindItemBidirectional(Property, Function, TriConsumer)}.
|
||||
*/
|
||||
public void bindItemBidirectional(Property<T> other, Function<T, Integer> itemConverter, TriConsumer<Boolean, T, Property<T>> updateOther) {
|
||||
selectionManager.bindItemBidirectional(other, itemConverter, updateOther);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#unbind()}.
|
||||
*/
|
||||
public void unbind() {
|
||||
selectionManager.unbind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#unbindIndexBidirectional(Property)}.
|
||||
*/
|
||||
public void unbindIndexBidirectional(Property<Number> other) {
|
||||
selectionManager.unbindIndexBidirectional(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#unbindItemBidirectional(Property)}.
|
||||
*/
|
||||
public void unbindItemBidirectional(Property<T> other) {
|
||||
selectionManager.unbindItemBidirectional(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#unbindBidirectional()}.
|
||||
*/
|
||||
public void unbindBidirectional() {
|
||||
selectionManager.unbindBidirectional();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate method for {@link SingleSelectionManager#isBound()}.
|
||||
*/
|
||||
public boolean isBound() {
|
||||
return selectionManager.isBound();
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.selection.base;
|
||||
|
||||
import io.github.palexdev.materialfx.selection.MultipleSelectionManager;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
/**
|
||||
* Abstract base class for all MultipleSelectionModels.
|
||||
* <p>
|
||||
* This class holds a property for the items list. Controls that uses this selection model are
|
||||
* responsible for changes in the source list, so if anything changes there be sure to keep the
|
||||
* selection model in a consistent state.
|
||||
* Also holds a reference for {@link MultipleSelectionManager}, the class that is effectively
|
||||
* responsible for updating/managing the selection model' state.
|
||||
*/
|
||||
public abstract class AbstractMultipleSelectionModel<T> implements IMultipleSelectionModel<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
protected final ObjectProperty<ObservableList<T>> items = new SimpleObjectProperty<>();
|
||||
protected final MultipleSelectionManager<T> selectionManager = new MultipleSelectionManager<>(this);
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
protected AbstractMultipleSelectionModel(ObservableList<T> items) {
|
||||
this.items.set(items);
|
||||
}
|
||||
|
||||
protected AbstractMultipleSelectionModel(ObservableValue<? extends ObservableList<T>> items) {
|
||||
this.items.bind(items);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* @return an unmodifiable copy of the items list
|
||||
*/
|
||||
public ObservableList<T> getItems() {
|
||||
return FXCollections.unmodifiableObservableList(items.get());
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.selection.base;
|
||||
|
||||
import io.github.palexdev.materialfx.selection.SingleSelectionManager;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
/**
|
||||
* Abstract base class for all SingleSelectionModels.
|
||||
* <p>
|
||||
* This class holds a property for the items list. Controls that uses this selection model are
|
||||
* responsible for changes in the source list, so if anything changes there be sure to keep the
|
||||
* selection model in a consistent state.
|
||||
* Also holds a reference for {@link SingleSelectionManager}, the class that is effectively
|
||||
* responsible for updating/managing the selection model' state.
|
||||
*/
|
||||
public abstract class AbstractSingleSelectionModel<T> implements ISingleSelectionModel<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
protected final ObjectProperty<ObservableList<T>> items = new SimpleObjectProperty<>();
|
||||
protected final SingleSelectionManager<T> selectionManager = new SingleSelectionManager<>(this);
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
protected AbstractSingleSelectionModel(ObservableList<T> items) {
|
||||
this.items.set(items);
|
||||
}
|
||||
|
||||
protected AbstractSingleSelectionModel(ObservableValue<? extends ObservableList<T>> items) {
|
||||
this.items.bind(items);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Getters/Setters
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* @return an unmodifiable copy of the items list
|
||||
*/
|
||||
public ObservableList<T> getUnmodifiableItems() {
|
||||
return FXCollections.unmodifiableObservableList(items.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the items list
|
||||
*/
|
||||
final protected ObservableList<T> getItems() {
|
||||
return items.get();
|
||||
}
|
||||
}
|
@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.selection.base;
|
||||
|
||||
import javafx.beans.property.MapProperty;
|
||||
import javafx.collections.ObservableMap;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Public API that every MultipleSelectionModel must implement.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public interface IMultipleSelectionModel<T> {
|
||||
|
||||
/**
|
||||
* Clears the selection.
|
||||
*/
|
||||
void clearSelection();
|
||||
|
||||
/**
|
||||
* Deselects the given index.
|
||||
*/
|
||||
void deselectIndex(int index);
|
||||
|
||||
/**
|
||||
* Deselects the given item.
|
||||
*/
|
||||
void deselectItem(T item);
|
||||
|
||||
/**
|
||||
* Deselects the given indexes.
|
||||
*/
|
||||
void deselectIndexes(int... indexes);
|
||||
|
||||
/**
|
||||
* Deselects the given items.
|
||||
*/
|
||||
void deselectItems(T... items);
|
||||
|
||||
/**
|
||||
* Selects the given index.
|
||||
*/
|
||||
void selectIndex(int index);
|
||||
|
||||
/**
|
||||
* Selects the given item.
|
||||
*/
|
||||
void selectItem(T item);
|
||||
|
||||
/**
|
||||
* Selects the given indexes list.
|
||||
*/
|
||||
void selectIndexes(List<Integer> indexes);
|
||||
|
||||
/**
|
||||
* Selects the given items list.
|
||||
*/
|
||||
void selectItems(List<T> items);
|
||||
|
||||
/**
|
||||
* Expands the selection in the given index direction.
|
||||
*/
|
||||
void expandSelection(int index);
|
||||
|
||||
/**
|
||||
* Clears the selection and replaces it with the given indexes.
|
||||
*/
|
||||
void replaceSelection(Integer... indexes);
|
||||
|
||||
/**
|
||||
* Clears the selection and replaces it with the given items.
|
||||
*/
|
||||
void replaceSelection(T... items);
|
||||
|
||||
/**
|
||||
* @return the selection {@link ObservableMap}
|
||||
*/
|
||||
ObservableMap<Integer, T> getSelection();
|
||||
|
||||
/**
|
||||
* The {@link MapProperty} used to keep track of multiple selection.
|
||||
* <p></p>
|
||||
* We use a {@link MapProperty} to represent multiple selection because this way
|
||||
* we can always update it "atomically", meaning that when the selected indexes changes
|
||||
* the selected items are updated as well (also true viceversa).
|
||||
*/
|
||||
MapProperty<Integer, T> selectionProperty();
|
||||
|
||||
/**
|
||||
* Replaces the selection with the given {@link ObservableMap}.
|
||||
*/
|
||||
void setSelection(ObservableMap<Integer, T> newSelection);
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable {@link List} containing all the selected values extracted from
|
||||
* {@link Map#values()}.
|
||||
* The values order is kept since the selection is backed by a {@link LinkedHashMap}.
|
||||
*/
|
||||
List<T> getSelectedValues();
|
||||
|
||||
/**
|
||||
* @return the first selected item or null if selection is empty
|
||||
*/
|
||||
default T getSelectedValue() {
|
||||
return getSelectedValues().isEmpty() ? null : getSelectedValues().get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the last selected item or null if selection is empty
|
||||
*/
|
||||
default T getLastSelectedValue() {
|
||||
return getSelectedValues().isEmpty() ? null : getSelectedValues().get(getSelectedValues().size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies if this model allows multiple selection or should act like
|
||||
* a SingleSelectionModel.
|
||||
*/
|
||||
boolean allowsMultipleSelection();
|
||||
|
||||
/**
|
||||
* Sets the selection behavior of this model to be multiple (true) or
|
||||
* single (false).
|
||||
*/
|
||||
void setAllowsMultipleSelection(boolean allowsMultipleSelection);
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package io.github.palexdev.materialfx.selection.base;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
|
||||
import javafx.beans.property.MapProperty;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public interface ISelectionModel<T> {
|
||||
boolean contains(int index);
|
||||
|
||||
boolean contains(T element);
|
||||
|
||||
void clearSelection();
|
||||
|
||||
void deselectIndex(int index);
|
||||
|
||||
void deselectItem(T item);
|
||||
|
||||
void deselectIndexes(int... indexes);
|
||||
|
||||
void deselectIndexes(IntegerRange range);
|
||||
|
||||
void deselectItems(T... items);
|
||||
|
||||
void selectIndex(int index);
|
||||
|
||||
void selectItem(T item);
|
||||
|
||||
void selectIndexes(Integer... indexes);
|
||||
|
||||
void selectIndexes(IntegerRange range);
|
||||
|
||||
void selectItems(T... items);
|
||||
|
||||
void expandSelection(int index, boolean fromLast);
|
||||
|
||||
void replaceSelection(Integer... indexes);
|
||||
|
||||
void replaceSelection(IntegerRange range);
|
||||
|
||||
void replaceSelection(T... items);
|
||||
|
||||
MapProperty<Integer, T> selection();
|
||||
|
||||
List<T> getSelectedItems();
|
||||
|
||||
default int size() {
|
||||
return selection().size();
|
||||
}
|
||||
|
||||
default boolean isEmpty() {
|
||||
return selection().isEmpty();
|
||||
}
|
||||
|
||||
default T getSelectedItem() {
|
||||
return (size() == 0) ? null : getSelectedItems().getFirst();
|
||||
}
|
||||
|
||||
default Optional<T> getSelectedItemOpt() {
|
||||
return Optional.ofNullable(getSelectedItem());
|
||||
}
|
||||
|
||||
default T getLastSelectedItem() {
|
||||
int size = size();
|
||||
return (size == 0) ? null : getSelectedItems().get(size - 1);
|
||||
}
|
||||
|
||||
default Optional<T> getLastSelectedItemOpt() {
|
||||
return Optional.ofNullable(getLastSelectedItem());
|
||||
}
|
||||
|
||||
default Map.Entry<Integer, T> getSelectedEntry() {
|
||||
return (size() == 0) ? null : selection().entrySet().iterator().next();
|
||||
}
|
||||
|
||||
default Optional<Map.Entry<Integer, T>> getSelectedEntryOpt() {
|
||||
return Optional.ofNullable(getSelectedEntry());
|
||||
}
|
||||
|
||||
boolean allowsMultipleSelection();
|
||||
|
||||
void setAllowsMultipleSelection(boolean allowsMultipleSelection);
|
||||
|
||||
void dispose();
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.selection.base;
|
||||
|
||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
|
||||
/**
|
||||
* Public API that every SingleSelectionModel must implement.
|
||||
*/
|
||||
public interface ISingleSelectionModel<T> {
|
||||
|
||||
/**
|
||||
* Clears the selection.
|
||||
*/
|
||||
void clearSelection();
|
||||
|
||||
/**
|
||||
* Selects the given index.
|
||||
*/
|
||||
void selectIndex(int index);
|
||||
|
||||
/**
|
||||
* Selects the given item.
|
||||
*/
|
||||
void selectItem(T item);
|
||||
|
||||
/**
|
||||
* @return the current selected index
|
||||
*/
|
||||
int getSelectedIndex();
|
||||
|
||||
/**
|
||||
* The selected index property as a read-only property.
|
||||
* Selection should always be updated with the dedicated methods.
|
||||
*/
|
||||
ReadOnlyIntegerProperty selectedIndexProperty();
|
||||
|
||||
/**
|
||||
* @return the current selected item
|
||||
*/
|
||||
T getSelectedItem();
|
||||
|
||||
/**
|
||||
* The selected item property as a read-only property.
|
||||
* Selection should always be updated with the dedicated methods.
|
||||
*/
|
||||
ReadOnlyObjectProperty<T> selectedItemProperty();
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package io.github.palexdev.materialfx.selection.base;
|
||||
|
||||
public interface WithSelectionModel<T> {
|
||||
ISelectionModel<T> getSelectionModel();
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package io.github.palexdev.materialfx.skins;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXCheckbox;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXCheckListCell;
|
||||
import io.github.palexdev.mfxcore.base.beans.Position;
|
||||
import io.github.palexdev.mfxcore.events.WhenEvent;
|
||||
import io.github.palexdev.mfxcore.utils.fx.LayoutUtils;
|
||||
import io.github.palexdev.virtualizedfx.cells.CellBaseBehavior;
|
||||
import io.github.palexdev.virtualizedfx.cells.VFXCellBase;
|
||||
import io.github.palexdev.virtualizedfx.events.VFXContainerEvent;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.scene.control.ContentDisplay;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
|
||||
public class MFXCheckListCellSkin<T> extends MFXListCellSkin<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final MFXCheckbox checkbox;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXCheckListCellSkin(MFXCheckListCell<T> cell) {
|
||||
super(cell);
|
||||
|
||||
checkbox = new MFXCheckbox() {
|
||||
@Override
|
||||
public void fire() {
|
||||
}
|
||||
};
|
||||
checkbox.setContentDisposition(ContentDisplay.GRAPHIC_ONLY);
|
||||
checkbox.selectedProperty().bind(cell.selectedProperty());
|
||||
getChildren().add(checkbox);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected MFXCheckListCell<T> getCell() {
|
||||
return (MFXCheckListCell<T>) super.getCell();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initBehavior(CellBaseBehavior<T> behavior) {
|
||||
VFXCellBase<T> cell = getSkinnable();
|
||||
behavior.init();
|
||||
events(
|
||||
WhenEvent.intercept(cell, VFXContainerEvent.UPDATE)
|
||||
.process(e -> {
|
||||
update();
|
||||
e.consume();
|
||||
}),
|
||||
WhenEvent.intercept(checkbox, MouseEvent.MOUSE_CLICKED)
|
||||
.process(e -> {
|
||||
behavior.mouseClicked(null);
|
||||
e.consume();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layoutChildren(double x, double y, double w, double h) {
|
||||
MFXCheckListCell<T> cell = getCell();
|
||||
|
||||
// Checkbox
|
||||
checkbox.autosize();
|
||||
Position cPos = LayoutUtils.computePosition(
|
||||
cell, checkbox,
|
||||
x, y, w, h, 0, Insets.EMPTY,
|
||||
HPos.LEFT, VPos.CENTER
|
||||
);
|
||||
checkbox.relocate(cPos.getX(), cPos.getY());
|
||||
|
||||
// Label
|
||||
double gap = cell.getHGap();
|
||||
layoutInArea(
|
||||
label,
|
||||
x + gap + checkbox.getWidth(), y, w, h, 0,
|
||||
HPos.LEFT, VPos.CENTER
|
||||
);
|
||||
|
||||
// Ripple
|
||||
rg.resizeRelocate(0, 0, cell.getWidth(), cell.getHeight());
|
||||
}
|
||||
}
|
@ -21,8 +21,9 @@ package io.github.palexdev.materialfx.skins;
|
||||
import io.github.palexdev.materialfx.controls.*;
|
||||
import io.github.palexdev.materialfx.selection.ComboBoxSelectionModel;
|
||||
import io.github.palexdev.materialfx.utils.AnimationUtils;
|
||||
import io.github.palexdev.virtualizedfx.cell.Cell;
|
||||
import io.github.palexdev.virtualizedfx.unused.simple.SimpleVirtualFlow;
|
||||
import io.github.palexdev.virtualizedfx.cells.base.VFXCell;
|
||||
import io.github.palexdev.virtualizedfx.list.VFXList;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
@ -55,7 +56,7 @@ public class MFXComboBoxSkin<T> extends MFXTextFieldSkin {
|
||||
private EventHandler<MouseEvent> popupManager;
|
||||
|
||||
protected final BooleanProperty vfInitialized = new SimpleBooleanProperty(false);
|
||||
protected SimpleVirtualFlow<T, Cell<T>> virtualFlow;
|
||||
protected VFXList<T, VFXCell<T>> vfxList;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
@ -125,8 +126,7 @@ public class MFXComboBoxSkin<T> extends MFXTextFieldSkin {
|
||||
private void selectionBehavior() {
|
||||
MFXComboBox<T> comboBox = getComboBox();
|
||||
ComboBoxSelectionModel<T> selectionModel = comboBox.getSelectionModel();
|
||||
|
||||
selectionModel.selectedIndexProperty().addListener((observable, oldValue, newValue) -> {
|
||||
selectionModel.selection().addListener((InvalidationListener) i -> {
|
||||
if (!comboBox.valueProperty().isBound()) {
|
||||
comboBox.setValue(selectionModel.getSelectedItem());
|
||||
}
|
||||
@ -155,8 +155,7 @@ public class MFXComboBoxSkin<T> extends MFXTextFieldSkin {
|
||||
popup.showingProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue) {
|
||||
comboBox.hide();
|
||||
if (trailingIcon instanceof MFXIconWrapper) {
|
||||
MFXIconWrapper icon = (MFXIconWrapper) trailingIcon;
|
||||
if (trailingIcon instanceof MFXIconWrapper icon) {
|
||||
icon.getRippleGenerator().generateRipple(null);
|
||||
}
|
||||
animateIcon(comboBox.getTrailingIcon(), false);
|
||||
@ -187,7 +186,7 @@ public class MFXComboBoxSkin<T> extends MFXTextFieldSkin {
|
||||
.setOnFinished(end -> {
|
||||
if (comboBox.isScrollOnOpen()) {
|
||||
int selectedIndex = comboBox.getSelectedIndex();
|
||||
if (selectedIndex >= 0) virtualFlow.scrollTo(selectedIndex);
|
||||
if (selectedIndex >= 0) vfxList.scrollToIndex(selectedIndex);
|
||||
}
|
||||
})
|
||||
.getAnimation()
|
||||
@ -245,26 +244,26 @@ public class MFXComboBoxSkin<T> extends MFXTextFieldSkin {
|
||||
*/
|
||||
protected Node createPopupContent() {
|
||||
MFXComboBox<T> comboBox = getComboBox();
|
||||
if (virtualFlow == null) {
|
||||
virtualFlow = new SimpleVirtualFlow<>(
|
||||
if (vfxList == null) {
|
||||
vfxList = new VFXList<>(
|
||||
comboBox.itemsProperty(),
|
||||
comboBox.getCellFactory(),
|
||||
Orientation.VERTICAL
|
||||
);
|
||||
virtualFlow.cellFactoryProperty().bind(comboBox.cellFactoryProperty());
|
||||
virtualFlow.prefWidthProperty().bind(comboBox.widthProperty());
|
||||
vfxList.getCellFactory().bind(comboBox.cellFactoryProperty());
|
||||
vfxList.prefWidthProperty().bind(comboBox.widthProperty());
|
||||
|
||||
Runnable createBinding = () ->
|
||||
virtualFlow.prefHeightProperty().bind(Bindings.createDoubleBinding(
|
||||
() -> Math.min(comboBox.getRowsCount(), comboBox.getItems().size()) * virtualFlow.getCellHeight(),
|
||||
comboBox.rowsCountProperty(), comboBox.getItems(), virtualFlow.cellFactoryProperty(), vfInitialized
|
||||
vfxList.prefHeightProperty().bind(Bindings.createDoubleBinding(
|
||||
() -> Math.min(comboBox.getRowsCount(), comboBox.getItems().size()) * vfxList.getCellSize(),
|
||||
comboBox.rowsCountProperty(), comboBox.getItems(), vfxList.cellSizeProperty(), vfInitialized
|
||||
));
|
||||
virtualFlow.itemsProperty().addListener((observable, oldValue, newValue) -> {
|
||||
vfxList.itemsProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue != null) createBinding.run();
|
||||
});
|
||||
createBinding.run();
|
||||
}
|
||||
return virtualFlow;
|
||||
return vfxList.makeScrollable();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -283,7 +282,7 @@ public class MFXComboBoxSkin<T> extends MFXTextFieldSkin {
|
||||
protected void layoutChildren(double x, double y, double w, double h) {
|
||||
super.layoutChildren(x, y, w, h);
|
||||
|
||||
if (virtualFlow != null && !vfInitialized.get() && virtualFlow.getCellHeight() != 0)
|
||||
if (vfxList != null && !vfInitialized.get() && vfxList.getCellSize() != 0)
|
||||
vfInitialized.set(true);
|
||||
}
|
||||
|
||||
@ -295,6 +294,6 @@ public class MFXComboBoxSkin<T> extends MFXTextFieldSkin {
|
||||
comboBox.getTrailingIcon().removeEventHandler(MouseEvent.MOUSE_PRESSED, popupManager);
|
||||
}
|
||||
popupManager = null;
|
||||
virtualFlow = null;
|
||||
vfxList = null;
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,18 @@
|
||||
|
||||
package io.github.palexdev.materialfx.skins;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.YearMonth;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import io.github.palexdev.materialfx.beans.NumberRange;
|
||||
import io.github.palexdev.materialfx.controls.*;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXDateCell;
|
||||
@ -40,18 +52,6 @@ import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.YearMonth;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* Skin associated with every {@link MFXDatePicker} by default.
|
||||
* <p>
|
||||
@ -239,8 +239,7 @@ public class MFXDatePickerSkin extends MFXTextFieldSkin {
|
||||
popup.showingProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue) {
|
||||
datePicker.hide();
|
||||
if (trailingIcon instanceof MFXIconWrapper) {
|
||||
MFXIconWrapper icon = (MFXIconWrapper) trailingIcon;
|
||||
if (trailingIcon instanceof MFXIconWrapper icon) {
|
||||
icon.getRippleGenerator().generateRipple(null);
|
||||
}
|
||||
}
|
||||
@ -325,7 +324,7 @@ public class MFXDatePickerSkin extends MFXTextFieldSkin {
|
||||
});
|
||||
leftArrow.disableProperty().bind(Bindings.createBooleanBinding(
|
||||
() -> Objects.equals(yearCombo.getSelectedItem(), datePicker.getYearsRange().getMin()) && currentYearMonth.getMonth() == Month.JANUARY,
|
||||
datePicker.yearsRangeProperty(), yearCombo.selectedItemProperty(), monthCombo.selectedItemProperty()
|
||||
datePicker.yearsRangeProperty(), yearCombo.selection(), monthCombo.selection()
|
||||
));
|
||||
|
||||
rightArrow.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
|
||||
@ -336,7 +335,7 @@ public class MFXDatePickerSkin extends MFXTextFieldSkin {
|
||||
});
|
||||
rightArrow.disableProperty().bind(Bindings.createBooleanBinding(
|
||||
() -> Objects.equals(yearCombo.getSelectedItem(), datePicker.getYearsRange().getMax()) && currentYearMonth.getMonth() == Month.DECEMBER,
|
||||
datePicker.yearsRangeProperty(), yearCombo.selectedItemProperty(), monthCombo.selectedItemProperty()
|
||||
datePicker.yearsRangeProperty(), yearCombo.selection(), monthCombo.selection()
|
||||
));
|
||||
|
||||
NodeUtils.makeRegionCircular(leftArrow);
|
||||
@ -402,8 +401,8 @@ public class MFXDatePickerSkin extends MFXTextFieldSkin {
|
||||
* Responsible for updating the days grid, also builds the cells cache or
|
||||
* updates it if a reset was not needed.
|
||||
* <p></p>
|
||||
* This is also responsible for marking/un-marking some cells as "extra" cells, {@link MFXDateCell#markAsExtra()},
|
||||
* {@link MFXDateCell#unmarkAsExtra()}. Extra cells are those cells that contains days belonging to the previous/next month.
|
||||
* This is also responsible for marking/un-marking some cells as "extra" cells, {@link MFXDateCell#setExtra(boolean)}.
|
||||
* Extra cells are those cells that contains days belonging to the previous/next month.
|
||||
*/
|
||||
private void updateGrid() {
|
||||
MFXDatePicker datePicker = getDatePicker();
|
||||
@ -433,7 +432,7 @@ public class MFXDatePickerSkin extends MFXTextFieldSkin {
|
||||
cell = getCell(i, j, null);
|
||||
cell.updateItem(null);
|
||||
cells[i][j] = cell;
|
||||
children.add(cell.toParent());
|
||||
children.add(cell.toNode());
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -441,23 +440,23 @@ public class MFXDatePickerSkin extends MFXTextFieldSkin {
|
||||
YearMonth previous = currentYearMonth.plusMonths(-1);
|
||||
date = LocalDate.of(previous.getYear(), previous.getMonth(), day);
|
||||
cell = getCell(i, j, date);
|
||||
cell.markAsExtra();
|
||||
cell.setExtra(true);
|
||||
} else if (index > endIndex) {
|
||||
YearMonth next = currentYearMonth.plusMonths(1);
|
||||
date = LocalDate.of(next.getYear(), next.getMonth(), day);
|
||||
cell = getCell(i, j, date);
|
||||
cell.markAsExtra();
|
||||
cell.setExtra(true);
|
||||
} else {
|
||||
date = LocalDate.of(currentYearMonth.getYear(), currentYearMonth.getMonth(), day);
|
||||
cell = getCell(i, j, date);
|
||||
cell.unmarkAsExtra();
|
||||
cell.setExtra(false);
|
||||
}
|
||||
|
||||
if (!cellsInitialized) {
|
||||
cells[i][j] = cell;
|
||||
}
|
||||
cell.updateItem(date);
|
||||
children.add(cell.toParent());
|
||||
children.add(cell.toNode());
|
||||
}
|
||||
|
||||
if (!cellsInitialized) {
|
||||
|
@ -18,12 +18,15 @@
|
||||
|
||||
package io.github.palexdev.materialfx.skins;
|
||||
|
||||
import io.github.palexdev.materialfx.collections.TransformableList;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import io.github.palexdev.materialfx.collections.RefineList;
|
||||
import io.github.palexdev.materialfx.controls.BoundTextField;
|
||||
import io.github.palexdev.materialfx.controls.MFXFilterComboBox;
|
||||
import io.github.palexdev.materialfx.controls.MFXTextField;
|
||||
import io.github.palexdev.materialfx.i18n.I18N;
|
||||
import io.github.palexdev.virtualizedfx.unused.simple.SimpleVirtualFlow;
|
||||
import io.github.palexdev.mfxlocalization.I18N;
|
||||
import io.github.palexdev.virtualizedfx.list.VFXList;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.geometry.Orientation;
|
||||
import javafx.geometry.Pos;
|
||||
@ -31,9 +34,6 @@ import javafx.scene.Node;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Skin associated with every {@link MFXFilterComboBox} by default.
|
||||
* <p>
|
||||
@ -101,37 +101,37 @@ public class MFXFilterComboBoxSkin<T> extends MFXComboBoxSkin<T> {
|
||||
@Override
|
||||
protected Node createPopupContent() {
|
||||
MFXFilterComboBox<T> comboBox = getComboBox();
|
||||
TransformableList<T> filterList = comboBox.getFilterList();
|
||||
RefineList<T> list = comboBox.getFilterList();
|
||||
|
||||
MFXTextField searchField = new MFXTextField("", I18N.getOrDefault("filterCombo.search"));
|
||||
searchField.getStyleClass().add("search-field");
|
||||
searchField.textProperty().bindBidirectional(comboBox.searchTextProperty());
|
||||
searchField.setMaxWidth(Double.MAX_VALUE);
|
||||
|
||||
virtualFlow = new SimpleVirtualFlow<>(
|
||||
filterList,
|
||||
comboBox.getCellFactory(),
|
||||
Orientation.VERTICAL
|
||||
vfxList = new VFXList<>(
|
||||
list.getView(),
|
||||
comboBox.getCellFactory(),
|
||||
Orientation.VERTICAL
|
||||
);
|
||||
virtualFlow.cellFactoryProperty().bind(comboBox.cellFactoryProperty());
|
||||
virtualFlow.prefWidthProperty().bind(comboBox.widthProperty());
|
||||
virtualFlow.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
|
||||
vfxList.getCellFactory().bind(comboBox.cellFactoryProperty());
|
||||
vfxList.prefWidthProperty().bind(comboBox.widthProperty());
|
||||
vfxList.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
|
||||
if (popup.isShowing()) {
|
||||
popup.hide();
|
||||
}
|
||||
});
|
||||
|
||||
Runnable createBinding = () ->
|
||||
virtualFlow.minHeightProperty().bind(Bindings.createDoubleBinding(
|
||||
() -> Math.min(comboBox.getRowsCount(), comboBox.getItems().size()) * virtualFlow.getCellHeight(),
|
||||
comboBox.rowsCountProperty(), comboBox.getItems(), virtualFlow.cellFactoryProperty(), vfInitialized
|
||||
));
|
||||
virtualFlow.itemsProperty().addListener((observable, oldValue, newValue) -> {
|
||||
vfxList.minHeightProperty().bind(Bindings.createDoubleBinding(
|
||||
() -> Math.min(comboBox.getRowsCount(), comboBox.getItems().size()) * vfxList.getCellSize(),
|
||||
comboBox.rowsCountProperty(), comboBox.getItems(), vfxList.cellSizeProperty(), vfInitialized
|
||||
));
|
||||
vfxList.itemsProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue != null) createBinding.run();
|
||||
});
|
||||
createBinding.run();
|
||||
|
||||
VBox container = new VBox(10, searchField, virtualFlow);
|
||||
VBox container = new VBox(10, searchField, vfxList.makeScrollable());
|
||||
container.getStyleClass().add("search-container");
|
||||
container.setAlignment(Pos.TOP_CENTER);
|
||||
return container;
|
||||
|
@ -0,0 +1,71 @@
|
||||
package io.github.palexdev.materialfx.skins;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXListCell;
|
||||
import io.github.palexdev.mfxcore.events.WhenEvent;
|
||||
import io.github.palexdev.mfxeffects.ripple.MFXRippleGenerator;
|
||||
import io.github.palexdev.virtualizedfx.cells.CellBaseBehavior;
|
||||
import io.github.palexdev.virtualizedfx.cells.VFXLabeledCellSkin;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
public class MFXListCellSkin<T> extends VFXLabeledCellSkin<T> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
protected final MFXRippleGenerator rg;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXListCellSkin(MFXListCell<T> cell) {
|
||||
super(cell);
|
||||
|
||||
rg = new MFXRippleGenerator(cell);
|
||||
getChildren().addFirst(rg);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
protected MFXListCell<T> getCell() {
|
||||
return ((MFXListCell<T>) getSkinnable());
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
|
||||
@Override
|
||||
protected void initBehavior(CellBaseBehavior<T> behavior) {
|
||||
super.initBehavior(behavior);
|
||||
MFXListCell<T> cell = getCell();
|
||||
events(
|
||||
WhenEvent.intercept(cell, MouseEvent.MOUSE_PRESSED)
|
||||
.process(rg::generate),
|
||||
WhenEvent.intercept(cell, MouseEvent.MOUSE_CLICKED)
|
||||
.process(e -> behavior.mouseClicked(e, c -> rg.release()))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void update() {
|
||||
MFXListCell<T> cell = getCell();
|
||||
T item = cell.getItem();
|
||||
StringConverter<T> converter = cell.getConverter();
|
||||
label.setText(converter.toString(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layoutChildren(double x, double y, double w, double h) {
|
||||
super.layoutChildren(x, y, w, h);
|
||||
|
||||
MFXListCell<T> cell = getCell();
|
||||
rg.resizeRelocate(0, 0, cell.getWidth(), cell.getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
rg.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Parisi Alessandro
|
||||
* This file is part of MaterialFX (https://github.com/palexdev/MaterialFX).
|
||||
*
|
||||
* MaterialFX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MaterialFX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MaterialFX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.palexdev.materialfx.skins;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.base.AbstractMFXListView;
|
||||
import io.github.palexdev.materialfx.effects.MFXDepthManager;
|
||||
import io.github.palexdev.materialfx.factories.MFXAnimationFactory;
|
||||
import io.github.palexdev.materialfx.utils.AnimationUtils;
|
||||
import io.github.palexdev.virtualizedfx.unused.simple.SimpleVirtualFlow;
|
||||
import javafx.animation.Animation;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.scene.control.ScrollBar;
|
||||
import javafx.scene.control.SkinBase;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.util.Duration;
|
||||
|
||||
/**
|
||||
* Implementation of the {@code Skin} used by all list views based on VirtualizedFX.
|
||||
*/
|
||||
public class MFXListViewSkin<T> extends SkinBase<AbstractMFXListView<T, ?>> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final ScrollBar hBar;
|
||||
private final ScrollBar vBar;
|
||||
private Animation hideBars;
|
||||
private Animation showBars;
|
||||
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXListViewSkin(AbstractMFXListView<T, ?> listView, SimpleVirtualFlow<T, ?> virtualFlow) {
|
||||
super(listView);
|
||||
hBar = virtualFlow.getHBar();
|
||||
vBar = virtualFlow.getVBar();
|
||||
|
||||
hideBars = AnimationUtils.TimelineBuilder.build()
|
||||
.add(
|
||||
AnimationUtils.KeyFrames.of(Duration.millis(400),
|
||||
new KeyValue(vBar.opacityProperty(), 0.0, MFXAnimationFactory.INTERPOLATOR_V1),
|
||||
new KeyValue(hBar.opacityProperty(), 0.0, MFXAnimationFactory.INTERPOLATOR_V1))
|
||||
)
|
||||
.getAnimation();
|
||||
showBars = AnimationUtils.TimelineBuilder.build()
|
||||
.add(
|
||||
AnimationUtils.KeyFrames.of(Duration.millis(400),
|
||||
new KeyValue(vBar.opacityProperty(), 1.0, MFXAnimationFactory.INTERPOLATOR_V1),
|
||||
new KeyValue(hBar.opacityProperty(), 1.0, MFXAnimationFactory.INTERPOLATOR_V1))
|
||||
)
|
||||
.getAnimation();
|
||||
|
||||
if (listView.isHideScrollBars()) {
|
||||
vBar.setOpacity(0.0);
|
||||
hBar.setOpacity(0.0);
|
||||
}
|
||||
listView.setEffect(MFXDepthManager.shadowOf(listView.getDepthLevel()));
|
||||
|
||||
getChildren().setAll(virtualFlow);
|
||||
setListeners();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
|
||||
/**
|
||||
* Calls {@link #setScrollBarHandlers()}, adds a listener to the list view's depth property.
|
||||
*/
|
||||
private void setListeners() {
|
||||
AbstractMFXListView<T, ?> listView = getSkinnable();
|
||||
setScrollBarHandlers();
|
||||
listView.depthLevelProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue.equals(oldValue)) {
|
||||
listView.setEffect(MFXDepthManager.shadowOf(listView.getDepthLevel()));
|
||||
}
|
||||
});
|
||||
|
||||
listView.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> listView.requestFocus());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the scroll bars behavior.
|
||||
*/
|
||||
private void setScrollBarHandlers() {
|
||||
AbstractMFXListView<T, ?> listView = getSkinnable();
|
||||
|
||||
listView.setOnMouseExited(event -> {
|
||||
if (listView.isHideScrollBars()) {
|
||||
hideBars.setDelay(listView.getHideAfter());
|
||||
|
||||
if (hBar.isPressed()) {
|
||||
hBar.pressedProperty().addListener(new ChangeListener<>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||
if (!newValue) {
|
||||
hideBars.play();
|
||||
}
|
||||
hBar.pressedProperty().removeListener(this);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (vBar.isPressed()) {
|
||||
vBar.pressedProperty().addListener(new ChangeListener<>() {
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||
if (!newValue) {
|
||||
hideBars.play();
|
||||
}
|
||||
vBar.pressedProperty().removeListener(this);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
hideBars.play();
|
||||
}
|
||||
});
|
||||
|
||||
listView.setOnMouseEntered(event -> {
|
||||
if (hideBars.getStatus().equals(Animation.Status.RUNNING)) {
|
||||
hideBars.stop();
|
||||
}
|
||||
showBars.play();
|
||||
});
|
||||
|
||||
listView.hideScrollBarsProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
hideBars.play();
|
||||
} else {
|
||||
showBars.play();
|
||||
}
|
||||
if (newValue &&
|
||||
hideBars.getStatus() != Animation.Status.RUNNING ||
|
||||
vBar.getOpacity() != 0 ||
|
||||
hBar.getOpacity() != 0
|
||||
) {
|
||||
vBar.setOpacity(0.0);
|
||||
hBar.setOpacity(0.0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Override Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
|
||||
return topInset + 350 + bottomInset;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
|
||||
return leftInset + 200 + rightInset;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
|
||||
return getSkinnable().prefHeight(width);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
|
||||
return getSkinnable().prefWidth(height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
if (hideBars != null) {
|
||||
hideBars = null;
|
||||
}
|
||||
if (showBars != null) {
|
||||
showBars = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
package io.github.palexdev.materialfx.skins;
|
||||
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXCheckbox;
|
||||
import io.github.palexdev.materialfx.controls.MFXNotificationCenter;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXNotificationCell;
|
||||
import io.github.palexdev.materialfx.effects.Interpolators;
|
||||
import io.github.palexdev.materialfx.notifications.base.INotification;
|
||||
import io.github.palexdev.materialfx.utils.AnimationUtils;
|
||||
import io.github.palexdev.mfxcore.base.beans.Position;
|
||||
import io.github.palexdev.mfxcore.controls.SkinBase;
|
||||
import io.github.palexdev.mfxcore.events.WhenEvent;
|
||||
import io.github.palexdev.mfxcore.observables.When;
|
||||
import io.github.palexdev.mfxcore.utils.fx.LayoutUtils;
|
||||
import io.github.palexdev.virtualizedfx.cells.CellBaseBehavior;
|
||||
import io.github.palexdev.virtualizedfx.cells.VFXCellBase;
|
||||
import io.github.palexdev.virtualizedfx.events.VFXContainerEvent;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
|
||||
import static javafx.scene.layout.Region.USE_PREF_SIZE;
|
||||
|
||||
public class MFXNotificationCellSkin extends SkinBase<VFXCellBase<INotification>, CellBaseBehavior<INotification>> {
|
||||
//================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final MFXCheckbox checkbox;
|
||||
private final StackPane container;
|
||||
|
||||
public MFXNotificationCellSkin(MFXNotificationCell cell) {
|
||||
super(cell);
|
||||
|
||||
checkbox = new MFXCheckbox("");
|
||||
checkbox.setId("check");
|
||||
|
||||
container = new StackPane(checkbox);
|
||||
container.setMinWidth(USE_PREF_SIZE);
|
||||
container.setPrefWidth(0);
|
||||
container.setMaxWidth(USE_PREF_SIZE);
|
||||
|
||||
Rectangle clip = new Rectangle();
|
||||
clip.widthProperty().bind(container.widthProperty());
|
||||
clip.heightProperty().bind(container.heightProperty());
|
||||
container.setClip(clip);
|
||||
|
||||
addListeners();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
protected void addListeners() {
|
||||
MFXNotificationCell cell = getCell();
|
||||
listeners(
|
||||
When.onInvalidated(cell.itemProperty())
|
||||
.then(t -> update()),
|
||||
When.onInvalidated(cell.getNotificationCenter().selectionModeProperty())
|
||||
.then(this::expand)
|
||||
.executeNow()
|
||||
);
|
||||
}
|
||||
|
||||
protected void update() {
|
||||
MFXNotificationCell cell = getCell();
|
||||
MFXNotificationCenter notificationCenter = cell.getNotificationCenter();
|
||||
INotification notification = cell.getItem();
|
||||
if (notificationCenter.isSelectionMode()) {
|
||||
checkbox.setOpacity(1.0);
|
||||
checkbox.setPrefWidth(45);
|
||||
}
|
||||
getChildren().setAll(container, notification.getContent());
|
||||
}
|
||||
|
||||
protected void expand(boolean selectionMode) {
|
||||
MFXNotificationCell cell = getCell();
|
||||
MFXNotificationCenter notificationCenter = cell.getNotificationCenter();
|
||||
double width = selectionMode ? 45 : 0;
|
||||
double opacity = selectionMode ? 1 : 0;
|
||||
if (notificationCenter.isAnimated()) {
|
||||
AnimationUtils.ParallelBuilder.build()
|
||||
.add(
|
||||
AnimationUtils.KeyFrames.of(150, checkbox.opacityProperty(), opacity, Interpolators.EASE_OUT),
|
||||
AnimationUtils.KeyFrames.of(250, container.prefWidthProperty(), width, Interpolators.EASE_OUT_SINE)
|
||||
).getAnimation().play();
|
||||
} else {
|
||||
container.setPrefWidth(width);
|
||||
checkbox.setOpacity(opacity);
|
||||
}
|
||||
if (!selectionMode) {
|
||||
notificationCenter.getSelectionModel().clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
protected MFXNotificationCell getCell() {
|
||||
return (MFXNotificationCell) getSkinnable();
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected void initBehavior(CellBaseBehavior<INotification> behavior) {
|
||||
MFXNotificationCell cell = getCell();
|
||||
behavior.init();
|
||||
events(
|
||||
WhenEvent.intercept(cell, VFXContainerEvent.UPDATE)
|
||||
.process(e -> {
|
||||
update();
|
||||
e.consume();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void layoutChildren(double x, double y, double w, double h) {
|
||||
MFXNotificationCell cell = getCell();
|
||||
|
||||
// Checkbox
|
||||
checkbox.autosize();
|
||||
Position cPos = LayoutUtils.computePosition(
|
||||
cell, checkbox,
|
||||
x, y, w, h, 0, Insets.EMPTY,
|
||||
HPos.LEFT, VPos.CENTER
|
||||
);
|
||||
checkbox.relocate(cPos.getX(), cPos.getY());
|
||||
|
||||
// Content
|
||||
INotification notification = cell.getItem();
|
||||
if (notification == null) return;
|
||||
|
||||
Region content = notification.getContent();
|
||||
double gap = cell.getHGap();
|
||||
layoutInArea(
|
||||
content,
|
||||
x + gap + checkbox.getLayoutX(), y, w, h, 0,
|
||||
HPos.LEFT, VPos.CENTER
|
||||
);
|
||||
|
||||
}
|
||||
}
|
@ -28,7 +28,6 @@ import io.github.palexdev.materialfx.factories.InsetsFactory;
|
||||
import io.github.palexdev.materialfx.i18n.I18N;
|
||||
import io.github.palexdev.materialfx.notifications.base.INotification;
|
||||
import io.github.palexdev.materialfx.utils.NodeUtils;
|
||||
import io.github.palexdev.virtualizedfx.unused.simple.SimpleVirtualFlow;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.css.Styleable;
|
||||
import javafx.geometry.Bounds;
|
||||
@ -60,7 +59,7 @@ public class MFXNotificationCenterSkin extends SkinBase<MFXNotificationCenter> {
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXNotificationCenterSkin(MFXNotificationCenter notificationCenter, SimpleVirtualFlow<INotification, MFXNotificationCell> virtualFlow) {
|
||||
public MFXNotificationCenterSkin(MFXNotificationCenter notificationCenter, MFXListView<INotification, MFXNotificationCell> listView) {
|
||||
super(notificationCenter);
|
||||
|
||||
bellWrapped = new MFXIconWrapper("fas-bell", 36, 56);
|
||||
@ -113,7 +112,7 @@ public class MFXNotificationCenterSkin extends SkinBase<MFXNotificationCenter> {
|
||||
|
||||
BorderPane borderPane = new BorderPane();
|
||||
borderPane.setTop(header);
|
||||
borderPane.setCenter(virtualFlow);
|
||||
borderPane.setCenter(listView);
|
||||
borderPane.setBottom(actions);
|
||||
borderPane.getStyleClass().add("notifications-container");
|
||||
|
||||
@ -121,7 +120,7 @@ public class MFXNotificationCenterSkin extends SkinBase<MFXNotificationCenter> {
|
||||
borderPane.setMaxHeight(Region.USE_PREF_SIZE);
|
||||
borderPane.prefWidthProperty().bind(notificationCenter.popupWidthProperty());
|
||||
borderPane.prefHeightProperty().bind(notificationCenter.popupHeightProperty());
|
||||
BorderPane.setMargin(virtualFlow, InsetsFactory.all(5));
|
||||
BorderPane.setMargin(listView, InsetsFactory.all(5));
|
||||
|
||||
popup = new MFXPopup(borderPane) {
|
||||
@Override
|
||||
|
@ -0,0 +1,78 @@
|
||||
package io.github.palexdev.materialfx.skins;
|
||||
|
||||
|
||||
import io.github.palexdev.materialfx.beans.Alignment;
|
||||
import io.github.palexdev.materialfx.controls.MFXListView;
|
||||
import io.github.palexdev.materialfx.controls.MFXPagination;
|
||||
import io.github.palexdev.materialfx.controls.MFXPopup;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXListCell;
|
||||
import io.github.palexdev.materialfx.controls.cell.MFXPage;
|
||||
import io.github.palexdev.mfxcore.base.beans.range.IntegerRange;
|
||||
import io.github.palexdev.mfxcore.events.WhenEvent;
|
||||
import io.github.palexdev.virtualizedfx.cells.CellBaseBehavior;
|
||||
import io.github.palexdev.virtualizedfx.events.VFXContainerEvent;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
|
||||
public class MFXPageSkin extends MFXListCellSkin<Integer> {
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXPageSkin(MFXPage cell) {
|
||||
super(cell);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Methods
|
||||
//================================================================================
|
||||
protected void showPopup() {
|
||||
MFXPage cell = getCell();
|
||||
MFXPagination pagination = cell.getPagination();
|
||||
IntegerRange between = cell.getBetween();
|
||||
if (!pagination.isShowPopupForTruncatedPages() || between == null) return;
|
||||
|
||||
ObservableList<Integer> indexes = FXCollections.observableArrayList(IntegerRange.expandRangeToArray(between));
|
||||
MFXListView<Integer, MFXListCell<Integer>> listView = new MFXListView<>(indexes, null);
|
||||
|
||||
MFXPopup popup = new MFXPopup(listView);
|
||||
popup.getStyleClass().add("pages-popup");
|
||||
popup.setPopupStyleableParent(pagination);
|
||||
|
||||
listView.setCellFactory(i -> {
|
||||
MFXListCell<Integer> c = new MFXListCell<>(i);
|
||||
c.setOnMouseClicked(e -> {
|
||||
pagination.setCurrentPage(c.getItem());
|
||||
popup.hide();
|
||||
});
|
||||
return c;
|
||||
});
|
||||
|
||||
popup.show(cell, Alignment.of(HPos.CENTER, VPos.BOTTOM), 0, 5);
|
||||
}
|
||||
|
||||
//================================================================================
|
||||
// Overridden Methods
|
||||
//================================================================================
|
||||
@Override
|
||||
protected void initBehavior(CellBaseBehavior<Integer> behavior) {
|
||||
MFXPage cell = getCell();
|
||||
behavior.init();
|
||||
events(
|
||||
WhenEvent.intercept(cell, VFXContainerEvent.UPDATE)
|
||||
.process(e -> {
|
||||
update();
|
||||
e.consume();
|
||||
}),
|
||||
WhenEvent.intercept(cell, MouseEvent.MOUSE_CLICKED)
|
||||
.process(e -> behavior.mouseClicked(e, c -> showPopup()))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MFXPage getCell() {
|
||||
return (MFXPage) super.getCell();
|
||||
}
|
||||
}
|
@ -20,14 +20,8 @@ package io.github.palexdev.materialfx.skins;
|
||||
|
||||
import io.github.palexdev.materialfx.controls.MFXPaginatedTableView;
|
||||
import io.github.palexdev.materialfx.controls.MFXPagination;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableRow;
|
||||
import io.github.palexdev.materialfx.utils.AnimationUtils.PauseBuilder;
|
||||
import io.github.palexdev.virtualizedfx.unused.simple.SimpleVirtualFlow;
|
||||
import io.github.palexdev.materialfx.controls.MFXTableView;
|
||||
import javafx.animation.PauseTransition;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
/**
|
||||
* This is the default skin implementation for {@link MFXPaginatedTableView}.
|
||||
@ -44,7 +38,12 @@ import javafx.scene.layout.StackPane;
|
||||
* is thrown.
|
||||
*/
|
||||
public class MFXPaginatedTableViewSkin<T> extends MFXTableViewSkin<T> {
|
||||
//================================================================================
|
||||
public MFXPaginatedTableViewSkin(MFXTableView<T> tableView) {
|
||||
super(tableView);
|
||||
}
|
||||
|
||||
|
||||
/* //================================================================================
|
||||
// Properties
|
||||
//================================================================================
|
||||
private final MFXPagination pagination;
|
||||
@ -53,7 +52,7 @@ public class MFXPaginatedTableViewSkin<T> extends MFXTableViewSkin<T> {
|
||||
//================================================================================
|
||||
// Constructors
|
||||
//================================================================================
|
||||
public MFXPaginatedTableViewSkin(MFXPaginatedTableView<T> tableView, SimpleVirtualFlow<T, MFXTableRow<T>> rowsFlow) {
|
||||
public MFXPaginatedTableViewSkin(MFXPaginatedTableView<T> tableView, VFXList<T, MFXTableRow<T>> rowsFlow) {
|
||||
super(tableView, rowsFlow);
|
||||
|
||||
rowsFlow.setMinHeight(Region.USE_PREF_SIZE);
|
||||
@ -112,5 +111,5 @@ public class MFXPaginatedTableViewSkin<T> extends MFXTableViewSkin<T> {
|
||||
protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
|
||||
double footerWidth = leftInset + footer.prefWidth(-1) + pagination.prefWidth(-1) * 2 + 10 + rightInset;
|
||||
return Math.max(footerWidth, super.computeMinWidth(height, topInset, rightInset, bottomInset, leftInset));
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user