Skip to content

Commit eab09b3

Browse files
committed
merge changes
2 parents 05cbe46 + b31be11 commit eab09b3

7 files changed

Lines changed: 392 additions & 56 deletions

File tree

README.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ $ ./gradlew build
5555
## Features
5656

5757
RxJavaFX has a lightweight set of features:
58-
- Factories to turn `Node` and `ObservableValue` events into an RxJava `Observable`
58+
- Factories to turn `Node`, `ObservableValue`, and other component events into an RxJava `Observable`
5959
- Factories to turn an RxJava `Observable` into a JavaFX `Binding`.
6060
- A scheduler for the JavaFX dispatch thread
6161

@@ -93,7 +93,7 @@ Observable<ActionEvent> menuItemEvents =
9393

9494
There are also factories provided to convert events from a `Window` as well as a `Scene` into an `Observable`. If you would like to see factories for other components and event types, please let us know or put in a PR.
9595

96-
#####Emitting Mouse Movement Events
96+
#####Emitting Scene Events
9797

9898
```java
9999
Observable<MouseEvent> sceneMouseMovements =
@@ -121,7 +121,7 @@ Observable<String> textInputs =
121121
```
122122
Note that many Nodes in JavaFX will have an initial value, which sometimes can be `null`, and you might consider using RxJava's `skip()` operator to ignore this initial value.
123123

124-
###ObservableValue Changes
124+
#####ObservableValue Changes
125125

126126
For every change to an `ObservableValue`, you can emit the old value and new value as a pair. The two values will be wrapped up in a `Change` class and you can access them via `getOldVal()` and `getNewVal()`. Just call the `JavaFxObservable.fromObservableValueChanges()` factory.
127127

@@ -173,6 +173,16 @@ sub2 = textInputs.observeOn(Schedulers.computation())
173173
.observeOn(JavaFxScheduler.getInstance())
174174
.subscribe(fippedTextLabel::setText);
175175
```
176+
177+
##Differences from ReactFX
178+
[ReactFX](https://github.com/TomasMikula/ReactFX) is a popular API to implement reactive patterns with JavaFX using the `EventStream`. However, RxJava uses an `Observable` and the two are not (directly) compatible with each other.
179+
180+
Although ReactFX has some asynchronous operators like `threadBridge`, ReactFX emphasizes *synchronous* behavior. This means it encourages keeping events on the JavaFX thread. RxJavaFX, which fully embraces RxJava and *asynchronous* design, can switch between threads and schedulers with ease. As long as subscriptions affecting the UI are observed on the JavaFX thread, you can leverage the powerful operators and libraries of RxJava safely.
181+
182+
If you are heavily dependent on RxJava, asynchronous processing, or do not want your entire reactive codebase to be UI-focused, you will probably want to use RxJavaFX.
183+
184+
185+
176186
##Notes for Kotlin
177187
If you are building your JavaFX application with [Kotlin](https://kotlinlang.org/), this library becomes even more useful with extension functions. These extension functions exist in the [RxKotlinFX](https://github.com/thomasnield/RxKotlinFX) project, but the API is so small it is not worth publishing at the moment. But you can simply add these extension functions below to your project and utilize Kotlin's fluent style.
178188

@@ -181,7 +191,7 @@ fun <T> Observable<T>.toBinding() = JavaFxSubscriber.toBinding(this)
181191
fun <T> Observable<T>.toBinding(errorHandler: (Throwable) -> Unit) = JavaFxSubscriber.toBinding(this,errorHandler)
182192
fun <T> ObservableValue<T>.toObservable() = JavaFxObservable.fromObservableValue(this)
183193
fun <T> ObservableValue<T>.toObservableChanges() = JavaFxObservable.fromObservableValueChanges(this)
184-
fun <T : Event> Node.toNodeEvents(eventType: EventType<T>) = JavaFxObservable.fromNodeEvents(this, eventType)
194+
fun <T: Event> Node.toNodeEvents(eventType: EventType<T>) = JavaFxObservable.fromNodeEvents(this, eventType)
185195
fun <T> Observable<T>.observeOnFx() = this.observeOn(JavaFxScheduler.getInstance())
186196
```
187197
This allows you to better use Kotlin's features to interop JavaFX and RxJava much more cleanly.
@@ -194,12 +204,6 @@ val lengthBinding = textInputs.map { it.length }.toBinding()
194204

195205
If you are doing JavaFX and Kotlin development, definitely check out [TornadoFX](https://github.com/edvin/tornadofx) as well to utilize type-safe builders and other features enabled by Kotlin.
196206

197-
##Differences from ReactFX
198-
[ReactFX](https://github.com/TomasMikula/ReactFX) is a popular API to implement reactive patterns with JavaFX using the `EventStream`. However, RxJava uses an `Observable` and the two are not (directly) compatible with each other.
199-
200-
Although ReactFX has some asynchronous operators like `threadBridge`, ReactFX emphasizes *synchronous* behavior. This means it encourages keeping events on the JavaFX thread. RxJavaFX, which fully embraces RxJava and *asynchronous* design, can switch between threads and schedulers with ease. As long as subscriptions affecting the UI are observed on the JavaFX thread, you can leverage the powerful operators and libraries of RxJava safely.
201-
202-
If you are heavily dependent on RxJava, asynchronous processing, or do not want your entire reactive codebase to be UI-focused, you will probably want to use RxJavaFX.
203207

204208
## Comprehensive Example
205209
```java
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package rx.javafx.sources;
2+
3+
public enum Flag {
4+
ADDED,
5+
REMOVED,
6+
UPDATED;
7+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package rx.javafx.sources;
2+
3+
/**
4+
* Holds an ADDED, REMOVED, or UPDATED flag with the associated value
5+
* @param <T>
6+
*/
7+
public final class ListChange<T> {
8+
private final T value;
9+
private final Flag flag;
10+
11+
private ListChange(T value, Flag flag) {
12+
this.value = value;
13+
this.flag = flag;
14+
}
15+
public static <T> ListChange<T> of(T value, Flag flag) {
16+
return new ListChange<>(value, flag);
17+
}
18+
public T getValue() {
19+
return value;
20+
}
21+
public Flag getFlag() {
22+
return flag;
23+
}
24+
@Override
25+
public String toString() {
26+
return flag + " " + value;
27+
}
28+
}

src/main/java/rx/javafx/sources/ObservableListSource.java

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -191,39 +191,17 @@ public int remove(T value) {
191191
Integer prev = counts.get(value);
192192
if (prev != null && prev > 0) {
193193
int newVal = prev - 1;
194-
counts.put(value, newVal);
194+
if (newVal == 0) {
195+
counts.remove(value);
196+
} else {
197+
counts.put(value, newVal);
198+
}
195199
return newVal;
196200
}
197201
else {
198202
throw new IllegalStateException();
199203
}
200204
}
201205
}
202-
public static final class ListChange<T> {
203-
private final T value;
204-
private final Flag flag;
205206

206-
private ListChange(T value, Flag flag) {
207-
this.value = value;
208-
this.flag = flag;
209-
}
210-
public static <T> ListChange<T> of(T value, Flag flag) {
211-
return new ListChange<>(value, flag);
212-
}
213-
public T getValue() {
214-
return value;
215-
}
216-
public Flag getFlag() {
217-
return flag;
218-
}
219-
@Override
220-
public String toString() {
221-
return flag + " " + value;
222-
}
223-
}
224-
public enum Flag {
225-
ADDED,
226-
REMOVED,
227-
UPDATED;
228-
}
229207
}

src/main/java/rx/observables/JavaFxObservable.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818

1919
import javafx.beans.value.ObservableValue;
20+
import javafx.collections.ObservableList;
2021
import javafx.event.ActionEvent;
2122
import javafx.event.Event;
2223
import javafx.event.EventType;
@@ -27,6 +28,7 @@
2728
import javafx.stage.Window;
2829
import javafx.stage.WindowEvent;
2930
import rx.Observable;
31+
import rx.functions.Func1;
3032
import rx.javafx.sources.*;
3133

3234

@@ -117,4 +119,73 @@ public static Observable<ActionEvent> fromActionEvents(final ContextMenu context
117119
public static Observable<ActionEvent> fromActionEvents(final MenuItem menuItem) {
118120
return ActionEventSource.fromActionEvents(menuItem);
119121
}
122+
123+
/**
124+
* Creates an observable that emits an ObservableList every time it is modified
125+
*
126+
* @param source The target ObservableList of the ListChange events
127+
* @return An Observable emitting the ObservableList each time it changes
128+
*/
129+
public static <T> Observable<ObservableList<T>> fromObservableList(final ObservableList<T> source) {
130+
return ObservableListSource.fromObservableList(source);
131+
}
132+
/**
133+
* Creates an observable that emits all additions to an ObservableList
134+
*
135+
* @param source The target ObservableList for the item add events
136+
* @return An Observable emitting items added to the ObservableList
137+
*/
138+
public static <T> Observable<T> fromObservableListAdds(final ObservableList<T> source) {
139+
return ObservableListSource.fromObservableListAdds(source);
140+
}
141+
/**
142+
* Creates an observable that emits all removal items from an ObservableList
143+
*
144+
* @param source The target ObservableList for the item removal events
145+
* @return An Observable emitting items removed from the ObservableList
146+
*/
147+
public static <T> Observable<T> fromObservableListRemovals(final ObservableList<T> source) {
148+
return ObservableListSource.fromObservableListRemovals(source);
149+
}
150+
/**
151+
* Creates an observable that emits all updated items from an ObservableList.
152+
* If you declare an ObservableList that listens to one or more properties of each element,
153+
* you can emit the changed items every time these properties are modified
154+
* <pre>ObservableList<Person> sourceList = FXCollections.observableArrayList(user -> new javafx.beans.Observable[]{user.age} );</pre>
155+
* @param source The target ObservableList for the item update events
156+
*
157+
* @return An Observable emitting items updated in the ObservableList
158+
*/
159+
public static <T> Observable<T> fromObservableListUpdates(final ObservableList<T> source) {
160+
return ObservableListSource.fromObservableListUpdates(source);
161+
}
162+
163+
/**
164+
* Emits all added, removed, and updated items from an ObservableList
165+
* @param source
166+
* @return An Observable emitting changed items with an ADDED, REMOVED, or UPDATED flags
167+
*/
168+
public static <T> Observable<ListChange<T>> fromObservableListChanges(final ObservableList<T> source) {
169+
return ObservableListSource.fromObservableListChanges(source);
170+
}
171+
/**
172+
* Emits distinctly added and removed items from an ObservableList.
173+
* If dupe items with identical hashcode/equals evaluations are added to an ObservableList, only the first one will fire an ADDED item.
174+
* When the last dupe is removed, only then will it fire a REMOVED item.
175+
* @param source
176+
* @return An Observable emitting changed items with an ADDED, REMOVED, or UPDATED flags
177+
*/
178+
public static <T> Observable<ListChange<T>> fromObservableListDistinctChanges(final ObservableList<T> source) {
179+
return ObservableListSource.fromObservableListDistinctChanges(source);
180+
}
181+
/**
182+
* Emits distinctly added and removed mappings of each item from an ObservableList.
183+
* If dupe mapped items with identical hashcode/equals evaluations are added to an ObservableList, only the first one will fire an ADDED item.
184+
* When the last dupe is removed, only then will it fire a REMOVED item.
185+
* @param source
186+
* @return An Observable emitting changed mapped items with an ADDED, REMOVED, or UPDATED flags
187+
*/
188+
public static <T,R> Observable<ListChange<R>> fromObservableListDistinctMappings(final ObservableList<T> source, Func1<T,R> mapper) {
189+
return ObservableListSource.fromObservableListDistinctMappings(source,mapper);
190+
}
120191
}

src/main/java/rx/schedulers/JavaFxScheduler.java

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,3 @@
1-
/**
2-
* Copyright 2014 Netflix, Inc.
3-
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
7-
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
15-
*/
161
package rx.schedulers;
172

183
import javafx.animation.KeyFrame;
@@ -163,10 +148,9 @@ public Subscription schedule(final Action0 action) {
163148
if (trampoline(runnable)) {
164149
return Subscriptions.unsubscribed();
165150
}
166-
else {
167-
queue.offer(runnable);
168-
Platform.runLater(this);
169-
}
151+
}else {
152+
queue.offer(runnable);
153+
Platform.runLater(this);
170154
}
171155

172156
// wrap for returning so it also removes it from the 'innerSubscription'

0 commit comments

Comments
 (0)