浏览代码

Add a version of ObservableController for JFormattedTxtField.
TODO: Find a way to merge these together.

Sam Jaffe 5 年之前
父节点
当前提交
5a8ece5553

+ 1 - 1
pom.xml

@@ -4,7 +4,7 @@
 
   <groupId>org.leumasjaffe</groupId>
   <artifactId>observer</artifactId>
-  <version>0.5.1</version>
+  <version>0.6.0</version>
   <packaging>jar</packaging>
 
   <name>observer</name>

+ 32 - 17
src/main/lombok/org/leumasjaffe/observer/ObservableController.java

@@ -39,20 +39,6 @@ public class ObservableController<S extends JTextComponent, T extends Observable
 		AnyActionDocumentListener.skipEmpty(comp, evt -> accept( ) );
 	}
 	
-	public ObservableController(final S comp, final Function<T, String> get,
-			final BiConsumer<T, String> set, final BiConsumer<? super S, String> update) {
-		super(comp, (c, u) -> {
-			if (c.getText().equals(get.apply(u))) return;
-			update.accept(c, get.apply(u));
-		});
-		this.func = (t, u) -> {
-			if (t.equals(get.apply(u))) return false;
-			set.accept(u, t);
-			return true;
-		};
-		AnyActionDocumentListener.skipEmpty(comp, evt -> accept( ) );
-	}
-	
 	/**
 	 * Construct an ObservableController that modifies its model whenever the linked
 	 * JTextComponent is updated, including deleting all of the text.
@@ -71,10 +57,39 @@ public class ObservableController<S extends JTextComponent, T extends Observable
 				evt -> accept( ) );
 	}
 	
+	/**
+	 * Construct an ObservableController that ties together a JTextComponent with
+	 * the member of an observable object such that updates flow between the model
+	 * and the JTextComponent. Ignores changes when the text document is empty.
+	 * @param comp The component being watched
+	 * @param get A model getter function to be called on the observed object
+	 * @param set A model setter function to be called on the observed object
+	 */
+	public ObservableController(final S comp, final Function<T, String> get,
+			final BiConsumer<T, String> set) {
+		super(comp, (c, u) -> {
+			if (c.getText().equals(get.apply(u))) return;
+			c.setText(get.apply(u));
+		});
+		this.func = (t, u) -> {
+			if (t.equals(get.apply(u))) return false;
+			set.accept(u, t);
+			return true;
+		};
+		AnyActionDocumentListener.skipEmpty(comp, evt -> accept( ) );
+	}
+	
+	/**
+	 * Construct an ObservableController that ties together a JTextComponent with
+	 * the member of an observable object such that updates flow between the model
+	 * and the JTextComponent, including deleting all of the text.
+	 * @param comp The component being watched
+	 * @param get A model getter function to be called on the observed object
+	 * @param set A model setter function to be called on the observed object
+	 */
 	public ObservableController(final S comp, final Function<T, String> get,
-			final BiConsumer<T, String> set, final BiConsumer<? super S, String> update,
-			final Consumer<T> onEmpty) {
-		super(comp, (c, u) -> update.accept(c, get.apply(u)));
+			final BiConsumer<T, String> set, final Consumer<T> onEmpty) {
+		super(comp, (c, u) -> c.setText(get.apply(u)));
 		this.func = (t, u) -> {
 			if (t.equals(get.apply(u))) return false;
 			set.accept(u, t);

+ 114 - 0
src/main/lombok/org/leumasjaffe/observer/ObservableFmtController.java

@@ -0,0 +1,114 @@
+package org.leumasjaffe.observer;
+
+import java.util.Objects;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import javax.swing.JFormattedTextField;
+
+import org.leumasjaffe.event.AnyActionDocumentListener;
+
+import lombok.experimental.FieldDefaults;
+import lombok.AccessLevel;
+
+/**
+ * An ObservableListener object that also provides an update link in the other direction.
+ * The normal Listener updates some (UI) component when the model object updates. This
+ * object adds the feature to update the model from an update to UI component.
+ *
+ * This allows you to have a formatted input field attached to your model (like with a JavaBean),
+ * that also fires off notifications to other interested listeners when you edit the text.
+ */
+@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+public class ObservableFmtController<S extends JFormattedTextField, T extends Observable, V> extends ObservableListener<S, T> {
+	BiFunction<V, T, Boolean> func;
+
+	/**
+	 * Construct an ObservableController that modifies its model whenever the linked
+	 * JFormattedTextField is updated. Ignores changes when the text document is empty.
+	 * @param comp A GUI object that is to be coupled with the model
+	 * @param func An update function to modify the model from text content
+	 * @param update The standard callback to set the component when the model changes elsewhere
+	 */
+	public ObservableFmtController(final S comp, final BiFunction<V, T, Boolean> func,
+			final BiConsumer<? super S, ? super T> update) {
+		super(comp, update);
+		this.func = func;
+		AnyActionDocumentListener.skipEmpty(comp, evt -> accept( ) );
+	}
+	
+	/**
+	 * Construct an ObservableController that modifies its model whenever the linked
+	 * JFormattedTextField is updated, including deleting all of the text.
+	 * @param comp A GUI object that is to be coupled with the model
+	 * @param func An update function to modify the model from text content
+	 * @param update The standard callback to set the component when the model changes elsewhere
+	 * @param onEmpty A special handler to set model value when we delete the content of the
+	 * component.
+	 */
+	public ObservableFmtController(final S comp, final BiFunction<V, T, Boolean> func,
+			final BiConsumer<? super S, ? super T> update, final Consumer<T> onEmpty) {
+		super(comp, update);
+		this.func = func;
+		AnyActionDocumentListener.emptyOrText( comp, 
+				e -> onEmpty.accept( impl.getModel() ), 
+				evt -> accept( ) );
+	}
+	
+	/**
+	 * Construct an ObservableController that ties together a JTextComponent with
+	 * the member of an observable object such that updates flow between the model
+	 * and the JFormattedTextField. Ignores changes when the text document is empty.
+	 * @param comp The component being watched
+	 * @param get A model getter function to be called on the observed object
+	 * @param set A model setter function to be called on the observed object
+	 */
+	public ObservableFmtController(final S comp, final Function<T, V> get,
+			final BiConsumer<T, V> set) {
+		super(comp, (c, u) -> {
+			if (c.getValue().equals(get.apply(u))) return;
+			c.setValue(get.apply(u));
+		});
+		this.func = (t, u) -> {
+			if (t.equals(get.apply(u))) return false;
+			set.accept(u, t);
+			return true;
+		};
+		AnyActionDocumentListener.skipEmpty(comp, evt -> accept( ) );
+	}
+	
+	/**
+	 * Construct an ObservableController that ties together a JTextComponent with
+	 * the member of an observable object such that updates flow between the model
+	 * and the JFormattedTextField, including deleting all of the text.
+	 * @param comp The component being watched
+	 * @param get A model getter function to be called on the observed object
+	 * @param set A model setter function to be called on the observed object
+	 */
+	public ObservableFmtController(final S comp, final Function<T, V> get,
+			final BiConsumer<T, V> set, final Consumer<T> onEmpty) {
+		super(comp, (c, u) -> c.setValue(get.apply(u)));
+		this.func = (t, u) -> {
+			if (t.equals(get.apply(u))) return false;
+			set.accept(u, t);
+			return true;
+		};
+		AnyActionDocumentListener.emptyOrText( comp, 
+				e -> onEmpty.accept( impl.getModel() ), 
+				evt -> accept( ) );
+	}
+
+	@SuppressWarnings("unchecked")
+	private boolean update() {
+		return func.apply( (V) impl.getComponent().getValue( ), impl.getModel() );
+	}
+
+	private void accept() {
+		Objects.requireNonNull( impl.getModel() );
+		if ( update( ) ) {
+			impl.notifySubscribers(impl.getModel());
+		}
+	}
+}