Custom JavaFX bindings made easy with lambdas.

Related tags

GUI EasyBind
Overview

EasyBind

EasyBind leverages lambdas to reduce boilerplate when creating custom bindings, provides a type-safe alternative to Bindings.select* methods (inspired by Anton Nashatyrev's feature request, planned for JavaFX 9) and adds monadic operations to ObservableValue.

Static methods

map

Creates a binding whose value is a mapping of some observable value.

ObservableStringValue str = ...;
Binding<Integer> strLen = EasyBind.map(str, String::length);

Compare to plain JavaFX:

ObservableStringValue str = ...;
IntegerBinding strLen = Bindings.createIntegerBinding(() -> str.get().length(), str);

The difference is subtle, but important: In the latter version, str is repeated twice — once in the function to compute binding's value and once as binding's dependency. This opens the possibility that a wrong dependency is specified by mistake.

combine

Creates a binding whose value is a combination of two or more (currently up to six) observable values.

ObservableStringValue str = ...;
ObservableValue<Integer> start = ...;
ObservableValue<Integer> end = ...;
Binding<String> subStr = EasyBind.combine(str, start, end, String::substring);

Compare to plain JavaFX:

ObservableStringValue str = ...;
ObservableIntegerValue start = ...;
ObservableIntegerValue end = ...;
StringBinding subStr = Bindings.createStringBinding(() -> str.get().substring(start.get(), end.get()), str, start, end);

Same difference as before — in the latter version, str, start and end are repeated twice, once in the function to compute binding's value and once as binding's dependencies, which opens the possibility of specifying wrong set of dependencies. Plus, the latter is getting less readable.

select

Type-safe alternative to Bindings.select* methods. The following example is borrowed from RT-35923.

Binding<Boolean> bb = EasyBind.select(control.sceneProperty()) 
        .select(s -> s.windowProperty()) 
        .selectObject(w -> w.showingProperty());

Compare to plain JavaFX:

BooleanBinding bb = Bindings.selectBoolean(control.sceneProperty(), "window", "isShowing");

The latter version is not type-safe, which means it may cause runtime errors.

map list

Returns a mapped view of an ObservableList.

ObservableList<String> tabIds = EasyBind.map(tabPane.getTabs(), Tab::getId);

In the above example, tabIds is updated as tabs are added and removed from tabPane.

An equivalent feature has been requested in JDK-8091967 and is scheduled for a future JavaFX release.

combine list

Turns an observable list of observable values into a single observable value. The resulting observable value is updated when elements are added or removed to or from the list, as well as when element values change.

Property<Integer> a = new SimpleObjectProperty<>(5);
Property<Integer> b = new SimpleObjectProperty<>(10);
ObservableList<Property<Integer>> list = FXCollections.observableArrayList();

Binding<Integer> sum = EasyBind.combine(
        list,
        stream -> stream.reduce((a, b) -> a + b).orElse(0));

assert sum.getValue() == 0;

// sum responds to element additions
list.add(a);
list.add(b);
assert sum.getValue() == 15;

// sum responds to element value changes
a.setValue(20);
assert sum.getValue() == 30;

// sum responds to element removals
list.remove(a);
assert sum.getValue() == 10;

You don't usually have an observable list of observable values, but you often have an observable list of something that contains an observable value. In that case, use the above map methods to get an observable list of observable values, as in the example below.

Example: Disable "Save All" button on no unsaved changes

Assume a tab pane that contains a text editor in every tab. The set of open tabs (i.e. open files) is changing. Let's further assume we use a custom Tab subclass EditorTab that has a boolean savedProperty() that indicates whether changes in its editor have been saved.

Task: Keep the "Save All" button disabled when there are no unsaved changes in any of the editors.

ObservableList<ObservableValue<Boolean>> individualTabsSaved =
        EasyBind.map(tabPane.getTabs(), t -> ((EditorTab) t).savedProperty());

ObservableValue<Boolean> allTabsSaved = EasyBind.combine(
        individualTabsSaved,
        stream -> stream.allMatch(saved -> saved));

Button saveAllButton = new Button(...);
saveAllButton.disableProperty().bind(allTabsSaved);

bind list

Occasionally one needs to synchronize the contents of an (observable) list with another observable list. If that is your case, listBind is your friend:

ObservableList<T> sourceList = ...;
List<T> targetList = ...;
EasyBind.listBind(targetList, sourceList);

subscribe to values

Often one wants to execute some code for each value of an ObservableValue, that is for the current value and each new value. This typically results in code like this:

this.doSomething(observable.getValue());
observable.addListener((obs, oldValue, newValue) -> this.doSomething(newValue));

This can be expressed more concisely using the subscribe helper method:

EasyBind.subscribe(observable, this::doSomething);

conditional collection membership

EasyBind.includeWhen includes or excludes an element in/from a collection based on a boolean condition.

Say that you want to draw a graph and highlight an edge when the edge itself or either of its end vertices is hovered over. To achieve this, let's add .highlight CSS class to the edge node when either of the three is hovered over and remove it when none of them is hovered over:

BooleanBinding highlight = edge.hoverProperty()
        .or(v1.hoverProperty())
        .or(v2.hoverProperty());
EasyBind.includeWhen(edge.getStyleClass(), "highlight", highlight);
.highlight { -fx-stroke: green; }

Monadic observable values

MonadicObservableValue interface adds monadic operations to ObservableValue.

interface MonadicObservableValue<T> extends ObservableValue<T> {
    boolean isPresent();
    boolean isEmpty();
    void ifPresent(Consumer<? super T> f);
    T getOrThrow();
    T getOrElse(T other);
    Optional<T> getOpt();
    MonadicBinding<T> orElse(T other);
    MonadicBinding<T> orElse(ObservableValue<T> other);
    MonadicBinding<T> filter(Predicate<? super T> p);
    <U> MonadicBinding<U> map(Function<? super T, ? extends U> f);
    <U> MonadicBinding<U> flatMap(Function<? super T, ObservableValue<U>> f);
    <U> PropertyBinding<U> selectProperty(Function<? super T, Property<U>> f);
}

Read more about monadic operations in this blog post.

The last two methods, flatMap and selectProperty, let you select a nested ObservableValue or Property, respectively. The nested property can be bound, just like a normal property. Example:

DoubleProperty changingOpacity = ...;
Property<Number> currentTabContentOpacity = EasyBind.monadic(tabPane.selectionModelProperty())
        .flatMap(SelectionModel::selectedItemProperty)
        .flatMap(Tab::contentProperty)
        .selectProperty(Node::opacityProperty);
currentTabContentOpacity.bind(changingOpacity);

In this example, when you switch tabs, the old tab's content opacity is unbound and the new tab's content opacity is bound to changingOpacity.

Use EasyBind in your project

Stable release

Current stable release is 1.0.3.

Maven coordinates

Group ID Artifact ID Version
org.fxmisc.easybind easybind 1.0.3

Gradle example

dependencies {
    compile group: 'org.fxmisc.easybind', name: 'easybind', version: '1.0.3'
}

Sbt example

libraryDependencies += "org.fxmisc.easybind" % "easybind" % "1.0.3"

Manual download

Download the JAR file and place it on your classpath.

Snapshot releases

Snapshot releases are deployed to Sonatype snapshot repository.

Maven coordinates

Group ID Artifact ID Version
org.fxmisc.easybind easybind 1.0.4-SNAPSHOT

Gradle example

repositories {
    maven {
        url 'https://oss.sonatype.org/content/repositories/snapshots/' 
    }
}

dependencies {
    compile group: 'org.fxmisc.easybind', name: 'easybind', version: '1.0.4-SNAPSHOT'
}

Sbt example

resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"

libraryDependencies += "org.fxmisc.easybind" % "easybind" % "1.0.4-SNAPSHOT"

Manual download

Download the latest JAR file and place it on your classpath.

Links

Javadoc

Comments
  • Mapping an ObserveableMap

    Mapping an ObserveableMap

    I have an ObserveableMap<K, V>, from which I would like to create an ObserveableList<K> or ObserveableList<V>

    Am I correct that this is not currently possible with EasyBind API? Would be great to have this if it isn't currently supported.

    thanks for the cool library! I am using it in this app. :+1:

    opened by hrj 3
  • Incompatibility with `javafx.beans.binding.Bindings` boolean utilities

    Incompatibility with `javafx.beans.binding.Bindings` boolean utilities

    EasyBind returns objects of generic types. JavaFX utilities like Bindings.or or Bindings.not are incompatible with ObservableValue<Boolean>, because their arguments are of type ObservableBooleanValue. I'm not sure what's the solution and/or workaround.

    opened by cubuspl42 2
  • EasyBind.FlattenList(ObservableList<ObservableList<? extends E>>)

    EasyBind.FlattenList(ObservableList>)

    This creates a binding to concatenate ObservableList values. This supersedes PR #8. I fixed the bugs that were present in property firing and added some extra tests to verify these components.

    opened by Nava2 2
  • Strange Behaviour with EasyBind.map

    Strange Behaviour with EasyBind.map

    Hello

    Thanks a lot for sharing this code. It's been realy helpful in lot of situations!

    I am experiencing some strange behaviour with 'EasyBind.map'. My goal is to derive a list of conditions (Strings) from a collection of buisiness objects, that will be passed to the application server on a search request.

    Basically, what I am doing is this:

    ObservableList<MyDataObject> selectedItems = checkComboBox.getCheckModel().getCheckedItems();
    ObservableList<String> conditions = EasyBind.map(selectedItems, p -> p.getCondition());
    sendRequestToServer(conditions);
    

    What I am seeing is that 'conditions' is always emtpy because 'conditions' will not be updated although the 'selectedItems' change.

    The following code has a dependency on ControlsFX (8.20.7). If you run it like this and check the first entry in the combo box, only the 'ListChangeListener' on 'selectedItems' will fire, but not the one on the 'MappedList'.

    Now please uncomment the two lines 74 and 75:

    ListView<String> selectedView = new ListView<String>(lastNames);
    box.getChildren().add(selectedView);
    

    and run the program again. Now, if an item is checked, the selection will be displayed in the new list and both 'ListChangeListener' instances are called!

    import javafx.application.Application;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.collections.FXCollections;
    import javafx.collections.ListChangeListener;
    import javafx.collections.ObservableList;
    import javafx.geometry.Insets;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    import javafx.util.StringConverter;
    
    import org.controlsfx.control.CheckComboBox;
    import org.fxmisc.easybind.EasyBind;
    
    public class MapTest extends Application {
        public static void main(String[] args) { 
            launch(args); 
        } 
    
        private ObservableList<MapTestPerson> people = FXCollections.observableArrayList();
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            initPeople();
            primaryStage.setScene(new Scene(createContent(primaryStage), 420, 200));
            primaryStage.show(); 
        }
    
        private void initPeople() {
            people.add(new MapTestPerson("Alfonso", "Cuarón"));
            people.add(new MapTestPerson("Ang", "Lee"));
            people.add(new MapTestPerson("Michel", "Hazanavicius"));
            people.add(new MapTestPerson("Tom", "Hooper"));
        }
    
        private Parent createContent(Stage primaryStage) {
            BorderPane pane = new BorderPane();
            pane.setPadding(new Insets(12));
            VBox box = new VBox(6);
            pane.setCenter(box);
    
            CheckComboBox<MapTestPerson> view = new CheckComboBox<MapTestPerson>(people);
            view.setConverter(new StringConverter<MapTestPerson>() {
                @Override
                public String toString(MapTestPerson object) {
                    return object.getFirstName() + " " + object.getLastName();
                }
                @Override
                public MapTestPerson fromString(String string) {
                    return null;
                }
            });
            box.getChildren().add(view);
    
            final ObservableList<MapTestPerson> selectedItems = view.getCheckModel().getCheckedItems();
            selectedItems.addListener((ListChangeListener<MapTestPerson>)change -> {
                while (change.next()) {
                    System.out.println("selectedItems change: " + change);
                }
            });
    
            ObservableList<String> lastNames = EasyBind.map(selectedItems, p -> p.getLastName());
            lastNames.addListener((ListChangeListener<String>)change -> {
                while (change.next()) {
                    System.out.println("lastNames change: " + change);
                }
            });
    
    //      ListView<String> selectedView = new ListView<String>(lastNames);
    //      box.getChildren().add(selectedView);
    
            return pane;
        }
    }
    
    class MapTestPerson {
        private StringProperty firstName = new SimpleStringProperty();
        private StringProperty lastName = new SimpleStringProperty();
    
        public MapTestPerson(String first, String last) {
            firstName.set(first);
            lastName.set(last);
        }
    
        @Override
        public String toString() {
            return firstName + " " + lastName;
        }
    
        public final StringProperty firstNameProperty() {
            return this.firstName;
        }
    
        public final String getFirstName() {
            return this.firstNameProperty().get();
        }
    
        public final void setFirstName(final String firstName) {
            this.firstNameProperty().set(firstName);
        }
    
        public final StringProperty lastNameProperty() {
            return this.lastName;
        }
    
        public final String getLastName() {
            return this.lastNameProperty().get();
        }
    
        public final void setLastName(final String lastName) {
            this.lastNameProperty().set(lastName);
        }
    }
    
    opened by ckeimel 2
  • map and listBind and weak references

    map and listBind and weak references

    I want to bind an ObservableList<T> to and `ObservableList.

    I wrote a mapping (map) from the first list to an intermediate one, then i use listBind between the intermediate and the target. The problem is that if i don't keep a strong reference on this intermediate list, it is garbaged and the binding is dispose.

    I wrote this simple test that fails:

    public class ListMapTest {
        @Test
        public void test() throws InterruptedException {
            ObservableList<String> source = FXCollections.observableArrayList();
            source.addAll("1", "2", "8");
    
            ObservableList<Integer> intermediate = EasyBind.map(source, Integer::parseInt);
    
            ObservableList<Integer> target = FXCollections.observableArrayList();
            EasyBind.listBind(target, intermediate);
    
            assertEquals(Arrays.asList(1, 2, 8) , target);
    
            intermediate = null;
            System.gc();
            Thread.sleep(1000);
    
            source.addAll(2, Arrays.asList("3", "5"));
            assertEquals(Arrays.asList(1, 2, 3, 5, 8), target);
    
            source.remove(1, 3);
            assertEquals(Arrays.asList(1, 5, 8), target);
        }
    }
    
    opened by gontard 1
  • Wrong link to jar file

    Wrong link to jar file

    Hi, Link to jar is not working: https://oss.sonatype.org/content/repositories/snapshots/org/fxmisc/easybind/easybind/1.0.3-SNAPSHOT/

    404 - Path /org/fxmisc/easybind/easybind/1.0.3-SNAPSHOT/ not found in local storage of repository "Snapshots" [id=snapshots]
    
    Path /org/fxmisc/easybind/easybind/1.0.3-SNAPSHOT/ not found in local storage of repository "Snapshots" [id=snapshots]
    
    opened by nonameplum 1
  • map function for Double property

    map function for Double property

    Currently, if I want to map DoubleProperty to String, I cannot use map function directly like:

    Easybind.map(someDoubleProperty, someMapperFromDoubleToString)
    

    because DoubleProperty only implements ObservableValue<Number>, not ObservableValue<Double> and thus the second argument should be Function<Number, U>. I think it would be better the method is overloaded for arguments (ObservableDoubleValue, DoubleFunction), (ObservableIntValue, IntFunction) and so on.

    It seems that this repository is not maintained these days. Since this is really splendid project, I hope that it will be continued developing...

    opened by b25rb2hkb25kbw 0
  • Javadoc Broken

    Javadoc Broken

    The links to the javadocs in the Readme don't work—it seems that fxmisc.org has disappeared. Can you consider hosting the javadocs somewhere else, like Github Pages?

    opened by nightpool 0
  • Add bindTo method to bind to properties

    Add bindTo method to bind to properties

    Right now one has to write

    target.targetProperty().bind(
                    EasyBind.map(source.sourceProperty(), mapping)
            );
    

    to bind to a property. A more fluent interface like

    EasyBind.map(source.sourceProperty(), mapping)
            .bindTo(target.targetProperty);
    

    would be nice.

    opened by tobiasdiez 0
  • Add EasyBind.concat(List<ObservableList<T>>)

    Add EasyBind.concat(List>)

    Creates a new list that combines the values of the given lists. Unlike FXCollections.concat(), updates to the source lists propagate to the combined list.

    I needed this for a project of mine, so I hacked it together. Since it might be useful to others as well, I thought I'd submit it here. If there are any changes you'd like me to make, let me know.

    opened by maul-esel 5
  • Allow combine on observable lists with immutable data & enhance combine performance

    Allow combine on observable lists with immutable data & enhance combine performance

    Please see the commit comments for further notes.

    I decided to forego putting the two ListCombinationBinding classes in a inheritance relationship. To my mind, the limitations of generics would make a mess out of that.

    Sidenote: Thumbs-Up for this repository! It's really helpful for my project.

    opened by mhoff 0
Releases(v1.0.3)
Owner
Tomas Mikula
Tomas Mikula
SynchronizeFX - a library for JavaFX 2 and later that enables property bindings between different JVMs

SynchronizeFX - a library for JavaFX 2 and later that enables property bindings between different JVMs, both on a local computer and over the network.

Manuel Mauky 8 Jul 24, 2020
Java Foreign Linker bindings to Lua

jpanlua Java bindings to liblua using JEP 398: Foreign Linker API. Requires JDK 16 or above. Requires -Dforeign.restricted=permit JRE flag. Exports pa

Aly Cerruti 5 Dec 24, 2021
🍏 A collection of partial JNA bindings for various macOS frameworks. (e.g. Foundation, AppKit, etc.)

JNApple ?? A collection of partial JNA bindings for various macOS frameworks. (e.g. Foundation, AppKit, etc.) Usage These are just some common example

Iridescent 3 Jun 19, 2022
RxJava bindings for Android

RxAndroid: Reactive Extensions for Android Android specific bindings for RxJava 3. This module adds the minimum classes to RxJava that make writing re

ReactiveX 19.7k Dec 28, 2022
A project that shows the different ways on how to create custom controls in JavaFX

JavaFX Custom Controls This project will show different ways on how to create custom controls in JavaFX. It will cover the following approaches: Resty

Gerrit Grunwald 27 Sep 5, 2022
A small tools to play with JavaFX Color.derive() function - allows to create custom colors and to save those in color palettes.

DeriveColorsFX This is not a serious application. Its a small tool where I just played with the method Color::deriveColor provided by JavaFX. Also its

Oliver Löffler 11 Oct 9, 2022
RXControls is a JavaFX custom component library.

RXControls RXControls Version 8.x.y need javafx8 RXControls Version 11.x.y need javafx11+ 一个javafx的自定义组件库, 密码可见组件, 轮播图组件, 动态按钮组件等, 音频频谱可视化组件,歌词组件 等...

null 164 Jan 1, 2023
Custom captions (window decorations) on Windows for JavaFX

javafx-customcaption javafx-customcaption is designed to allow customizing the native window caption on Microsoft Windows Usage: You can use the follo

null 3 Dec 15, 2022
CSGO cheat made in Java using JavaFX as the overlay and JNA for memory manipulation

CSGO-CHEAT CSGO cheat made in Java using JavaFX as the overlay and JNA for memory manipulation FEATURES RCS Triggerbot No Flash Third Person Rank Reve

Notorious 9 Dec 28, 2022
The snake and Ladders Game 🎲 for two players made using JavaFx with important concepts of OOPs🐍 🪜

The snake and Ladders Game made using JavaFx with important concepts of OOPs, done as a project for Advanced Programming course (CSE201) under Prof. Koteswar Rao Jerripothula

null 2 Sep 5, 2022
Tray Icon implementation for JavaFX applications. Say goodbye to using AWT's SystemTray icon, instead use a JavaFX Tray Icon.

FXTrayIcon Library intended for use in JavaFX applications that makes adding a System Tray icon easier. The FXTrayIcon class handles all the messy AWT

Dustin Redmond 248 Dec 30, 2022
Lib-Tile is a multi Maven project written in JavaFX and NetBeans IDE 8 and provides the functionalities to use and handle easily Tiles in your JavaFX application.

Lib-Tile Intention Lib-Tile is a multi Maven project written in JavaFX and NetBeans IDE and provides the functionalities to use and handle easily Tile

Peter Rogge 13 Apr 13, 2022
DataFX - is a JavaFX frameworks that provides additional features to create MVC based applications in JavaFX by providing routing and a context for CDI.

What you’ve stumbled upon here is a project that intends to make retrieving, massaging, populating, viewing, and editing data in JavaFX UI controls ea

Guigarage 110 Dec 29, 2022
Tree View; Mind map; Think map; tree map; custom view; 自定义; 树状图;思维导图;组织机构图;层次图

GysoTreeView 【中文】【English】 ⭐ If ok, give me a star ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ Tree View; Mind map; Think map; tree map; 树状图;思维导图;组织机构图;层次图;树型图 A custom tree view for

怪兽N 303 Dec 30, 2022
Understand Server-Sent Events (SSE), analyze its principle, and implement custom SSE.

custom-sse Email:[email protected] Github:https://github.com/JorringHsiao QQ:3129600569 ?? 关键词:SSE, 服务端推送, 实时推送, 进度条 ?? 本项目的目的 以进度条的功能需求为例,引出

JorringHsiao 17 Oct 18, 2021
Tool for creating custom GUIs using packets.

Tool for creating custom GUIs using packets.

Geo3gamer 0 Feb 14, 2022
Lightweight installer written in java, made for minecraft mods, The installer uses JPanel and uses a URL to install to the specific area (Discord URL's work the best i've seen)

InstallerForJava Lightweight installer written in java, made for minecraft mods, The installer uses JPanel and uses a URL to install to the specific a

null 18 Dec 9, 2022
Pomodoro app made for myself in Java.

Pomodoro app This is a Pomodoro-type app made in Java using Swift UI. How to use Click on "Start timer" and input how many minutes you want to focus.

null 4 Oct 19, 2022
Collection of Binding helpers for JavaFX(8)

Advanced-Bindings for JavaFX (8) advanced-bindings is a collection of useful helpers and custom binding implementations to simplify the development of

Manuel Mauky 63 Nov 19, 2022