Coder Social home page Coder Social logo

reactfx's People

Contributors

cardamo avatar hastebrot avatar jordanmartinez avatar tomasmikula avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reactfx's Issues

Build fails if sonatypeUsername or sonatypePassword aren't set.

Both variables are used during the configuration phase of the build, so they'll cause an error:

No such property: sonatypeUsername for class: org.gradle.api.publication.maven.internal.ant.DefaultGroovyMavenDeployer

The evaluation of both properties needs to be delayed until the execution phase. Right now the onlyIf condition has no effect since the configuration phase can't be skipped.

I think the easiest way to delay property evaluation is to use a closure for those properties, but I'm not positive it's the correct way. You'll have to test it to make sure it doesn't break uploading archives.

repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
  authentication(userName: {sonatypeUsername}, password: {sonatypePassword})
}

snapshotRepository(url: 'https://oss.sonatype.org/content/repositories/snapshots') {
    authentication(userName: {sonatypeUsername}, password: {sonatypePassword})
}

subscribe: NoSuchMethodError

Hi,

I have come across the following exception:

Caused by: java.lang.NoSuchMethodError: org.reactfx.EventStreamBase.subscribe(Ljava/lang/Object;)Lorg/reactfx/Subscription;
at org.reactfx.BiEventSource.subscribe(BiEventSource.java:5)

here is my code:

BiEventSource<Metadata,Metadata> itemUpdateES = new BiEventSource();

public Subscription subscribeToUpdates(BiConsumer<Metadata, Metadata> bc) {
return itemUpdateES.subscribe(bc);
}

another exception that seems to have the same cause is:

Caused by: java.lang.NoSuchMethodError: org.reactfx.MappedBiStream.subscribeToBi(Lorg/reactfx/BiEventStream;Ljava/util/function/BiConsumer;)Lorg/reactfx/Subscription;
at org.reactfx.MappedBiStream.subscribeToInputs(MappedStream.java:46)
at org.reactfx.LazilyBoundStream.firstSubscriber(LazilyBoundStream.java:17)
at org.reactfx.EventStreamBase.subscribe(EventStreamBase.java:35)
at org.reactfx.EventStreams$StreamBoundValueImpl.(EventStreams.java:689)
at org.reactfx.EventStreams.toObservableValue(EventStreams.java:544)
at org.reactfx.EventStream.toObservableValue(EventStream.java:72)

thrown when used toObservableValue() on an event stream that is a result of :
itemUpdateES.map((ov,nv)->nv).toObservableValue(value)

Any thoughts?

It looks like a problem with BiEventStreamBase implementation. I know i can always use BiTouple, but id rather not.

Or am doing something wrong? Not sure why it is looking for subscribe(Object). Should the parameter not be a BiConsumer?

edit: the problem turns out to be outdated .jar in the download section. Using the sources.jar directly does not cause any problems. The method toObservable() was apparently renamed to toBinding().

Weak Consumer references

JavaFX has a few different WeakXXX wrappers for listeners. I currently need a similar thing for a Consumer that I can subscribe with, but that will automatically be removed if it's owners get garbage collected. LimitedInvocationSubscriber offers similar functionality, but with a different "reason" to revoke the subscription.

Val.filer contains null if predicate is false

Hi,

I am using a Val bound to the text property of a TextField. I filter out strings which cannot be converted to LocalDateTime with a predicate in the string formatter

Val.filter(this.textField.textProperty(), this.format::isValidDateTime)

and them map the Val to LocalDateTime. In my program I start observing null values in the the Val when the isValidDateTime returns false. From the documentation of Val.filter, I would expect that values that do not satisfy the predicate are dropped and not replaced with null. However, I am not sure because I did not find a definition for an empty Val. Here is the code with the javadoc from

/**
 * Returns a new {@linkplain Val} that holds the same value
 * as this {@linkplain Val} when the value satisfies the predicate
 * and is empty when this {@linkplain Val} is empty or its value
 * does not satisfy the given predicate.
 */
default Val<T> filter(Predicate<? super T> p) {
    return filter(this, p);
}

static <T> Val<T> filter(
        ObservableValue<T> src,
        Predicate<? super T> p) {
    return map(src, t -> p.test(t) ? t : null);
}

It is obvious that values failing the predicate are substituted by null. Is that how it is supposed to work?

suggestion: intelligent styled text entity.. more like a snippet

hi tomas, i would like to suggest adding Api to create snippets. the idea is that those snippets would know if they are bing selected, hovered over, etc. and a text area would know these snippets' locations, their style, etc. and probably have a property to report on which, if any, snippet is being clicked inside of or selected.

i implemented a very sketchy version, which half-works for the purpose of my project, but i thought such element type would be cool to have i richtextfx.

thanks !
Maher

Question about how to combine streams

I've read all the documentation I can find and have experimented quite a bit, but I just can't figure out what's going on. I posted my question on StackOverflow and I'm posting here with the hope of getting my issue some attention. If my problem can be solved I think it would make a good use example to go in the wiki.

Feature request: lazy Val/Var

Hi,

I'm thinking about an extension to Val/Var, or rather to the provided factory methods. The idea is to provide a Supplier that gets called the first time the value is actually read.

Val<String> lazyVal = Val.lazy(this::calculateComplicatedName); // not called yet
// do some stuff
System.out.println(lazyVal.getValue()); // calculateComplicatedName called now
System.out.println(lazyVal.getValue()); // result was cached, not called again

This is obviously useful for cases where a default value is expensive to calculate. But I was actually thinking about loading remote values on demand. Instead of loading everythink immediately, this would provide a clean API for lazy loading. For remote calls, this would have to be done async.

Val<String> lazyVal = Val.lazyAsync(String placeholder, this::loadNameFromServer); // not called yet
// do some stuff
System.out.println(lazyVal.getValue()); // returns placeholder, loadNameFromServer started in separate thread
// some time later
System.out.println(lazyVal.getValue()); // loading was finished, returns result now

While it's easy enough for a Val, because it cannot change, I'm not so sure how or even if it's useful for a Var. But I would be grateful for any input on this. If there is interest, I would be happy to code it myself and start a pull request.

Cannot use EventStreams.eventsOf with MenuItem

 MenuItem menuItem = ...;
 EventStream<ActionEvent> stream = EventStreams.eventsOf(menuItem, ActionEvent.ACTION);

Doesn't work because MenuItem doesn't extend Node.
This is just a minor inconvenience because I can simply adjust that method for MenuItem.

[enhancement] Some "min" and "max" methods for event stream.

Hi,
in a current project I use a lot "min" and "max" methods (like the methods from the java stream api).
So in this project I wrote a small event stream extension with this methods but maybe there is the possibility to add these methods in a upcoming release/milestone/snapshot ;)

default EventStream<T> min(Comparator<? super T> comparator) {
        return accumulate(BinaryOperator.minBy(comparator));
    }

 default EventStream<T> max(Comparator<? super T> comparator) {
        return accumulate(BinaryOperator.maxBy(comparator));
    }

Will anyone miss the error reporting mechanism?

I would like to know opinions on usefulness of the error reporting mechanism. I increasingly feel we could do just as well (or better) without it.

STATUS QUO: Currently, when an event stream encounters an error (exception), instead of letting the exception propagate up the call stack, it is caught and propagated through the network of event streams until it is handled (or printed to stderr by default). The consequence is that even when exceptions are being thrown, the program continues normally, likely with incorrect behavior, while exceptions are just being logged.

I increasingly feel that exceptions caused by bugs in the code should just be propagated up the call stack; it is unlikely they can be handled in any meaningful way anyway.

Note that in situations where failure is a valid outcome (such as IO or async tasks), it has always been recommended to materialize the error in the event type, i.e. use EventStream<Try<T>> instead of EventStream<T>.

There will be many backward incompatible changes in 2.0 already, so this seems like a good point to deal with the error reporting mechanism, too.

Does anyone have any opinions on this matter?

Memory leak due to EventStreams.ticks and EventStream.emitOnEach(EventStream<?>)

Hello,

i use an EventStream which emit values on each tick of a "timer" stream:

SimpleBooleanProperty property = new SimpleBooleanProperty(this, "test", true);
EventStream<?> timerStream = EventStreams.ticks(ofMillis(10));
EventStreams.valuesOf(property).emitOnEach(timerStream).subscribe(System.err::println);

this cause a memory leak in my application, the FxTimer seems to be kept by the Toolkit. This is the output of the eclipse memory analyser.

-----------------------------------------------------------------------------------------------------------------------------------------
com.sun.javafx.tk.quantum.QuantumToolkit$$Lambda$48 @ 0x76eb0b328 Native Stack                                                           
'- arg$1 com.sun.javafx.tk.quantum.QuantumToolkit @ 0x76eb103a8                                                                          
   '- animationRunnable com.sun.scenario.animation.AbstractMasterTimer$MainLoop @ 0x76ece3b10                                            
      '- this$0 com.sun.javafx.tk.quantum.MasterTimer @ 0x76eb7f068                                                                      
         '- receivers com.sun.scenario.animation.shared.PulseReceiver[2] @ 0x76eb7f0b8                                                   
            '- [0] javafx.animation.Animation$1 @ 0x76eb7f0d0                                                                            
               '- this$0 javafx.animation.Timeline @ 0x76eb7f0e0                                                                         
                  '- clipCore com.sun.scenario.animation.shared.TimelineClipCore @ 0x76eb7f420                                           
                     '- keyFrames javafx.animation.KeyFrame[1] @ 0x76eb7f450                                                             
                        '- [0] javafx.animation.KeyFrame @ 0x76eb7f468                                                                   
                           '- onFinished org.reactfx.util.FxTimer$$Lambda$213 @ 0x76eb7f498                                              
                              '- arg$1 org.reactfx.util.FxTimer @ 0x76eb7f4b0                                                            
                                 '- action org.reactfx.EventStreams$14$$Lambda$184 @ 0x76eb7f4d0                                         
                                    '- arg$1 org.reactfx.EventStreams$14 @ 0x76eb7f4e0                                                   
                                       '- observers org.reactfx.util.ListHelper$SingleElemHelper @ 0x76eb7f500                           
                                          '- elem org.reactfx.SuspendableBase$$Lambda$212 @ 0x76eb7f510                                  
                                             '- arg$1 org.reactfx.PausableEventStream @ 0x76eb7f520                                      
                                                '- observers org.reactfx.util.ListHelper$SingleElemHelper @ 0x76eb7f550                  
                                                   '- elem org.reactfx.SuspendWhenStream$$Lambda$211 @ 0x76eb7f560                       
                                                      '- arg$1 org.reactfx.SuspendWhenStream @ 0x76eb7f570                               
                                                         '- observers org.reactfx.util.ListHelper$SingleElemHelper @ 0x76eb7f598         
                                                            '- elem org.reactfx.EmitOnEachStream$$Lambda$209 @ 0x76eb7f5a8               
                                                               '- arg$1 org.reactfx.EmitOnEachStream @ 0x76eb7f5b8                       
                                                                  '- observers org.reactfx.util.ListHelper$SingleElemHelper @ 0x76eb7f5e0
                                                                     '- elem org.reactfx.MappedStream$$Lambda$196 @ 0x76eb7f5f0          
                                                                        '- arg$1 org.reactfx.MappedStream @ 0x76eb7f600                  
-----------------------------------------------------------------------------------------------------------------------------------------

am i missing something ?

Make an EventStream active based on a SimpleObjectProperty's bean value

How do I make an EventStream active only when an ObjectProperty's bean matches a specific value? (In this case, the ObjectProperty holds an Enum.) As of right now, a stream is always active unless suspended when an Observable is true. What's the best way to reverse this (an EventStream is active only when an ObjectProperty holds a specific value)?

enum State {
    UNFINISHED,
    CONNECTED,
    ROOT
}
SimpleObjectProperty<State> state = new SimpleObjectProperty(State.UNFINISHED)

I want to create 3 EventStreams, each of which are only active when 'state' holds a specific value:

  1. Stream A is only active when state's bean == State.UNFINISHED
  2. Stream B is only active when state's bean == State.CONNECTED
  3. Stream C is only active when state's bean == State.ROOT

how to pause

How do I pause and resume using Timer ?

I followed your approach in http://tomasmikula.github.io/blog/2014/06/04/timers-in-javafx-and-reactfx.html

In javafx, I can do

Timeline autosaveTimer = new Timeline(
				new KeyFrame(Duration.seconds(60 * MINUTES),
						ae -> masterClock.setSaveSim(DEFAULT))); 

At some point I have to call autosaveTimer().pause() to pause

and later call autosaveTimer().play() to resume

In ReactFX, I do the following

Timer autosaveTimer = FxTimer.runLater(
    				java.time.Duration.ofMinutes(60 * MINUTES),
    		        () -> masterClock.setSaveSim(DEFAULT)));

I notice there is a stop() and restart() for Timer

but I don't want to stop autosaveTimer completely and restart from the beginning of MINUTES. I want to resume from whatever MINUTES has elapsed.

Is there a pause() and resume() that I can call ?

List change event inconsistencies

I've noticed some strange behavior with list change events when using EventStreams or LiveList.

import static org.hamcrest.Matchers.contains;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;

import java.util.function.Consumer;

import javafx.beans.Observable;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.reactfx.EventStreams;
import org.reactfx.collection.ListChange;
import org.reactfx.collection.LiveList;

@RunWith(MockitoJUnitRunner.class)
public class LiveListUpdateTest {
  private static class Data {
    private final Property<String> string = new SimpleObjectProperty<>();

    public String getString() {
      return string.getValue();
    }

    public Property<String> stringProperty() {
      return string;
    }
  }

  @Captor
  ArgumentCaptor<ListChangeListener.Change<? extends Data>> originalChange;
  @Captor
  ArgumentCaptor<ListChangeListener.Change<? extends Data>> eventStreamChange;
  @Captor
  ArgumentCaptor<ListChange<? extends Data>> liveListChange;
  @Captor
  ArgumentCaptor<ListChangeListener.Change<? extends String>> mappedChange;

  @Mock
  ListChangeListener<? super Data> originalListener;
  @Mock
  Consumer<ListChangeListener.Change<? extends Data>> eventStreamListener;
  @Mock
  Consumer<ListChange<? extends Data>> liveListListener;
  @Mock
  ListChangeListener<? super String> mappedListener;

  Data data;
  ObservableList<Data> updateableList;
  ObservableList<String> mappedList;

  @Before
  public void before() {
    data = new Data();
    updateableList = FXCollections.observableArrayList(d -> new Observable[] { d.stringProperty() });
    updateableList.add(data);

    mappedList = LiveList.map(updateableList, Data::getString);
  }

  @Test
  public void initialChangeState() {
    updateableList.addListener(originalListener);
    EventStreams.changesOf(updateableList).subscribe(eventStreamListener);

    data.stringProperty().setValue("foo");

    verify(originalListener).onChanged(originalChange.capture());
    assertTrue(originalChange.getValue().next());

    verify(eventStreamListener).accept(eventStreamChange.capture());
    assertTrue(eventStreamChange.getValue().next()); // Fails
  }

  @Test
  public void liveListShouldNotInvalidateInitialChangeState() {
    updateableList.addListener(originalListener);
    LiveList.changesOf(updateableList).subscribe(liveListListener);

    data.stringProperty().setValue("foo");

    verify(originalListener).onChanged(originalChange.capture());
    assertTrue(originalChange.getValue().next()); // Fails

    verify(liveListListener).accept(liveListChange.capture());
    assertEquals(1, liveListChange.getValue().getModificationCount());
  }

  @Test
  public void mappedUpdates() {
    updateableList.addListener(originalListener);
    mappedList.addListener(mappedListener);

    data.stringProperty().setValue("foo");
    assertThat(mappedList, contains("foo"));

    verify(originalListener).onChanged(originalChange.capture());
    assertTrue(originalChange.getValue().next()); // Fails
    assertTrue(originalChange.getValue().wasUpdated());
    assertFalse(originalChange.getValue().next());

    verify(mappedListener).onChanged(mappedChange.capture());
    assertTrue(originalChange.getValue().next()); // Fails
    assertTrue(mappedChange.getValue().wasUpdated()); // Fails
    assertFalse(originalChange.getValue().next());
  }
}

Combining multiple Vals

Adding a way to combine instances of Val, like EasyBind.combine(...) would fully cover functionallity of EasyBind.

May be except EasyBind.select(...), but I dont use it :)

Val (with change listener) that is invalidated doesn't get revalidated?

I'm confused why the code below isn't working.

I have a list of episodes, each with a watched property. This list can get large, so I'm trying to avoid binding to each item, but only binding to the activate one, as indicated by episodeProp. I create a Val that indicates the state of the current episode.

Next, I create a Val that will reflect the percentage of episodes watched, using the aforementioned Val which indicates the state of the current episode. However, despite a change listener being present on this derived Val, it doesn't update, even though it does get invalidated (and as far as I know, if something gets invalidated that has a change listener attached to it, then it must get revalidated again...)

It seems to me that properties are not getting revalidated, resulting in further invalidations to be lost.

Solutions are to add episodeProp as an additional dependency for watchedPercentage, or to add a ChangeListener to episodeWatched.

The output of the program is:

Recalculating...
WatchedPercentage = 0
1, expecting invalidation as episodeProp is changed:
>> EpisodeWatched Invalidated
>> WatchedPercentage Invalidated
Recalculating...
Watched Percentage = 25
2, expecting invalidation as episodeProp->watched is changed:
3, expecting invalidation as episodeProp is changed:
4

The code:

public static void main(String[] args) throws InterruptedException {
  List<Ep> eps = new ArrayList<>();

  eps.addAll(Arrays.asList(new Ep(), new Ep(), new Ep(), new Ep()));

  ObjectProperty<Ep> episodeProp = new SimpleObjectProperty<>();

  Val<Boolean> episodeWatched = Val.wrap(episodeProp).flatMap(ep -> ep.watched);
  Val<Integer> watchedPercentage = Val.create(() -> {
    System.out.println("Recalculating...");
    int watched = 0;
    int total = 0;
    for(Ep ep : eps) {
      total++;
      if(ep.watched.get()) {
        watched++;
      }
    }

    return 100 * watched / total;
  }, episodeWatched);

  episodeWatched.addListener((obs) -> System.out.println(">> EpisodeWatched Invalidated"));
  watchedPercentage.addListener((obs) -> System.out.println(">> WatchedPercentage Invalidated"));

  System.out.println("WatchedPercentage = " + watchedPercentage.getValue());

  watchedPercentage.addListener((obs, old, current) -> System.out.println("Watched Percentage = " + current));
  eps.get(0).watched.set(true);
  System.out.println("1, expecting invalidation as episodeProp is changed:");
  episodeProp.set(eps.get(0));
  System.out.println("2, expecting invalidation as episodeProp->watched is changed:");
  episodeProp.get().watched.set(false);
  System.out.println("3, expecting invalidation as episodeProp is changed:");
  episodeProp.set(eps.get(1));
  System.out.println("4");

  Thread.sleep(2000);
}

public static class Ep {
  BooleanProperty watched = new SimpleBooleanProperty();
}

Converting a Var into a Val?

I have a Var used internally, and want to expose its value (read only) as a Val.

It seems that this should be pretty trivial, but there is no method on Var to accomplish this directly:

 Var<Integer> var = Var.newSimpleVar(0);
 Val<Integer> val = var.asVal();
 // or:
 Val<Integer> val = var.readOnly();

So for now I'm using:

 Val<Integer> val = var.orElseConst(0);

Am I missing something obvious?

Add a global toggle for FX thread checks.

I'm not sure if a global toggle is the right approach, but starting in Java 8u40 some of the restrictions that require nodes be created on the application thread are being loosened. See here:

https://javafx-jira.kenai.com/browse/RT-17716

I want to take advantage of the above to build my entire UI on the JavaFX-Launcher thread. However, ReactFX has a handful of thread checks that bubble an IllegalStateException when I try this. For example:

Caused by: java.lang.IllegalStateException: Not on FX application thread
    at org.reactfx.EventStream.reduceCloseSuccessions(EventStream.java:125) ~[reactfx-1.0.0.jar:na]
    at org.reactfx.EventStream.reduceCloseSuccessions(EventStream.java:98) ~[reactfx-1.0.0.jar:na]

Is it possible for ReactFX to accommodate building components on a non-application thread?

prepend(T t) EventStream

When combining an EventStream that has a dependency on an EventStream that emits InputEvents, the combined EventStream will not emit a value (and thus anything that depends upon it will not work) until that that InputEvent is fired:

EventStream<Boolean> controlPressed = EventStreams
    .eventsOf(scene, KeyEvent.KEY_PRESSED)
    .filter(key -> key.getCode().is(KeyCode.CONTROL))
    .map(key -> key.isControlDown());
EventStream<?> other = // code...

EventStream<Tuple2<Boolean, ?>> combination = EventStreams
    .combine(controlPressed, other);

// this will not run immediately upon program start; it will once user presses a key once.
combination.subscribe(tuple2 -> System.out.println("Tuple2: " + tuple2.toString());

The only workaround is to merge such an EventStream with an EventSource:

EventSource<Boolean> initialValue = new EventSource();
EventStream<Boolean> controlKeys = EventStreams
    .eventsOf(scene, KeyEvent.KEY_PRESSED)
    .filter(key -> key.getCode().is(KeyCode.CONTROL))
    .map(key -> key.isControlDown());
EventStream<Boolean> controlPressed = EventStreams.merge(initialValue, controlKeys);

EventStream<Tuple2<Boolean, ?>> combination = EventStreams
    .combine(controlPressed, other);

initialValue.push(false);

The following code could be a better workaround:

EventStream<Boolean> controlPressed = EventStreams
    .eventsOf(scene, KeyEvent.KEY_PRESSED)
    .filter(key -> key.getCode().is(KeyCode.CONTROL))
    .map(key -> key.isControlDown())
    // since a value won't be emitted until user presses control key,
    //    force the stream to emit an initial value when subscribed to
    .prepend(false);

Image placeholder for ReactFX EventStream "Ecosystem"

Although there's this approach to making images linkable in a wiki, this approach is faster.

reactfx - freeplane - mind map mode -home-jordan-dropbox-groovy-reactfx mm_006

If one wants to be able to edit this image, please download Freeplane and copy/paste the save file's text (below) into an empty file and save as "ReactFX - Ecosystem.mm"

Save File Text

<map version="freeplane 1.3.0">
<!--To view this file, download free mind mapping software Freeplane from http://freeplane.sourceforge.net -->
<node TEXT="ReactFX" ID="ID_1723255651" CREATED="1283093380553" MODIFIED="1445967632760"><hook NAME="MapStyle" zoom="1.1">

<map_styles>
<stylenode LOCALIZED_TEXT="styles.root_node">
<stylenode LOCALIZED_TEXT="styles.predefined" POSITION="right">
<stylenode LOCALIZED_TEXT="default" MAX_WIDTH="600" COLOR="#000000" STYLE="as_parent">
<font NAME="SansSerif" SIZE="10" BOLD="false" ITALIC="false"/>
</stylenode>
<stylenode LOCALIZED_TEXT="defaultstyle.details"/>
<stylenode LOCALIZED_TEXT="defaultstyle.note"/>
<stylenode LOCALIZED_TEXT="defaultstyle.floating">
<edge STYLE="hide_edge"/>
<cloud COLOR="#f0f0f0" SHAPE="ROUND_RECT"/>
</stylenode>
</stylenode>
<stylenode LOCALIZED_TEXT="styles.user-defined" POSITION="right">
<stylenode LOCALIZED_TEXT="styles.topic" COLOR="#18898b" STYLE="fork">
<font NAME="Liberation Sans" SIZE="10" BOLD="true"/>
</stylenode>
<stylenode LOCALIZED_TEXT="styles.subtopic" COLOR="#cc3300" STYLE="fork">
<font NAME="Liberation Sans" SIZE="10" BOLD="true"/>
</stylenode>
<stylenode LOCALIZED_TEXT="styles.subsubtopic" COLOR="#669900">
<font NAME="Liberation Sans" SIZE="10" BOLD="true"/>
</stylenode>
<stylenode LOCALIZED_TEXT="styles.important">
<icon BUILTIN="yes"/>
</stylenode>
</stylenode>
<stylenode LOCALIZED_TEXT="styles.AutomaticLayout" POSITION="right">
<stylenode LOCALIZED_TEXT="AutomaticLayout.level.root" COLOR="#000000">
<font SIZE="18"/>
</stylenode>
<stylenode LOCALIZED_TEXT="AutomaticLayout.level,1" COLOR="#0033ff">
<font SIZE="16"/>
</stylenode>
<stylenode LOCALIZED_TEXT="AutomaticLayout.level,2" COLOR="#00b439">
<font SIZE="14"/>
</stylenode>
<stylenode LOCALIZED_TEXT="AutomaticLayout.level,3" COLOR="#990000">
<font SIZE="12"/>
</stylenode>
<stylenode LOCALIZED_TEXT="AutomaticLayout.level,4" COLOR="#111111">
<font SIZE="10"/>
</stylenode>
</stylenode>
</stylenode>
</map_styles>
</hook>
<hook NAME="AutomaticEdgeColor" COUNTER="9"/>
<node TEXT="Ecosystem" POSITION="left" ID="ID_956871798" CREATED="1447990190342" MODIFIED="1447994714816">
<edge STYLE="hide_edge" COLOR="#007c00"/>
<node TEXT="Creation" LOCALIZED_STYLE_REF="AutomaticLayout.level,1" ID="ID_1656444366" CREATED="1447990199025" MODIFIED="1447994687649" HGAP="508" VSHIFT="-358">
<edge STYLE="hide_edge"/>
<cloud COLOR="#f0f0f0" SHAPE="ARC"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#000000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_801039055" MIDDLE_LABEL="(Optional)" STARTINCLINATION="-50;73;" ENDINCLINATION="-38;-30;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#000000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_199892113" STARTINCLINATION="-211;273;" ENDINCLINATION="-41;-153;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#000000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_751662814" MIDDLE_LABEL="(optional)" STARTINCLINATION="-73;14;" ENDINCLINATION="-72;-111;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<node TEXT="EventSource" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_1695262203" CREATED="1447990248438" MODIFIED="1447994862314" HGAP="51" VSHIFT="-140">
<hook NAME="FreeNode"/>
<node TEXT="new EventSource()" ID="ID_1507429926" CREATED="1447990253127" MODIFIED="1447991016339">
<edge STYLE="bezier"/>
</node>
</node>
<node TEXT="Val / Var" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_244595518" CREATED="1447990235081" MODIFIED="1447994862315" HGAP="-74" VSHIFT="-75">
<hook NAME="FreeNode"/>
<node TEXT="changes" ID="ID_592004289" CREATED="1447990237876" MODIFIED="1447994394772">
<edge STYLE="bezier"/>
</node>
<node TEXT="values" ID="ID_1427903886" CREATED="1447990239944" MODIFIED="1447994397199">
<edge STYLE="bezier"/>
</node>
<node TEXT="invalidations" ID="ID_940442032" CREATED="1447990241909" MODIFIED="1447994399626">
<edge STYLE="bezier"/>
</node>
</node>
<node TEXT="EventStreams" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_1475374352" CREATED="1447990210006" MODIFIED="1447994862312" HGAP="134" VSHIFT="-29">
<hook NAME="FreeNode"/>
<node TEXT="eventsOf" ID="ID_290199428" CREATED="1447990212497" MODIFIED="1447991016336">
<edge STYLE="bezier"/>
</node>
<node TEXT="valuesOf" ID="ID_1539311778" CREATED="1447990217376" MODIFIED="1447991016337">
<edge STYLE="bezier"/>
</node>
<node TEXT="nonNullValuesOf" ID="ID_1784220558" CREATED="1447990225554" MODIFIED="1447991016337">
<edge STYLE="bezier"/>
</node>
<node TEXT="invalidationsOf" ID="ID_1366474000" CREATED="1447990231641" MODIFIED="1447991016338">
<edge STYLE="bezier"/>
</node>
<node TEXT="changesOf" ID="ID_1739854836" CREATED="1447990221639" MODIFIED="1447991016338">
<edge STYLE="bezier"/>
</node>
<node TEXT="sizesOf" ID="ID_1424920048" CREATED="1447990223224" MODIFIED="1447991016334">
<edge STYLE="bezier"/>
</node>
</node>
</node>
<node TEXT="Composition" LOCALIZED_STYLE_REF="AutomaticLayout.level,1" ID="ID_801039055" CREATED="1447990270103" MODIFIED="1447994714815" HGAP="734" VSHIFT="-135">
<cloud COLOR="#f0f0f0" SHAPE="ARC"/>
<hook NAME="FreeNode"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#ff0000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_199892113" STARTINCLINATION="7;49;" ENDINCLINATION="51;-86;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#ff0000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_751662814" MIDDLE_LABEL="(optional)" STARTINCLINATION="-2;31;" ENDINCLINATION="48;-1;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<node TEXT="EventStream&lt;Either&lt;?, ?&gt;&gt;" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_389179999" CREATED="1447993597596" MODIFIED="1447994848271" HGAP="776" VSHIFT="-402">
<hook NAME="FreeNode"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#ff0000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_97132340" MIDDLE_LABEL="unify" STARTINCLINATION="-72;57;" ENDINCLINATION="39;-62;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
</node>
<node TEXT="TaskStream" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_1395358890" CREATED="1447991713823" MODIFIED="1447994848271" HGAP="648" VSHIFT="-352">
<hook NAME="FreeNode"/>
</node>
<node TEXT="CompletionStageStream" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_938073560" CREATED="1447991651724" MODIFIED="1447994848271" HGAP="454" VSHIFT="-339">
<hook NAME="FreeNode"/>
</node>
<node TEXT="Tuple2&lt;EventStream, EventStream&gt;" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_1826488728" CREATED="1447991555326" MODIFIED="1447994848272" HGAP="242" VSHIFT="-211">
<hook NAME="FreeNode"/>
</node>
<node TEXT="AwaitingEventStream" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_1461645592" CREATED="1447991866452" MODIFIED="1447994848272" HGAP="138" VSHIFT="-73">
<hook NAME="FreeNode"/>
</node>
<node TEXT="EventStream" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_97132340" CREATED="1447990278506" MODIFIED="1447994848271" HGAP="701" VSHIFT="-31">
<hook NAME="FreeNode"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#000000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_1941293977" MIDDLE_LABEL="accumulative&#xa;forgetful&#xa;pausable&#xa;reducible&#xa;successionEnds&#xa;suppressible" STARTINCLINATION="-151;82;" ENDINCLINATION="14;-48;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#000000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_1826488728" MIDDLE_LABEL="fork" STARTINCLINATION="-81;-5;" ENDINCLINATION="89;36;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#000000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_938073560" MIDDLE_LABEL="mapToCompetionStage&#xa;supplyCompletionStage" STARTINCLINATION="-209;-104;" ENDINCLINATION="16;27;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#000000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_1395358890" MIDDLE_LABEL="mapToTask&#xa;supplyTask" STARTINCLINATION="-17;-94;" ENDINCLINATION="16;69;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#000000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_1461645592" MIDDLE_LABEL="reduceSuccessions&#xa;thenAccumulateFor&#xa;thenIgnoreFor&#xa;thenReduceFor&#xa;thenRetainLatestFor" STARTINCLINATION="-291;0;" ENDINCLINATION="205;-19;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#000000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_1606981411" MIDDLE_LABEL="suspenderOf" STARTINCLINATION="-12;111;" ENDINCLINATION="100;-100;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#000000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_310260200" MIDDLE_LABEL="toBinding" STARTINCLINATION="-145;105;" ENDINCLINATION="86;-69;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#000000" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_389179999" MIDDLE_LABEL="or&#xa;splitBy" STARTINCLINATION="50;-11;" ENDINCLINATION="5;17;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<node TEXT="Basic" ID="ID_255991212" CREATED="1447992678941" MODIFIED="1447993678446" HGAP="80">
<edge STYLE="bezier"/>
<node TEXT="cast" ID="ID_444008011" CREATED="1447990804730" MODIFIED="1447991199117">
<edge STYLE="bezier"/>
</node>
<node TEXT="conditionOn" ID="ID_1747385380" CREATED="1447990285492" MODIFIED="1447991199116">
<edge STYLE="bezier"/>
<node TEXT="conditionOnShowing" ID="ID_961460209" CREATED="1447991480209" MODIFIED="1447991482354"/>
</node>
<node TEXT="distinct" ID="ID_1001587261" CREATED="1447990814972" MODIFIED="1447991199118">
<edge STYLE="bezier"/>
</node>
<node TEXT="emitOn" ID="ID_1519741257" CREATED="1447991501415" MODIFIED="1447991502478">
<node TEXT="emitOnEach" ID="ID_570761946" CREATED="1447991502996" MODIFIED="1447991504619"/>
<node TEXT="emitBothOnEach" ID="ID_204982288" CREATED="1447991497592" MODIFIED="1447991501072"/>
</node>
<node TEXT="filter" ID="ID_391121211" CREATED="1447990288199" MODIFIED="1447991199117">
<edge STYLE="bezier"/>
</node>
<node TEXT="filterMap" ID="ID_545431987" CREATED="1447990289562" MODIFIED="1447991199117">
<edge STYLE="bezier"/>
</node>
<node TEXT="flatMap" ID="ID_69332464" CREATED="1447990294752" MODIFIED="1447991199118">
<edge STYLE="bezier"/>
</node>
<node TEXT="hook" ID="ID_141218508" CREATED="1447990292547" MODIFIED="1447991199117">
<edge STYLE="bezier"/>
</node>
<node TEXT="latestN" ID="ID_862581645" CREATED="1447991636516" MODIFIED="1447991638964"/>
<node TEXT="map" ID="ID_1857243115" CREATED="1447990291483" MODIFIED="1447991199117">
<edge STYLE="bezier"/>
</node>
<node TEXT="repeatOn" ID="ID_1232314083" CREATED="1447992006105" MODIFIED="1447992008500"/>
<node TEXT="retainLatestBetween" ID="ID_649044072" CREATED="1447992009422" MODIFIED="1447992012516"/>
<node TEXT="retainLatestUntilLater" ID="ID_1865821695" CREATED="1447992013365" MODIFIED="1447992017210"/>
<node TEXT="retainLatestWhen" ID="ID_577934226" CREATED="1447992022621" MODIFIED="1447992031489"/>
<node TEXT="queueBetween" ID="ID_360780057" CREATED="1447991807360" MODIFIED="1447991809482"/>
<node TEXT="queueUntilLater" ID="ID_685890339" CREATED="1447991809789" MODIFIED="1447991812319"/>
<node TEXT="supply" ID="ID_1271104031" CREATED="1447992071343" MODIFIED="1447992072874"/>
<node TEXT="threadBridge" ID="ID_275566541" CREATED="1447992270680" MODIFIED="1447992277083">
<node TEXT="threadBridgeFromFX" ID="ID_740406186" CREATED="1447992277433" MODIFIED="1447992282080"/>
<node TEXT="threadBridgeToFX" ID="ID_1981360670" CREATED="1447992282600" MODIFIED="1447992286351"/>
</node>
</node>
<node TEXT="suspendable-related" ID="ID_469007239" CREATED="1447992762045" MODIFIED="1447993155954" HGAP="56">
<edge STYLE="bezier"/>
<node TEXT="accumulate" ID="ID_45848307" CREATED="1447990751132" MODIFIED="1447991199117">
<edge STYLE="bezier"/>
<node TEXT="accumulateBetween" ID="ID_60854696" CREATED="1447991454312" MODIFIED="1447991457266"/>
<node TEXT="accumulateUntilLater" ID="ID_1416487198" CREATED="1447991458095" MODIFIED="1447991464044"/>
<node TEXT="accumulateWhen" ID="ID_108256930" CREATED="1447991467391" MODIFIED="1447991469371"/>
</node>
<node TEXT="pauseWhen" ID="ID_205893718" CREATED="1447991804564" MODIFIED="1447991806312"/>
<node TEXT="supressWhen" ID="ID_653403835" CREATED="1447992111911" MODIFIED="1447992116307"/>
<node TEXT="reduceBetween" ID="ID_1301074690" CREATED="1447991820454" MODIFIED="1447991823158"/>
<node TEXT="reduceUntilLater" ID="ID_1153022761" CREATED="1447991965622" MODIFIED="1447991968414"/>
<node TEXT="reduceWhen" ID="ID_1446326163" CREATED="1447991968730" MODIFIED="1447991970796"/>
</node>
<node TEXT="onRecurse..." ID="ID_1236939545" CREATED="1447993020831" MODIFIED="1447993158555" HGAP="68">
<edge STYLE="bezier"/>
<node TEXT="onRecurseAccumulate" ID="ID_1043643710" CREATED="1447991760162" MODIFIED="1447991763665"/>
<node TEXT="onRecurseQueue" ID="ID_1994327287" CREATED="1447991764060" MODIFIED="1447991767429"/>
<node TEXT="onRecurseReduce" ID="ID_960529983" CREATED="1447991767747" MODIFIED="1447991770635"/>
<node TEXT="onRecurseRetainLatest" ID="ID_242352220" CREATED="1447991770970" MODIFIED="1447991774982"/>
</node>
</node>
<node TEXT="SuspendableEventStream" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_1941293977" CREATED="1447990768440" MODIFIED="1447994848272" HGAP="189" VSHIFT="157">
<hook NAME="FreeNode"/>
</node>
<node TEXT="Binding" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_310260200" CREATED="1447992295918" MODIFIED="1447994848272" HGAP="448" VSHIFT="326">
<hook NAME="FreeNode"/>
</node>
<node TEXT="SuspenderStream" LOCALIZED_STYLE_REF="styles.subtopic" ID="ID_1606981411" CREATED="1447992134049" MODIFIED="1447994848272" HGAP="584" VSHIFT="363">
<hook NAME="FreeNode"/>
</node>
</node>
<node TEXT="Error Handling" LOCALIZED_STYLE_REF="AutomaticLayout.level,1" ID="ID_751662814" CREATED="1447994090391" MODIFIED="1447994631569" HGAP="398" VSHIFT="-26">
<hook NAME="FreeNode"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#29b501" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_199892113" STARTINCLINATION="10;21;" ENDINCLINATION="-116;0;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
</node>
<node TEXT="Subscribe" LOCALIZED_STYLE_REF="AutomaticLayout.level,1" ID="ID_199892113" CREATED="1447990309385" MODIFIED="1447994609292" HGAP="618" VSHIFT="79">
<cloud COLOR="#f0f0f0" SHAPE="ARC"/>
<hook NAME="FreeNode"/>
<arrowlink SHAPE="CUBIC_CURVE" COLOR="#1b02dd" WIDTH="2" TRANSPARENCY="80" FONT_SIZE="9" FONT_FAMILY="SansSerif" DESTINATION="ID_1803011927" STARTINCLINATION="-1;60;" ENDINCLINATION="-18;-22;" STARTARROW="NONE" ENDARROW="DEFAULT"/>
<node TEXT="subscribe" ID="ID_1530590359" CREATED="1447990312253" MODIFIED="1447991184385">
<edge STYLE="bezier"/>
</node>
<node TEXT="subscribeFor" ID="ID_1057172675" CREATED="1447990317442" MODIFIED="1447991184386">
<edge STYLE="bezier"/>
</node>
<node TEXT="subscribeForOne" ID="ID_413670360" CREATED="1447990320236" MODIFIED="1447991184386">
<edge STYLE="bezier"/>
</node>
<node TEXT="feedTo" ID="ID_600048489" CREATED="1447990322593" MODIFIED="1447991184386">
<edge STYLE="bezier"/>
</node>
<node TEXT="pin" ID="ID_920179664" CREATED="1447990324192" MODIFIED="1447991184386">
<edge STYLE="bezier"/>
</node>
</node>
<node TEXT="Unsubscribe" LOCALIZED_STYLE_REF="AutomaticLayout.level,1" ID="ID_1803011927" CREATED="1447994197404" MODIFIED="1447994635865" HGAP="586" VSHIFT="176"/>
</node>
</node>
</map>

Create Custom Elements

So is there a way to lets say extend from a BorderPane?

For example create a custom element containing a label and a image and textfield etc...
Then add this custom element to a GridPane (or whatever)

As far as i see my class has to be a View or Fragment but those i can't add to a Pane because their children have to be a Node.

Am I right that for now i would have to write in plain Javafx to get such a custom element?

Compatibility Problem with javafx-8.0.jar

Val<T> extends javafx.beans.value.ObservableValue. However, "javafx-8.0.jar" is using another version of javafx.beans.value.ObservableValue. So, There is a conflict happens whenever we use both jars in the same project.

Add a way to specify default error monitor

Now default monitor is Throwable::printStackTrace. The alternative one could be

  • some static handler (oh no! static mutable state),
  • Thread.getDefaultUncaughtExceptionHandler() (again mutable),
  • or set by SPI (seems good)

suggestion. code field.

hi Tomas , i implemented a basic command line functionality using a text field in my project. it thought it might be handy to make a codeFiled for use in command lines where you highlight the keywords of a command. one could use the code area to do this, but a code field makes more sense. i am not sure how much more work it is to make this, so it's just a suggestion!
best,
Maher

EasyBind vs ReactFX

It's a bit unclear what's relation between EasyBind and ReactFX. Now I'm using both of them in my project, and only recently I've discovered that ReactFX contains Val/Var in addition to EventStream. Is ReactFX aiming to replace EasyBind?

mouse shake events

hi Tomas, does reactFX support or enable detecting mouse shake, and press-and-shake events on FX nodes ? I haven't made myself well aquanted with the library as of yet, but wanted to check if this is doable. if so, where should I start-- how can one go about describing such an event.

org.reactfx.Change class lacks equals & hashCode methods

Hi,

I encountered an issue while using UndoFX, which makes use of ReactFX.

I created an EventStream of Change instances, thanks to EventStreams.changesOf method, that I gave to UndoFX. However, the library is unable to process it correctly because Change does not override equals.

I managed to overcome this limitation by using the map method to turn the Stream <Change> into a Stream <ComparableChange> (where ComparableChange is a new class I created).

It would be far easier if Change redefined directly equals and hashCode. What I propose is to consider two Change instances equal if both their old values and new values are equal.

If you agree I can submit a PR to fix the problem.

[Feature Request] Vetoable Listeners, LiveSet & LiveMap

Hello,

I have a scene graph-like data structure that heavily relies on observable properties to keep the components mutually updated. I would like to use listeners to prevent/veto invalid changes of properties. Using a Change(Performed)Listener to change back values is not satisfactory for two reasons:

  • Read-only properties could not veto.
  • The intermediate invalid value fired as Event could cause consistency issues elsewhere

Is there any way to improve Val and Var in the ReactFX library in a way that also allows for vetoable listeners? I understand that this would not work in interaction with FX properties as they do not support VetoListeners. But in my application the data-stucture is separate from the JavaFX Views so I would like to use the ReactFX properties as "standalone".

I have written my own little property library that has an additional listener that is listening to events before changes are performed (ChangeProposedListener). The problem is my library is non-lazy, will create memory leaks left and right and is over all not nearly as awesome and powerful as the ReactFX library (It was created in 2 hours....).

In addition to that I would also require both a vetoable LiveSet and LiveMap as they are used in the model as well.

I feel like this is too much to ask for. A the same time I believe many other people could use vetoability as well. Just look at the horrible abomination of a VetoableListDecorator that can be found in the JavaFX library.

cheers
Dirk

Create an annotation for boilerplate getter/setter/property methods

In GroovyFX, they have the @FXBindable annotation that turns this:

@FXBindable
String myString = "hello"

into this:

private SimpleStringProperty myString = new SimpleStringProperty("hello")
public void setMyString(String value) { myString.setValue(value) }
public String getMyString() { myString.getValue() }
public SimpleStringProperty myStringProperty() { myString }

Could something be done for Val and Var? Something like:

@ReactableVal
Double someDouble;

@ReactableVar
Integer someInteger = 4;

into

private Val<Double> someDouble;
public double getSomeDouble() { return someDouble.getValue); }
public Val<Double> someDoubleProperty() { return someDouble; }

private Var<Integer> someInteger =Var.newSimpleVar(4);
public int getSomeInteger() { return someInteger.getValue(); }
public void setSomeInteger(int value) { someInteger.setValue(value); }
public Var<Integer> someIntegerProperty() { return someInteger; }

or even

@AsFXProperty
private Var<Double> someDouble = Var.newSimpleVar(4.5);

that generates the setter/getter/property method

public int getSomeDouble() { return someDouble.getValue(); }
public void setSomeDouble(int value) { someDouble.setValue(value); }
public Var<Integer> someDoubleProperty() { return someDouble; }

Best API ReadMe ever!

The use of examples, the crisp explanations, zero bloat, zero confusion, everything about it. Bravo!

DistinctMappedList

It would be nice to see a DistinctMappedList<R> that allows you to take a source ObservableList<T>, and map each T item to R and produce an ObservableList<R> that only holds distinct instances.

ObservableList<String> values = 
    FXCollections.observableArrayList("Alpha","Beta","Gamma","Delta","Epsilon");

DistinctMappedList<Integer> distinctLengths = new DistinctMappedList<>(values,v -> v.length());

distinctLengths would then only contain elements 5, 4, and 7 until more elements are added to values. A use case this would have been helpful is an Excel-like table filter control I contributed to ControlsFX.

I thought about adding this to the RxJavaFX project. But I think ReactFX would be a better home for something like this. RxJavaFX is not seeking to improve JavaFX but rather simply enable interoperability between RxJava and JavaFX.

NullPointerException in javac

Hi there, this is just to document a bug that affects this project in the hope it saves someone some work (and not have to deal with the Oracle bug tracker - damn that thing is annoying). Feel free to close if not useful.
Trying to compile the project I get the attached exception, which I think is triggered by the linked bug. HTH.

http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8062253
(if link just loads the search page use http://webcache.googleusercontent.com/search?q=cache:Q2wOgDJBmZEJ:bugs.java.com/bugdatabase/view_bug.do%3Fbug_id%3D8062253+&cd=1&hl=de&ct=clnk&gl=de)

Code at b369264
JDK 1.8.0_31-b13 on Linux x86.

:reactfx:compileJava
An exception has occurred in the compiler (1.8.0_31). Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport)  after checking the Bug Parade for duplicates. Include your program and the following diagnostic in your report.  Thank you.
java.lang.NullPointerException
    at com.sun.tools.javac.code.Types.isConvertible(Types.java:290)
    at com.sun.tools.javac.comp.Check.assertConvertible(Check.java:922)
    at com.sun.tools.javac.comp.Check.checkMethod(Check.java:876)
    at com.sun.tools.javac.comp.Attr.checkMethod(Attr.java:3838)
    at com.sun.tools.javac.comp.Attr.checkIdInternal(Attr.java:3615)
    at com.sun.tools.javac.comp.Attr.checkMethodIdInternal(Attr.java:3522)
    at com.sun.tools.javac.comp.Attr.checkMethodId(Attr.java:3501)
    at com.sun.tools.javac.comp.Attr.checkId(Attr.java:3488)
    at com.sun.tools.javac.comp.Attr.visitIdent(Attr.java:3237)
    at com.sun.tools.javac.tree.JCTree$JCIdent.accept(JCTree.java:2011)
    at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:607)
    at com.sun.tools.javac.comp.Attr.visitApply(Attr.java:1843)
    at com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1465)
    at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:607)
    at com.sun.tools.javac.comp.Attr.visitReturn(Attr.java:1704)
    at com.sun.tools.javac.tree.JCTree$JCReturn.accept(JCTree.java:1384)
    at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:607)
    at com.sun.tools.javac.comp.Attr.attribStat(Attr.java:676)
    at com.sun.tools.javac.comp.Attr.attribStats(Attr.java:692)
    at com.sun.tools.javac.comp.Attr.visitBlock(Attr.java:1142)
    at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:909)
    at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:607)
    at com.sun.tools.javac.comp.Attr.attribStat(Attr.java:676)
    at com.sun.tools.javac.comp.Attr.visitMethodDef(Attr.java:1035)
    at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:778)
    at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:607)
    at com.sun.tools.javac.comp.Attr.attribStat(Attr.java:676)
    at com.sun.tools.javac.comp.Attr.attribClassBody(Attr.java:4342)
    at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:4252)
    at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:4181)
    at com.sun.tools.javac.comp.Attr.attrib(Attr.java:4156)
    at com.sun.tools.javac.main.JavaCompiler.attribute(JavaCompiler.java:1248)
    at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:901)
    at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:860)
    at com.sun.tools.javac.main.Main.compile(Main.java:523)
    at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129)
    at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138)
    at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:42)
    at org.gradle.api.internal.tasks.compile.JdkJavaCompiler.execute(JdkJavaCompiler.java:35)
    at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.delegateAndHandleErrors(NormalizingJavaCompiler.java:97)
    at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:50)
    at org.gradle.api.internal.tasks.compile.NormalizingJavaCompiler.execute(NormalizingJavaCompiler.java:36)
    at org.gradle.api.internal.tasks.compile.CleaningJavaCompilerSupport.execute(CleaningJavaCompilerSupport.java:34)
    at org.gradle.api.internal.tasks.compile.CleaningJavaCompilerSupport.execute(CleaningJavaCompilerSupport.java:25)
    at org.gradle.api.tasks.compile.JavaCompile.performCompilation(JavaCompile.java:158)
    at org.gradle.api.tasks.compile.JavaCompile.compile(JavaCompile.java:138)
    at org.gradle.api.tasks.compile.JavaCompile.compile(JavaCompile.java:92)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:63)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$IncrementalTaskAction.doExecute(AnnotationProcessingTaskFactory.java:235)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:211)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$IncrementalTaskAction.execute(AnnotationProcessingTaskFactory.java:222)
    at org.gradle.api.internal.project.taskfactory.AnnotationProcessingTaskFactory$StandardTaskAction.execute(AnnotationProcessingTaskFactory.java:200)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:80)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:61)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.PostExecutionAnalysisTaskExecuter.execute(PostExecutionAnalysisTaskExecuter.java:35)
    at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:64)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:42)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:53)
    at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
    at org.gradle.api.internal.AbstractTask.executeWithoutThrowingTaskFailure(AbstractTask.java:305)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.executeTask(AbstractTaskPlanExecutor.java:79)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:63)
    at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:51)
    at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:23)
    at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:88)
    at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:29)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
    at org.gradle.execution.DefaultBuildExecuter.access$200(DefaultBuildExecuter.java:23)
    at org.gradle.execution.DefaultBuildExecuter$2.proceed(DefaultBuildExecuter.java:68)
    at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:62)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:55)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:149)
    at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:106)
    at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:86)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$DefaultBuildController.run(InProcessBuildActionExecuter.java:80)
    at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:33)
    at org.gradle.launcher.cli.ExecuteBuildAction.run(ExecuteBuildAction.java:24)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:36)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
    at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:51)
    at org.gradle.internal.Actions$RunnableActionAdapter.execute(Actions.java:171)
    at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:237)
    at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:210)
    at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:35)
    at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:24)
    at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:206)
    at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:169)
    at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
    at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
    at org.gradle.launcher.Main.doAction(Main.java:33)
    at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:54)
    at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:35)
    at org.gradle.launcher.GradleMain.main(GradleMain.java:23)

Add more documentation to EventStream ?

I wanted to add more documentation to EventStream that explains a few things:

  • The lifecycle of an EventStream object: creation, subscribe, unsubscribe
  • Subscription and how and why to use it to prevent memory leaks
  • Different ways to create an EventStream object
  • The different composition methods that are grouped together by common categories:
    • time-related
    • change-related
    • suspendable-related, etc.

When I first looked at this library, the Readme file helped show the usefulness of EventStream objects in general. Since EventStream, as opposed to Val/Var or LiveList, was demonstrated in the Readme file, I looked at that first. However, I didn't immediately know where to go from there, much less how to use the code in my program. My guess is that others who wish to know more about how to use this library would follow a similar path, hence why I think the documentation should be added to EventStream.

Add a ScrollPane that uses Var<Double> instead of DoubleProperty

I've used the simulated recursive binding idea you once suggested.

VirtualizedScrollPane vsPane = ...;
ScrollPane pane = new ScrollPane();
Val.wrap(pane.vvalueProperty()).values().feedTo(vsPane.estimatedScrollYProperty());
vsPane.estimatedScrollYProperty().values().feedTo(pane.vvalueProperty());

98% of the time, it works fine. There is one situation in my code, however, where the underlying ScrollPane will change it's vvalue to 1.0 (I have no idea why this only occurs in one specific case but never any time else). Additionally, this incorrect value, although fed to the VirtualizedScrollPane's property, does not actually update vsPane's scrollY value (I also have no idea why that doesn't occur).

So, I propose adding a custom ScrollPane to this library that uses Var instead of DoubleProperty for its properties. To limit the scope to this specific need (where others can add more features to it later), it wouldn't account for touch-based scrolling.

SimpleVar notifies Observers with wrong value

It seems I've found a nasty bug in 2.0-M5. When setValue(newVal) is called on SimpleVar, it updates the field value on SimpleVar, then calls invalidate() from its super class ValBase. This in turn notifies all Observers using its own field value. But the later only gets updated on getValue(), which was never called.

Maybe I'm missing something here. I don't get that AccuMap thing (yet). I'll try to come up with a test case later.

TableView setCellValueFactory() goes crazy with EventStream binding

Hey guys, I have found a problem in using reactive programming with JavaFX. The TableView TableColumn setCellValueFactory does not like being mapped to an EventStream binding. I noticed I can get an Observable in RxJava to work sometimes, but the moment any threads, schedulers, RxJava to ReactFX conversions, or Platform.runLater() calls are introduced chaos ensues. There is no way to effectively bring the subscription to the JavaFX thread without more complications. In many of these scenarios, the TableView keeps calling the cellValueFactory infinitely and re-emitting the values each time, but the values never make it to the table. Other times, no emissions make it at all.

I've spammed SO with a few questions on the odd reactive behaviors I've been finding with TableView. TableColumn setCellValueFactory. Is there an easy solution for this?

http://stackoverflow.com/questions/30804401/javafx-and-rxjava-tableview-infinitely-calling-setcellvaluefactory

http://stackoverflow.com/questions/30931384/rxjava-javafx-property

compiler warning on util.Lists in eclipse compiler

Hi Mikula,
There is a compiler warning in eclipse at util/Lists.java because of jdt.
a type cast added.
Is this OK?

diff --git a/reactfx/src/main/java/org/reactfx/util/Lists.java b/reactfx/src/main/java/org/reactfx/util/Lists.java
index 2140a23..5761204 100644
--- a/reactfx/src/main/java/org/reactfx/util/Lists.java
+++ b/reactfx/src/main/java/org/reactfx/util/Lists.java
@@ -299,7 +299,7 @@ class ListConcatenation<E> extends AbstractList<E> {
     static <E> List<E> create(List<List<? extends E>> lists) {
         return lists.stream()
             .filter(l -> !l.isEmpty())
-            .map(l -> {
+            .<FingerTree>map(l -> {
                 @SuppressWarnings("unchecked") // cast safe because l is unmodifiable
                 List<E> lst = (List<E>) l;
                 return lst instanceof ListConcatenation

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.