mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-22 22:45:21 +01:00
fix missing formatting updates
This commit is contained in:
parent
3c67b08ff9
commit
096f4c01e7
12 changed files with 298 additions and 266 deletions
|
@ -44,10 +44,9 @@ It is the JavaFX controller associated with the FXML view, but we don't use the
|
||||||
association with the classical MVC controller. It gets created by the JavaFX framework (FXMLLoader) and also the
|
association with the classical MVC controller. It gets created by the JavaFX framework (FXMLLoader) and also the
|
||||||
setup with the FXML view is done by the framework.
|
setup with the FXML view is done by the framework.
|
||||||
|
|
||||||
It knows the presentation model but not the model.
|
It knows the presentation model (via Guice injection) but not the model.
|
||||||
|
|
||||||
#####Responsibility:
|
#####Responsibility:
|
||||||
* Creates presentation model and passes model from Guice injection to the presenter (might be changed).
|
|
||||||
* Setup binding for updates from PM to view elements (also bidirectional for used for input data).
|
* Setup binding for updates from PM to view elements (also bidirectional for used for input data).
|
||||||
* Those binding are only simple bindings to plain presenter properties, no logical bindings.
|
* Those binding are only simple bindings to plain presenter properties, no logical bindings.
|
||||||
* Listens to UI events (Actions) from UI controls and calls method in presentation model.
|
* Listens to UI events (Actions) from UI controls and calls method in presentation model.
|
||||||
|
@ -62,7 +61,7 @@ It knows the presentation model but not the model.
|
||||||
It is the abstraction/presentation of the view.
|
It is the abstraction/presentation of the view.
|
||||||
Can be used for unit testing.
|
Can be used for unit testing.
|
||||||
|
|
||||||
It knows the model but it does not know the CodeBehind (View)
|
It knows the model (via Guice injection) but it does not know the CodeBehind (View)
|
||||||
|
|
||||||
#####Responsibility:
|
#####Responsibility:
|
||||||
* Holds the state of the view/CB
|
* Holds the state of the view/CB
|
||||||
|
|
|
@ -7,11 +7,16 @@ import java.util.ResourceBundle;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If caching is used for loader we use the CachedViewController for turning the controller into sleep mode if not
|
||||||
|
* active and awake it at reactivation.
|
||||||
|
* * @param <T> The PresentationModel used in that class
|
||||||
|
*/
|
||||||
public class CachedCodeBehind<T extends PresentationModel> extends CodeBehind<T> {
|
public class CachedCodeBehind<T extends PresentationModel> extends CodeBehind<T> {
|
||||||
private static final Logger log = LoggerFactory.getLogger(CachedCodeBehind.class);
|
private static final Logger log = LoggerFactory.getLogger(CachedCodeBehind.class);
|
||||||
|
|
||||||
public CachedCodeBehind(T pm) {
|
public CachedCodeBehind(T presentationModel) {
|
||||||
super(pm);
|
super(presentationModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,7 +40,7 @@ public class CachedCodeBehind<T extends PresentationModel> extends CodeBehind<T>
|
||||||
});
|
});
|
||||||
|
|
||||||
activate();
|
activate();
|
||||||
pm.initialized();
|
presentationModel.initialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -45,7 +50,7 @@ public class CachedCodeBehind<T extends PresentationModel> extends CodeBehind<T>
|
||||||
log.trace("Lifecycle: activate " + this.getClass().getSimpleName());
|
log.trace("Lifecycle: activate " + this.getClass().getSimpleName());
|
||||||
if (childController instanceof CachedViewController) ((CachedViewController) childController).activate();
|
if (childController instanceof CachedViewController) ((CachedViewController) childController).activate();
|
||||||
|
|
||||||
pm.activate();
|
presentationModel.activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,7 +60,7 @@ public class CachedCodeBehind<T extends PresentationModel> extends CodeBehind<T>
|
||||||
log.trace("Lifecycle: deactivate " + this.getClass().getSimpleName());
|
log.trace("Lifecycle: deactivate " + this.getClass().getSimpleName());
|
||||||
if (childController instanceof CachedViewController) ((CachedViewController) childController).deactivate();
|
if (childController instanceof CachedViewController) ((CachedViewController) childController).deactivate();
|
||||||
|
|
||||||
pm.deactivate();
|
presentationModel.deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,7 +72,7 @@ public class CachedCodeBehind<T extends PresentationModel> extends CodeBehind<T>
|
||||||
super.terminate();
|
super.terminate();
|
||||||
|
|
||||||
deactivate();
|
deactivate();
|
||||||
pm.terminate();
|
presentationModel.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ import org.slf4j.LoggerFactory;
|
||||||
* If caching is used for loader we use the CachedViewController for turning the controller into sleep mode if not
|
* If caching is used for loader we use the CachedViewController for turning the controller into sleep mode if not
|
||||||
* active and awake it at reactivation.
|
* active and awake it at reactivation.
|
||||||
*/
|
*/
|
||||||
|
// for new PM pattern use CachedCodeBehind
|
||||||
|
@Deprecated
|
||||||
public abstract class CachedViewController extends ViewController {
|
public abstract class CachedViewController extends ViewController {
|
||||||
private static final Logger log = LoggerFactory.getLogger(CachedViewController.class);
|
private static final Logger log = LoggerFactory.getLogger(CachedViewController.class);
|
||||||
|
|
||||||
|
|
|
@ -11,25 +11,26 @@ import javafx.scene.*;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non caching version for code behind classes using the PM pattern
|
||||||
|
*
|
||||||
|
* @param <T> The PresentationModel used in that class
|
||||||
|
*/
|
||||||
public class CodeBehind<T extends PresentationModel> implements Initializable {
|
public class CodeBehind<T extends PresentationModel> implements Initializable {
|
||||||
private static final Logger log = LoggerFactory.getLogger(CodeBehind.class);
|
private static final Logger log = LoggerFactory.getLogger(CodeBehind.class);
|
||||||
|
|
||||||
protected T pm;
|
protected T presentationModel;
|
||||||
protected ViewController childController;
|
protected ViewController childController;
|
||||||
protected ViewController parentController;
|
protected ViewController parentController;
|
||||||
@FXML protected Parent root;
|
@FXML protected Parent root;
|
||||||
|
|
||||||
public CodeBehind(T pm) {
|
public CodeBehind(T presentationModel) {
|
||||||
this.pm = pm;
|
this.presentationModel = presentationModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CodeBehind() {
|
public CodeBehind() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public T pm() {
|
|
||||||
return (T) pm;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get called form GUI framework when the UI is ready.
|
* Get called form GUI framework when the UI is ready.
|
||||||
*
|
*
|
||||||
|
@ -45,7 +46,7 @@ public class CodeBehind<T extends PresentationModel> implements Initializable {
|
||||||
if (oldValue != null && newValue == null) terminate();
|
if (oldValue != null && newValue == null) terminate();
|
||||||
});
|
});
|
||||||
|
|
||||||
pm.initialized();
|
presentationModel.initialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,7 +57,7 @@ public class CodeBehind<T extends PresentationModel> implements Initializable {
|
||||||
log.trace("Lifecycle: terminate " + this.getClass().getSimpleName());
|
log.trace("Lifecycle: terminate " + this.getClass().getSimpleName());
|
||||||
if (childController != null) childController.terminate();
|
if (childController != null) childController.terminate();
|
||||||
|
|
||||||
pm.terminate();
|
presentationModel.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,11 +2,7 @@ package io.bitsquare.gui;
|
||||||
|
|
||||||
public class PresentationModel<T extends UIModel> {
|
public class PresentationModel<T extends UIModel> {
|
||||||
|
|
||||||
private T model;
|
protected T model;
|
||||||
|
|
||||||
public T model() {
|
|
||||||
return (T) model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PresentationModel(T model) {
|
public PresentationModel(T model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
|
|
|
@ -31,6 +31,8 @@ import org.slf4j.LoggerFactory;
|
||||||
/**
|
/**
|
||||||
* Base class for all controllers.
|
* Base class for all controllers.
|
||||||
*/
|
*/
|
||||||
|
// for new PM pattern use CodeBehind
|
||||||
|
@Deprecated
|
||||||
public abstract class ViewController implements Initializable {
|
public abstract class ViewController implements Initializable {
|
||||||
private static final Logger log = LoggerFactory.getLogger(ViewController.class);
|
private static final Logger log = LoggerFactory.getLogger(ViewController.class);
|
||||||
|
|
||||||
|
|
|
@ -35,11 +35,13 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TextField with validation support. Validation is executed on the Validator object.
|
* TextField with validation support.
|
||||||
* In case of a invalid result we display a error message with a PopOver.
|
* In case the isValid property in amountValidationResultProperty get set to false we display a red border and an error
|
||||||
* The position is derived from the textField or if set from the errorPopupLayoutReference object.
|
* message within the errorMessageDisplay placed on the right of the text field.
|
||||||
* <p>
|
* The errorMessageDisplay gets closed when the ValidatingTextField instance gets removed from the scene graph or when
|
||||||
* That class implements just what we need for the moment. It is not intended as a general purpose library class.
|
* hideErrorMessageDisplay() is called.
|
||||||
|
* There can be only 1 errorMessageDisplays at a time we use static field for it.
|
||||||
|
* The position is derived from the position of the textField itself or if set from the layoutReference node.
|
||||||
*/
|
*/
|
||||||
public class ValidatingTextField extends TextField {
|
public class ValidatingTextField extends TextField {
|
||||||
private static final Logger log = LoggerFactory.getLogger(ValidatingTextField.class);
|
private static final Logger log = LoggerFactory.getLogger(ValidatingTextField.class);
|
||||||
|
@ -49,17 +51,17 @@ public class ValidatingTextField extends TextField {
|
||||||
final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>(new
|
final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>(new
|
||||||
InputValidator.ValidationResult(true));
|
InputValidator.ValidationResult(true));
|
||||||
|
|
||||||
private static PopOver popOver;
|
private static PopOver errorMessageDisplay;
|
||||||
private Region errorPopupLayoutReference = this;
|
private Region layoutReference = this;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Static
|
// Static
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public static void hidePopover() {
|
public static void hideErrorMessageDisplay() {
|
||||||
if (popOver != null)
|
if (errorMessageDisplay != null)
|
||||||
popOver.hide();
|
errorMessageDisplay.hide();
|
||||||
}
|
}
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
|
@ -73,17 +75,16 @@ public class ValidatingTextField extends TextField {
|
||||||
setEffect(newValue.isValid ? null : invalidEffect);
|
setEffect(newValue.isValid ? null : invalidEffect);
|
||||||
|
|
||||||
if (newValue.isValid)
|
if (newValue.isValid)
|
||||||
hidePopover();
|
hideErrorMessageDisplay();
|
||||||
else
|
else
|
||||||
applyErrorMessage(newValue);
|
applyErrorMessage(newValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
sceneProperty().addListener((ov, oldValue, newValue) -> {
|
sceneProperty().addListener((ov, oldValue, newValue) -> {
|
||||||
// we got removed from the scene
|
// we got removed from the scene so hide the popup (if open)
|
||||||
// lets hide an open popup
|
|
||||||
if (newValue == null)
|
if (newValue == null)
|
||||||
hidePopover();
|
hideErrorMessageDisplay();
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -99,11 +100,11 @@ public class ValidatingTextField extends TextField {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param errorPopupLayoutReference The node used as reference for positioning. If not set explicitely the
|
* @param layoutReference The node used as reference for positioning. If not set explicitely the
|
||||||
* ValidatingTextField instance is used.
|
* ValidatingTextField instance is used.
|
||||||
*/
|
*/
|
||||||
public void setErrorPopupLayoutReference(Region errorPopupLayoutReference) {
|
public void setLayoutReference(Region layoutReference) {
|
||||||
this.errorPopupLayoutReference = errorPopupLayoutReference;
|
this.layoutReference = layoutReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,25 +123,26 @@ public class ValidatingTextField extends TextField {
|
||||||
|
|
||||||
private void applyErrorMessage(InputValidator.ValidationResult validationResult) {
|
private void applyErrorMessage(InputValidator.ValidationResult validationResult) {
|
||||||
if (validationResult.isValid) {
|
if (validationResult.isValid) {
|
||||||
if (popOver != null) {
|
if (errorMessageDisplay != null) {
|
||||||
popOver.hide();
|
errorMessageDisplay.hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (popOver == null)
|
if (errorMessageDisplay == null)
|
||||||
createErrorPopOver(validationResult.errorMessage);
|
createErrorPopOver(validationResult.errorMessage);
|
||||||
else
|
else
|
||||||
((Label) popOver.getContentNode()).setText(validationResult.errorMessage);
|
((Label) errorMessageDisplay.getContentNode()).setText(validationResult.errorMessage);
|
||||||
|
|
||||||
popOver.show(getScene().getWindow(), getErrorPopupPosition().getX(), getErrorPopupPosition().getY());
|
errorMessageDisplay.show(getScene().getWindow(), getErrorPopupPosition().getX(),
|
||||||
|
getErrorPopupPosition().getY());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Point2D getErrorPopupPosition() {
|
private Point2D getErrorPopupPosition() {
|
||||||
Window window = getScene().getWindow();
|
Window window = getScene().getWindow();
|
||||||
Point2D point;
|
Point2D point;
|
||||||
point = errorPopupLayoutReference.localToScene(0, 0);
|
point = layoutReference.localToScene(0, 0);
|
||||||
double x = point.getX() + window.getX() + errorPopupLayoutReference.getWidth() + 20;
|
double x = point.getX() + window.getX() + layoutReference.getWidth() + 20;
|
||||||
double y = point.getY() + window.getY() + Math.floor(getHeight() / 2);
|
double y = point.getY() + window.getY() + Math.floor(getHeight() / 2);
|
||||||
return new Point2D(x, y);
|
return new Point2D(x, y);
|
||||||
}
|
}
|
||||||
|
@ -151,9 +153,9 @@ public class ValidatingTextField extends TextField {
|
||||||
errorLabel.setId("validation-error");
|
errorLabel.setId("validation-error");
|
||||||
errorLabel.setPadding(new Insets(0, 10, 0, 10));
|
errorLabel.setPadding(new Insets(0, 10, 0, 10));
|
||||||
|
|
||||||
popOver = new PopOver(errorLabel);
|
ValidatingTextField.errorMessageDisplay = new PopOver(errorLabel);
|
||||||
popOver.setDetachable(false);
|
ValidatingTextField.errorMessageDisplay.setDetachable(false);
|
||||||
popOver.setArrowIndent(5);
|
ValidatingTextField.errorMessageDisplay.setArrowIndent(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -78,7 +78,7 @@ public class TradeController extends CachedViewController {
|
||||||
|
|
||||||
//TODO update to new verison
|
//TODO update to new verison
|
||||||
((TabPane) root).getSelectionModel().selectedIndexProperty().addListener((observableValue) ->
|
((TabPane) root).getSelectionModel().selectedIndexProperty().addListener((observableValue) ->
|
||||||
Platform.runLater(ValidatingTextField::hidePopover));
|
Platform.runLater(ValidatingTextField::hideErrorMessageDisplay));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -58,10 +58,9 @@ public class CreateOfferCB extends CachedCodeBehind<CreateOfferPM> {
|
||||||
// Constructor
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
//TODO find a better solution, handle at base class?
|
|
||||||
@Inject
|
@Inject
|
||||||
public CreateOfferCB(CreateOfferModel model) {
|
CreateOfferCB(CreateOfferPM presentationModel) {
|
||||||
super(new CreateOfferPM(model));
|
super(presentationModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,7 +75,12 @@ public class CreateOfferCB extends CachedCodeBehind<CreateOfferPM> {
|
||||||
setupBindings();
|
setupBindings();
|
||||||
setupListeners();
|
setupListeners();
|
||||||
configTextFieldValidators();
|
configTextFieldValidators();
|
||||||
balanceTextField.setup(pm().getWalletFacade(), pm().address.get());
|
balanceTextField.setup(presentationModel.getWalletFacade(), presentationModel.address.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void activate() {
|
||||||
|
super.activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,13 +91,18 @@ public class CreateOfferCB extends CachedCodeBehind<CreateOfferPM> {
|
||||||
if (parentController != null) ((TradeController) parentController).onCreateOfferViewRemoved();
|
if (parentController != null) ((TradeController) parentController).onCreateOfferViewRemoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void terminate() {
|
||||||
|
super.terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Public methods
|
// Public methods (called form other views/CB)
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void setOrderBookFilter(OrderBookFilter orderBookFilter) {
|
public void setOrderBookFilter(OrderBookFilter orderBookFilter) {
|
||||||
pm().setOrderBookFilter(orderBookFilter);
|
presentationModel.setOrderBookFilter(orderBookFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,13 +112,11 @@ public class CreateOfferCB extends CachedCodeBehind<CreateOfferPM> {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void onPlaceOffer() {
|
public void onPlaceOffer() {
|
||||||
pm().placeOffer();
|
presentationModel.placeOffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void onClose() {
|
public void onClose() {
|
||||||
pm().close();
|
|
||||||
|
|
||||||
TabPane tabPane = ((TabPane) (root.getParent().getParent()));
|
TabPane tabPane = ((TabPane) (root.getParent().getParent()));
|
||||||
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedItem());
|
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedItem());
|
||||||
}
|
}
|
||||||
|
@ -120,99 +127,104 @@ public class CreateOfferCB extends CachedCodeBehind<CreateOfferPM> {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void setupListeners() {
|
private void setupListeners() {
|
||||||
volumeTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
// focus out
|
||||||
pm().onFocusOutVolumeTextField(oldValue, newValue);
|
|
||||||
volumeTextField.setText(pm().volume.get());
|
|
||||||
});
|
|
||||||
|
|
||||||
amountTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
amountTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
||||||
pm().onFocusOutAmountTextField(oldValue, newValue);
|
presentationModel.onFocusOutAmountTextField(oldValue, newValue, amountTextField.getText());
|
||||||
amountTextField.setText(pm().amount.get());
|
amountTextField.setText(presentationModel.amount.get());
|
||||||
});
|
|
||||||
|
|
||||||
priceTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
|
||||||
pm().onFocusOutPriceTextField(oldValue, newValue);
|
|
||||||
priceTextField.setText(pm().price.get());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
minAmountTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
minAmountTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
||||||
pm().onFocusOutMinAmountTextField(oldValue, newValue);
|
presentationModel.onFocusOutMinAmountTextField(oldValue, newValue, minAmountTextField.getText());
|
||||||
minAmountTextField.setText(pm().minAmount.get());
|
minAmountTextField.setText(presentationModel.minAmount.get());
|
||||||
});
|
});
|
||||||
|
|
||||||
pm().showWarningInvalidBtcDecimalPlaces.addListener((o, oldValue, newValue) -> {
|
priceTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
||||||
|
presentationModel.onFocusOutPriceTextField(oldValue, newValue, priceTextField.getText());
|
||||||
|
priceTextField.setText(presentationModel.price.get());
|
||||||
|
});
|
||||||
|
|
||||||
|
volumeTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
||||||
|
presentationModel.onFocusOutVolumeTextField(oldValue, newValue, volumeTextField.getText());
|
||||||
|
volumeTextField.setText(presentationModel.volume.get());
|
||||||
|
});
|
||||||
|
|
||||||
|
// warnings
|
||||||
|
presentationModel.showWarningInvalidBtcDecimalPlaces.addListener((o, oldValue, newValue) -> {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
Popups.openWarningPopup("Warning", "The amount you have entered exceeds the number of allowed decimal" +
|
Popups.openWarningPopup("Warning", "The amount you have entered exceeds the number of allowed decimal" +
|
||||||
" places.\nThe amount has been adjusted to 4 decimal places.");
|
" places.\nThe amount has been adjusted to 4 decimal places.");
|
||||||
pm().showWarningInvalidBtcDecimalPlaces.set(false);
|
presentationModel.showWarningInvalidBtcDecimalPlaces.set(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
pm().showWarningInvalidFiatDecimalPlaces.addListener((o, oldValue, newValue) -> {
|
presentationModel.showWarningInvalidFiatDecimalPlaces.addListener((o, oldValue, newValue) -> {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
Popups.openWarningPopup("Warning", "The amount you have entered exceeds the number of allowed decimal" +
|
Popups.openWarningPopup("Warning", "The amount you have entered exceeds the number of allowed decimal" +
|
||||||
" places.\nThe amount has been adjusted to 2 decimal places.");
|
" places.\nThe amount has been adjusted to 2 decimal places.");
|
||||||
pm().showWarningInvalidFiatDecimalPlaces.set(false);
|
presentationModel.showWarningInvalidFiatDecimalPlaces.set(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
pm().showWarningAdjustedVolume.addListener((o, oldValue, newValue) -> {
|
presentationModel.showWarningAdjustedVolume.addListener((o, oldValue, newValue) -> {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
Popups.openWarningPopup("Warning", "The total volume you have entered leads to invalid fractional " +
|
Popups.openWarningPopup("Warning", "The total volume you have entered leads to invalid fractional " +
|
||||||
"Bitcoin amounts.\nThe amount has been adjusted and a new total volume be calculated from it.");
|
"Bitcoin amounts.\nThe amount has been adjusted and a new total volume be calculated from it.");
|
||||||
pm().showWarningAdjustedVolume.set(false);
|
presentationModel.showWarningAdjustedVolume.set(false);
|
||||||
volumeTextField.setText(pm().volume.get());
|
volumeTextField.setText(presentationModel.volume.get());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
pm().requestPlaceOfferFailed.addListener((o, oldValue, newValue) -> {
|
presentationModel.requestPlaceOfferFailed.addListener((o, oldValue, newValue) -> {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
Popups.openErrorPopup("Error", "An error occurred when placing the offer.\n" +
|
Popups.openErrorPopup("Error", "An error occurred when placing the offer.\n" +
|
||||||
pm().requestPlaceOfferErrorMessage);
|
presentationModel.requestPlaceOfferErrorMessage);
|
||||||
pm().requestPlaceOfferFailed.set(false);
|
presentationModel.requestPlaceOfferFailed.set(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupBindings() {
|
private void setupBindings() {
|
||||||
buyLabel.textProperty().bind(pm().directionLabel);
|
buyLabel.textProperty().bind(presentationModel.directionLabel);
|
||||||
amountTextField.textProperty().bindBidirectional(pm().amount);
|
|
||||||
priceTextField.textProperty().bindBidirectional(pm().price);
|
|
||||||
volumeTextField.textProperty().bindBidirectional(pm().volume);
|
|
||||||
|
|
||||||
minAmountTextField.textProperty().bindBidirectional(pm().minAmount);
|
amountTextField.textProperty().bindBidirectional(presentationModel.amount);
|
||||||
collateralLabel.textProperty().bind(pm().collateralLabel);
|
minAmountTextField.textProperty().bindBidirectional(presentationModel.minAmount);
|
||||||
collateralTextField.textProperty().bind(pm().collateral);
|
priceTextField.textProperty().bindBidirectional(presentationModel.price);
|
||||||
totalToPayTextField.textProperty().bind(pm().totalToPay);
|
volumeTextField.textProperty().bindBidirectional(presentationModel.volume);
|
||||||
|
|
||||||
addressTextField.amountAsCoinProperty().bind(pm().totalToPayAsCoin);
|
collateralLabel.textProperty().bind(presentationModel.collateralLabel);
|
||||||
addressTextField.paymentLabelProperty().bind(pm().paymentLabel);
|
collateralTextField.textProperty().bind(presentationModel.collateral);
|
||||||
addressTextField.addressProperty().bind(pm().addressAsString);
|
totalToPayTextField.textProperty().bind(presentationModel.totalToPay);
|
||||||
|
totalFeesTextField.textProperty().bind(presentationModel.totalFees);
|
||||||
|
|
||||||
bankAccountTypeTextField.textProperty().bind(pm().bankAccountType);
|
addressTextField.amountAsCoinProperty().bind(presentationModel.totalToPayAsCoin);
|
||||||
bankAccountCurrencyTextField.textProperty().bind(pm().bankAccountCurrency);
|
addressTextField.paymentLabelProperty().bind(presentationModel.paymentLabel);
|
||||||
bankAccountCountyTextField.textProperty().bind(pm().bankAccountCounty);
|
addressTextField.addressProperty().bind(presentationModel.addressAsString);
|
||||||
|
|
||||||
acceptedCountriesTextField.textProperty().bind(pm().acceptedCountries);
|
bankAccountTypeTextField.textProperty().bind(presentationModel.bankAccountType);
|
||||||
acceptedLanguagesTextField.textProperty().bind(pm().acceptedLanguages);
|
bankAccountCurrencyTextField.textProperty().bind(presentationModel.bankAccountCurrency);
|
||||||
totalFeesTextField.textProperty().bind(pm().totalFees);
|
bankAccountCountyTextField.textProperty().bind(presentationModel.bankAccountCounty);
|
||||||
transactionIdTextField.textProperty().bind(pm().transactionId);
|
|
||||||
|
|
||||||
amountTextField.amountValidationResultProperty().bind(pm().amountValidationResult);
|
acceptedCountriesTextField.textProperty().bind(presentationModel.acceptedCountries);
|
||||||
minAmountTextField.amountValidationResultProperty().bind(pm().minAmountValidationResult);
|
acceptedLanguagesTextField.textProperty().bind(presentationModel.acceptedLanguages);
|
||||||
priceTextField.amountValidationResultProperty().bind(pm().priceValidationResult);
|
transactionIdTextField.textProperty().bind(presentationModel.transactionId);
|
||||||
volumeTextField.amountValidationResultProperty().bind(pm().volumeValidationResult);
|
|
||||||
|
|
||||||
placeOfferButton.visibleProperty().bind(pm().isPlaceOfferButtonVisible);
|
// Validation
|
||||||
placeOfferButton.disableProperty().bind(pm().isPlaceOfferButtonDisabled);
|
amountTextField.amountValidationResultProperty().bind(presentationModel.amountValidationResult);
|
||||||
closeButton.visibleProperty().bind(pm().isCloseButtonVisible);
|
minAmountTextField.amountValidationResultProperty().bind(presentationModel.minAmountValidationResult);
|
||||||
|
priceTextField.amountValidationResultProperty().bind(presentationModel.priceValidationResult);
|
||||||
|
volumeTextField.amountValidationResultProperty().bind(presentationModel.volumeValidationResult);
|
||||||
|
|
||||||
|
// buttons
|
||||||
|
placeOfferButton.visibleProperty().bind(presentationModel.isPlaceOfferButtonVisible);
|
||||||
|
placeOfferButton.disableProperty().bind(presentationModel.isPlaceOfferButtonDisabled);
|
||||||
|
closeButton.visibleProperty().bind(presentationModel.isCloseButtonVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configTextFieldValidators() {
|
private void configTextFieldValidators() {
|
||||||
Region referenceNode = (Region) amountTextField.getParent();
|
Region referenceNode = (Region) amountTextField.getParent();
|
||||||
amountTextField.setErrorPopupLayoutReference(referenceNode);
|
amountTextField.setLayoutReference(referenceNode);
|
||||||
priceTextField.setErrorPopupLayoutReference(referenceNode);
|
priceTextField.setLayoutReference(referenceNode);
|
||||||
volumeTextField.setErrorPopupLayoutReference(referenceNode);
|
volumeTextField.setLayoutReference(referenceNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,32 +100,18 @@ class CreateOfferModel extends UIModel {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CreateOfferModel(TradeManager tradeManager, WalletFacade walletFacade, Settings settings, User user) {
|
CreateOfferModel(TradeManager tradeManager, WalletFacade walletFacade, Settings settings, User user) {
|
||||||
this.tradeManager = tradeManager;
|
this.tradeManager = tradeManager;
|
||||||
this.walletFacade = walletFacade;
|
this.walletFacade = walletFacade;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
|
||||||
// static data
|
|
||||||
offerId = UUID.randomUUID().toString();
|
offerId = UUID.randomUUID().toString();
|
||||||
totalFeesAsCoin.setValue(FeePolicy.CREATE_OFFER_FEE.add(FeePolicy.TX_FEE));
|
|
||||||
|
|
||||||
|
// Node: Don't do setup in constructor to make object creation faster
|
||||||
//TODO just for unit testing, use mockito?
|
|
||||||
if (walletFacade != null && walletFacade.getWallet() != null)
|
|
||||||
addressEntry = walletFacade.getAddressInfoByTradeID(offerId);
|
|
||||||
|
|
||||||
collateralAsLong.setValue(settings.getCollateral());
|
|
||||||
|
|
||||||
BankAccount bankAccount = user.getCurrentBankAccount();
|
|
||||||
if (bankAccount != null) {
|
|
||||||
bankAccountType.setValue(bankAccount.getBankAccountType().toString());
|
|
||||||
bankAccountCurrency.setValue(bankAccount.getCurrency().getCurrencyCode());
|
|
||||||
bankAccountCounty.setValue(bankAccount.getCountry().getName());
|
|
||||||
}
|
|
||||||
acceptedCountries.setAll(settings.getAcceptedCountries());
|
|
||||||
acceptedLanguages.setAll(settings.getAcceptedLanguageLocales());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -133,11 +119,29 @@ class CreateOfferModel extends UIModel {
|
||||||
@Override
|
@Override
|
||||||
public void initialized() {
|
public void initialized() {
|
||||||
super.initialized();
|
super.initialized();
|
||||||
|
|
||||||
|
// static data
|
||||||
|
totalFeesAsCoin.set(FeePolicy.CREATE_OFFER_FEE.add(FeePolicy.TX_FEE));
|
||||||
|
|
||||||
|
if (walletFacade != null && walletFacade.getWallet() != null)
|
||||||
|
addressEntry = walletFacade.getAddressInfoByTradeID(offerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activate() {
|
public void activate() {
|
||||||
super.activate();
|
super.activate();
|
||||||
|
|
||||||
|
// might be changed after screen change
|
||||||
|
collateralAsLong.set(settings.getCollateral());
|
||||||
|
|
||||||
|
BankAccount bankAccount = user.getCurrentBankAccount();
|
||||||
|
if (bankAccount != null) {
|
||||||
|
bankAccountType.set(bankAccount.getBankAccountType().toString());
|
||||||
|
bankAccountCurrency.set(bankAccount.getCurrency().getCurrencyCode());
|
||||||
|
bankAccountCounty.set(bankAccount.getCountry().getName());
|
||||||
|
}
|
||||||
|
acceptedCountries.setAll(settings.getAcceptedCountries());
|
||||||
|
acceptedLanguages.setAll(settings.getAcceptedLanguageLocales());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -150,41 +154,43 @@ class CreateOfferModel extends UIModel {
|
||||||
super.terminate();
|
super.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Methods
|
// Methods
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
void placeOffer() {
|
void placeOffer() {
|
||||||
|
// data validation is done in the trade domain
|
||||||
tradeManager.requestPlaceOffer(offerId,
|
tradeManager.requestPlaceOffer(offerId,
|
||||||
direction,
|
direction,
|
||||||
priceAsFiat.get(),
|
priceAsFiat.get(),
|
||||||
amountAsCoin.get(),
|
amountAsCoin.get(),
|
||||||
minAmountAsCoin.get(),
|
minAmountAsCoin.get(),
|
||||||
(transaction) -> {
|
(transaction) -> {
|
||||||
requestPlaceOfferSuccess.setValue(true);
|
requestPlaceOfferSuccess.set(true);
|
||||||
transactionId.setValue(transaction.getHashAsString());
|
transactionId.set(transaction.getHashAsString());
|
||||||
},
|
},
|
||||||
(errorMessage) -> {
|
(errorMessage) -> {
|
||||||
requestPlaceOfferFailed.setValue(true);
|
requestPlaceOfferFailed.set(true);
|
||||||
requestPlaceOfferErrorMessage.setValue(errorMessage);
|
requestPlaceOfferErrorMessage.set(errorMessage);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateVolume() {
|
void calculateVolume() {
|
||||||
if (priceAsFiat.get() != null && amountAsCoin.get() != null /*&& !amountAsCoin.get().isZero()*/)
|
if (priceAsFiat.get() != null && amountAsCoin.get() != null /*&& !amountAsCoin.get().isZero()*/) {
|
||||||
volumeAsFiat.setValue(new ExchangeRate(priceAsFiat.get()).coinToFiat(amountAsCoin.get()));
|
volumeAsFiat.set(new ExchangeRate(priceAsFiat.get()).coinToFiat(amountAsCoin.get()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateAmount() {
|
void calculateAmount() {
|
||||||
|
|
||||||
if (volumeAsFiat.get() != null && priceAsFiat.get() != null/* && !volumeAsFiat.get().isZero() && !priceAsFiat
|
if (volumeAsFiat.get() != null && priceAsFiat.get() != null/* && !volumeAsFiat.get().isZero() && !priceAsFiat
|
||||||
.get().isZero()*/) {
|
.get().isZero()*/) {
|
||||||
amountAsCoin.setValue(new ExchangeRate(priceAsFiat.get()).fiatToCoin(volumeAsFiat.get()));
|
amountAsCoin.set(new ExchangeRate(priceAsFiat.get()).fiatToCoin(volumeAsFiat.get()));
|
||||||
|
|
||||||
// If we got a btc value with more then 4 decimals we convert it to max 4 decimals
|
// If we got a btc value with more then 4 decimals we convert it to max 4 decimals
|
||||||
amountAsCoin.setValue(reduceto4Dezimals(amountAsCoin.get()));
|
amountAsCoin.set(reduceto4Dezimals(amountAsCoin.get()));
|
||||||
calculateTotalToPay();
|
calculateTotalToPay();
|
||||||
calculateCollateral();
|
calculateCollateral();
|
||||||
}
|
}
|
||||||
|
@ -193,17 +199,16 @@ class CreateOfferModel extends UIModel {
|
||||||
void calculateTotalToPay() {
|
void calculateTotalToPay() {
|
||||||
calculateCollateral();
|
calculateCollateral();
|
||||||
|
|
||||||
if (collateralAsCoin.get() != null) {
|
if (collateralAsCoin.get() != null)
|
||||||
totalToPayAsCoin.setValue(collateralAsCoin.get().add(totalFeesAsCoin.get()));
|
totalToPayAsCoin.set(collateralAsCoin.get().add(totalFeesAsCoin.get()));
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateCollateral() {
|
void calculateCollateral() {
|
||||||
if (amountAsCoin.get() != null)
|
if (amountAsCoin.get() != null)
|
||||||
collateralAsCoin.setValue(amountAsCoin.get().multiply(collateralAsLong.get()).divide(1000));
|
collateralAsCoin.set(amountAsCoin.get().multiply(collateralAsLong.get()).divide(1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Validation
|
// Validation
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -214,6 +219,7 @@ class CreateOfferModel extends UIModel {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Setter/Getter
|
// Setter/Getter
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -30,6 +30,8 @@ import io.bitsquare.trade.orderbook.OrderBookFilter;
|
||||||
import com.google.bitcoin.core.Address;
|
import com.google.bitcoin.core.Address;
|
||||||
import com.google.bitcoin.core.Coin;
|
import com.google.bitcoin.core.Coin;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
@ -48,7 +50,6 @@ import static javafx.beans.binding.Bindings.createStringBinding;
|
||||||
class CreateOfferPM extends PresentationModel<CreateOfferModel> {
|
class CreateOfferPM extends PresentationModel<CreateOfferModel> {
|
||||||
private static final Logger log = LoggerFactory.getLogger(CreateOfferPM.class);
|
private static final Logger log = LoggerFactory.getLogger(CreateOfferPM.class);
|
||||||
|
|
||||||
|
|
||||||
private BtcValidator btcValidator = new BtcValidator();
|
private BtcValidator btcValidator = new BtcValidator();
|
||||||
private FiatValidator fiatValidator = new FiatValidator();
|
private FiatValidator fiatValidator = new FiatValidator();
|
||||||
|
|
||||||
|
@ -85,29 +86,23 @@ class CreateOfferPM extends PresentationModel<CreateOfferModel> {
|
||||||
final ObjectProperty<InputValidator.ValidationResult> priceValidationResult = new SimpleObjectProperty<>();
|
final ObjectProperty<InputValidator.ValidationResult> priceValidationResult = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<InputValidator.ValidationResult> volumeValidationResult = new SimpleObjectProperty<>();
|
final ObjectProperty<InputValidator.ValidationResult> volumeValidationResult = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
// That is needed for the addressTextField
|
// Those are needed for the addressTextField
|
||||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor (called by CB)
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Inject
|
||||||
CreateOfferPM(CreateOfferModel model) {
|
CreateOfferPM(CreateOfferModel model) {
|
||||||
super(model);
|
super(model);
|
||||||
|
|
||||||
paymentLabel.setValue("Bitsquare trade (" + model().getOfferId() + ")");
|
// Node: Don't do setup in constructor to make object creation faster
|
||||||
|
|
||||||
if (model().addressEntry != null) {
|
|
||||||
addressAsString.setValue(model().addressEntry.getAddress().toString());
|
|
||||||
address.setValue(model().addressEntry.getAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
setupModelBindings();
|
|
||||||
setupUIInputListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -115,6 +110,17 @@ class CreateOfferPM extends PresentationModel<CreateOfferModel> {
|
||||||
@Override
|
@Override
|
||||||
public void initialized() {
|
public void initialized() {
|
||||||
super.initialized();
|
super.initialized();
|
||||||
|
|
||||||
|
// static
|
||||||
|
paymentLabel.set("Bitsquare trade (" + model.getOfferId() + ")");
|
||||||
|
|
||||||
|
if (model.addressEntry != null) {
|
||||||
|
addressAsString.set(model.addressEntry.getAddress().toString());
|
||||||
|
address.set(model.addressEntry.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
setupBindings();
|
||||||
|
setupListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -132,23 +138,27 @@ class CreateOfferPM extends PresentationModel<CreateOfferModel> {
|
||||||
super.terminate();
|
super.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Public API methods (called by CB)
|
// Public API methods (called by CB)
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// setOrderBookFilter is a one time call
|
||||||
void setOrderBookFilter(OrderBookFilter orderBookFilter) {
|
void setOrderBookFilter(OrderBookFilter orderBookFilter) {
|
||||||
model().setDirection(orderBookFilter.getDirection());
|
model.setDirection(orderBookFilter.getDirection());
|
||||||
directionLabel.setValue(model().getDirection() == Direction.BUY ? "Buy:" : "Sell:");
|
directionLabel.set(model.getDirection() == Direction.BUY ? "Buy:" : "Sell:");
|
||||||
|
|
||||||
|
// apply only if valid
|
||||||
if (orderBookFilter.getAmount() != null && isBtcInputValid(orderBookFilter.getAmount().toPlainString())
|
if (orderBookFilter.getAmount() != null && isBtcInputValid(orderBookFilter.getAmount().toPlainString())
|
||||||
.isValid) {
|
.isValid) {
|
||||||
model().amountAsCoin.setValue(orderBookFilter.getAmount());
|
model.amountAsCoin.set(orderBookFilter.getAmount());
|
||||||
model().minAmountAsCoin.setValue(orderBookFilter.getAmount());
|
model.minAmountAsCoin.set(orderBookFilter.getAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO use Fiat in orderBookFilter
|
// TODO use Fiat in orderBookFilter
|
||||||
|
// apply only if valid
|
||||||
if (orderBookFilter.getPrice() != 0 && isBtcInputValid(String.valueOf(orderBookFilter.getPrice())).isValid)
|
if (orderBookFilter.getPrice() != 0 && isBtcInputValid(String.valueOf(orderBookFilter.getPrice())).isValid)
|
||||||
model().priceAsFiat.setValue(parseToFiatWith2Decimals(String.valueOf(orderBookFilter.getPrice())));
|
model.priceAsFiat.set(parseToFiatWith2Decimals(String.valueOf(orderBookFilter.getPrice())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,97 +167,96 @@ class CreateOfferPM extends PresentationModel<CreateOfferModel> {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void placeOffer() {
|
void placeOffer() {
|
||||||
model().placeOffer();
|
model.placeOffer();
|
||||||
isPlaceOfferButtonDisabled.setValue(true);
|
isPlaceOfferButtonDisabled.set(true);
|
||||||
isPlaceOfferButtonVisible.setValue(true);
|
isPlaceOfferButtonVisible.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// UI events (called by CB)
|
// UI events (called by CB)
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// when focus out we do validation and apply the data to the model
|
// On focus out we do validation and apply the data to the model
|
||||||
|
void onFocusOutAmountTextField(Boolean oldValue, Boolean newValue, String userInput) {
|
||||||
void onFocusOutAmountTextField(Boolean oldValue, Boolean newValue) {
|
|
||||||
if (oldValue && !newValue) {
|
if (oldValue && !newValue) {
|
||||||
InputValidator.ValidationResult result = isBtcInputValid(amount.get());
|
InputValidator.ValidationResult result = isBtcInputValid(amount.get());
|
||||||
boolean isValid = result.isValid;
|
amountValidationResult.set(result);
|
||||||
amountValidationResult.setValue(result);
|
if (result.isValid) {
|
||||||
if (isValid) {
|
showWarningInvalidBtcDecimalPlaces.set(!hasBtcValidDecimals(userInput));
|
||||||
showWarningInvalidBtcDecimalPlaces.setValue(!hasBtcValidDecimals(amount.get()));
|
|
||||||
// only allow max 4 decimal places for btc values
|
// only allow max 4 decimal places for btc values
|
||||||
setAmountToModel();
|
setAmountToModel();
|
||||||
// reformat input to general btc format
|
// reformat input
|
||||||
|
amount.set(formatCoin(model.amountAsCoin.get()));
|
||||||
|
|
||||||
calculateVolume();
|
calculateVolume();
|
||||||
|
|
||||||
if (!model().isMinAmountLessOrEqualAmount()) {
|
// handle minAmount/amount relationship
|
||||||
amountValidationResult.setValue(new InputValidator.ValidationResult(false,
|
if (!model.isMinAmountLessOrEqualAmount()) {
|
||||||
|
amountValidationResult.set(new InputValidator.ValidationResult(false,
|
||||||
"Amount cannot be smaller than minimum amount."));
|
"Amount cannot be smaller than minimum amount."));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
amountValidationResult.setValue(result);
|
amountValidationResult.set(result);
|
||||||
if (minAmount.get() != null)
|
if (minAmount.get() != null)
|
||||||
minAmountValidationResult.setValue(isBtcInputValid(minAmount.get()));
|
minAmountValidationResult.set(isBtcInputValid(minAmount.get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onFocusOutMinAmountTextField(Boolean oldValue, Boolean newValue) {
|
void onFocusOutMinAmountTextField(Boolean oldValue, Boolean newValue, String userInput) {
|
||||||
|
|
||||||
if (oldValue && !newValue) {
|
if (oldValue && !newValue) {
|
||||||
InputValidator.ValidationResult result = isBtcInputValid(minAmount.get());
|
InputValidator.ValidationResult result = isBtcInputValid(minAmount.get());
|
||||||
boolean isValid = result.isValid;
|
minAmountValidationResult.set(result);
|
||||||
minAmountValidationResult.setValue(result);
|
if (result.isValid) {
|
||||||
if (isValid) {
|
showWarningInvalidBtcDecimalPlaces.set(!hasBtcValidDecimals(userInput));
|
||||||
showWarningInvalidBtcDecimalPlaces.setValue(!hasBtcValidDecimals(minAmount.get()));
|
|
||||||
setMinAmountToModel();
|
setMinAmountToModel();
|
||||||
|
minAmount.set(formatCoin(model.minAmountAsCoin.get()));
|
||||||
|
|
||||||
if (!model().isMinAmountLessOrEqualAmount()) {
|
if (!model.isMinAmountLessOrEqualAmount()) {
|
||||||
minAmountValidationResult.setValue(new InputValidator.ValidationResult(false,
|
minAmountValidationResult.set(new InputValidator.ValidationResult(false,
|
||||||
"Minimum amount cannot be larger than amount."));
|
"Minimum amount cannot be larger than amount."));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
minAmountValidationResult.setValue(result);
|
minAmountValidationResult.set(result);
|
||||||
if (amount.get() != null)
|
if (amount.get() != null)
|
||||||
amountValidationResult.setValue(isBtcInputValid(amount.get()));
|
amountValidationResult.set(isBtcInputValid(amount.get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onFocusOutPriceTextField(Boolean oldValue, Boolean newValue) {
|
void onFocusOutPriceTextField(Boolean oldValue, Boolean newValue, String userInput) {
|
||||||
if (oldValue && !newValue) {
|
if (oldValue && !newValue) {
|
||||||
InputValidator.ValidationResult result = isFiatInputValid(price.get());
|
InputValidator.ValidationResult result = isFiatInputValid(price.get());
|
||||||
boolean isValid = result.isValid;
|
boolean isValid = result.isValid;
|
||||||
priceValidationResult.setValue(result);
|
priceValidationResult.set(result);
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
showWarningInvalidFiatDecimalPlaces.setValue(!hasFiatValidDecimals(price.get()));
|
showWarningInvalidFiatDecimalPlaces.set(!hasFiatValidDecimals(userInput));
|
||||||
setPriceToModel();
|
setPriceToModel();
|
||||||
|
price.set(formatFiat(model.priceAsFiat.get()));
|
||||||
|
|
||||||
calculateVolume();
|
calculateVolume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onFocusOutVolumeTextField(Boolean oldValue, Boolean newValue) {
|
void onFocusOutVolumeTextField(Boolean oldValue, Boolean newValue, String userInput) {
|
||||||
if (oldValue && !newValue) {
|
if (oldValue && !newValue) {
|
||||||
InputValidator.ValidationResult result = isBtcInputValid(volume.get());
|
InputValidator.ValidationResult result = isBtcInputValid(volume.get());
|
||||||
boolean isValid = result.isValid;
|
volumeValidationResult.set(result);
|
||||||
volumeValidationResult.setValue(result);
|
if (result.isValid) {
|
||||||
if (isValid) {
|
showWarningInvalidFiatDecimalPlaces.set(!hasFiatValidDecimals(userInput));
|
||||||
String origVolume = volume.get();
|
|
||||||
showWarningInvalidFiatDecimalPlaces.setValue(!hasFiatValidDecimals(volume.get()));
|
|
||||||
setVolumeToModel();
|
setVolumeToModel();
|
||||||
|
volume.set(formatFiat(model.volumeAsFiat.get()));
|
||||||
|
|
||||||
calculateAmount();
|
calculateAmount();
|
||||||
|
|
||||||
// must be after calculateAmount (btc value has been adjusted in case the calculation leads to
|
// must be placed after calculateAmount (btc value has been adjusted in case the calculation leads to
|
||||||
// invalid decimal places for the amount value
|
// invalid decimal places for the amount value
|
||||||
showWarningAdjustedVolume.setValue(!formatFiat(parseToFiatWith2Decimals(origVolume)).equals(volume
|
showWarningAdjustedVolume.set(!formatFiat(parseToFiatWith2Decimals(userInput)).equals(volume
|
||||||
.get()));
|
.get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,7 +268,7 @@ class CreateOfferPM extends PresentationModel<CreateOfferModel> {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
WalletFacade getWalletFacade() {
|
WalletFacade getWalletFacade() {
|
||||||
return model().getWalletFacade();
|
return model.getWalletFacade();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -267,129 +276,127 @@ class CreateOfferPM extends PresentationModel<CreateOfferModel> {
|
||||||
// Private
|
// Private
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void setupUIInputListeners() {
|
private void setupListeners() {
|
||||||
|
|
||||||
// bindBidirectional for amount, price, volume and minAmount
|
// Bidirectional bindings are used for all input fields: amount, price, volume and minAmount
|
||||||
// We do volume/amount calculation during input
|
// We do volume/amount calculation during input, so user has immediate feedback
|
||||||
amount.addListener((ov, oldValue, newValue) -> {
|
amount.addListener((ov, oldValue, newValue) -> {
|
||||||
if (isBtcInputValid(newValue).isValid) {
|
if (isBtcInputValid(newValue).isValid) {
|
||||||
model().amountAsCoin.setValue(parseToCoinWith4Decimals(newValue));
|
setMinAmountToModel();
|
||||||
calculateVolume();
|
calculateVolume();
|
||||||
model().calculateTotalToPay();
|
model.calculateTotalToPay();
|
||||||
model().calculateCollateral();
|
model.calculateCollateral();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
price.addListener((ov, oldValue, newValue) -> {
|
price.addListener((ov, oldValue, newValue) -> {
|
||||||
if (isFiatInputValid(newValue).isValid) {
|
if (isFiatInputValid(newValue).isValid) {
|
||||||
model().priceAsFiat.setValue(parseToFiatWith2Decimals(newValue));
|
setPriceToModel();
|
||||||
calculateVolume();
|
calculateVolume();
|
||||||
model().calculateTotalToPay();
|
model.calculateTotalToPay();
|
||||||
model().calculateCollateral();
|
model.calculateCollateral();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
volume.addListener((ov, oldValue, newValue) -> {
|
volume.addListener((ov, oldValue, newValue) -> {
|
||||||
if (isFiatInputValid(newValue).isValid) {
|
if (isFiatInputValid(newValue).isValid) {
|
||||||
model().volumeAsFiat.setValue(parseToFiatWith2Decimals(newValue));
|
|
||||||
setVolumeToModel();
|
setVolumeToModel();
|
||||||
setPriceToModel();
|
setPriceToModel();
|
||||||
model().calculateAmount();
|
model.calculateAmount();
|
||||||
model().calculateTotalToPay();
|
model.calculateTotalToPay();
|
||||||
model().calculateCollateral();
|
model.calculateCollateral();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Binding with Bindings.createObjectBinding does not work becaue of bi-directional binding in CB
|
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||||
model().amountAsCoin.addListener((ov, oldValue, newValue) -> amount.set(formatCoin(newValue)));
|
model.amountAsCoin.addListener((ov, oldValue, newValue) -> amount.set(formatCoin(newValue)));
|
||||||
model().minAmountAsCoin.addListener((ov, oldValue, newValue) -> minAmount.set(formatCoin(newValue)));
|
model.minAmountAsCoin.addListener((ov, oldValue, newValue) -> minAmount.set(formatCoin(newValue)));
|
||||||
model().priceAsFiat.addListener((ov, oldValue, newValue) -> price.set(formatFiat(newValue)));
|
model.priceAsFiat.addListener((ov, oldValue, newValue) -> price.set(formatFiat(newValue)));
|
||||||
model().volumeAsFiat.addListener((ov, oldValue, newValue) -> volume.set(formatFiat(newValue)));
|
model.volumeAsFiat.addListener((ov, oldValue, newValue) -> volume.set(formatFiat(newValue)));
|
||||||
}
|
|
||||||
|
|
||||||
private void setupModelBindings() {
|
|
||||||
totalToPay.bind(createStringBinding(() -> formatCoinWithCode(model().totalToPayAsCoin.get()),
|
|
||||||
model().totalToPayAsCoin));
|
|
||||||
collateral.bind(createStringBinding(() -> formatCoinWithCode(model().collateralAsCoin.get()),
|
|
||||||
model().collateralAsCoin));
|
|
||||||
|
|
||||||
collateralLabel.bind(Bindings.createStringBinding(() -> "Collateral (" + BSFormatter.formatCollateralPercent
|
|
||||||
(model().collateralAsLong.get()) + "):", model().collateralAsLong));
|
|
||||||
totalToPayAsCoin.bind(model().totalToPayAsCoin);
|
|
||||||
|
|
||||||
bankAccountType.bind(Bindings.createStringBinding(() -> Localisation.get(model().bankAccountType.get()),
|
|
||||||
model().bankAccountType));
|
|
||||||
bankAccountCurrency.bind(model().bankAccountCurrency);
|
|
||||||
bankAccountCounty.bind(model().bankAccountCounty);
|
|
||||||
|
|
||||||
// ObservableLists
|
// ObservableLists
|
||||||
model().acceptedCountries.addListener((Observable o) -> acceptedCountries.setValue(BSFormatter
|
model.acceptedCountries.addListener((Observable o) -> acceptedCountries.set(BSFormatter
|
||||||
.countryLocalesToString(model().acceptedCountries)));
|
.countryLocalesToString(model.acceptedCountries)));
|
||||||
model().acceptedLanguages.addListener((Observable o) -> acceptedLanguages.setValue(BSFormatter
|
model.acceptedLanguages.addListener((Observable o) -> acceptedLanguages.set(BSFormatter
|
||||||
.languageLocalesToString(model().acceptedLanguages)));
|
.languageLocalesToString(model.acceptedLanguages)));
|
||||||
|
|
||||||
isCloseButtonVisible.bind(model().requestPlaceOfferSuccess);
|
|
||||||
requestPlaceOfferErrorMessage.bind(model().requestPlaceOfferErrorMessage);
|
|
||||||
requestPlaceOfferFailed.bind(model().requestPlaceOfferFailed);
|
|
||||||
showTransactionPublishedScreen.bind(model().requestPlaceOfferSuccess);
|
|
||||||
|
|
||||||
isPlaceOfferButtonDisabled.bind(Bindings.createBooleanBinding(() -> !model().requestPlaceOfferFailed.get(),
|
|
||||||
model().requestPlaceOfferFailed));
|
|
||||||
|
|
||||||
isPlaceOfferButtonVisible.bind(Bindings.createBooleanBinding(() -> !model().requestPlaceOfferSuccess.get(),
|
|
||||||
model().requestPlaceOfferSuccess));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setupBindings() {
|
||||||
|
totalToPay.bind(createStringBinding(() -> formatCoinWithCode(model.totalToPayAsCoin.get()),
|
||||||
|
model.totalToPayAsCoin));
|
||||||
|
collateral.bind(createStringBinding(() -> formatCoinWithCode(model.collateralAsCoin.get()),
|
||||||
|
model.collateralAsCoin));
|
||||||
|
|
||||||
|
collateralLabel.bind(Bindings.createStringBinding(() -> "Collateral (" + BSFormatter.formatCollateralPercent
|
||||||
|
(model.collateralAsLong.get()) + "):", model.collateralAsLong));
|
||||||
|
totalToPayAsCoin.bind(model.totalToPayAsCoin);
|
||||||
|
|
||||||
|
totalFees.bind(createStringBinding(() -> formatCoinWithCode(model.totalFeesAsCoin.get()),
|
||||||
|
model.totalFeesAsCoin));
|
||||||
|
bankAccountType.bind(Bindings.createStringBinding(() -> Localisation.get(model.bankAccountType.get()),
|
||||||
|
model.bankAccountType));
|
||||||
|
bankAccountCurrency.bind(model.bankAccountCurrency);
|
||||||
|
bankAccountCounty.bind(model.bankAccountCounty);
|
||||||
|
|
||||||
|
isCloseButtonVisible.bind(model.requestPlaceOfferSuccess);
|
||||||
|
requestPlaceOfferErrorMessage.bind(model.requestPlaceOfferErrorMessage);
|
||||||
|
requestPlaceOfferFailed.bind(model.requestPlaceOfferFailed);
|
||||||
|
showTransactionPublishedScreen.bind(model.requestPlaceOfferSuccess);
|
||||||
|
|
||||||
|
isPlaceOfferButtonDisabled.bind(Bindings.createBooleanBinding(() -> !model.requestPlaceOfferFailed.get(),
|
||||||
|
model.requestPlaceOfferFailed));
|
||||||
|
|
||||||
|
isPlaceOfferButtonVisible.bind(Bindings.createBooleanBinding(() -> !model.requestPlaceOfferSuccess.get(),
|
||||||
|
model.requestPlaceOfferSuccess));
|
||||||
|
}
|
||||||
|
|
||||||
private void calculateVolume() {
|
private void calculateVolume() {
|
||||||
setAmountToModel();
|
setAmountToModel();
|
||||||
setPriceToModel();
|
setPriceToModel();
|
||||||
model().calculateVolume();
|
model.calculateVolume();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void calculateAmount() {
|
private void calculateAmount() {
|
||||||
setVolumeToModel();
|
setVolumeToModel();
|
||||||
setPriceToModel();
|
setPriceToModel();
|
||||||
model().calculateAmount();
|
model.calculateAmount();
|
||||||
|
|
||||||
if (!model().isMinAmountLessOrEqualAmount()) {
|
// Amount calculation could lead to amount/minAmount invalidation
|
||||||
amountValidationResult.setValue(new InputValidator.ValidationResult(false,
|
if (!model.isMinAmountLessOrEqualAmount()) {
|
||||||
|
amountValidationResult.set(new InputValidator.ValidationResult(false,
|
||||||
"Amount cannot be smaller than minimum amount."));
|
"Amount cannot be smaller than minimum amount."));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (amount.get() != null)
|
if (amount.get() != null)
|
||||||
amountValidationResult.setValue(isBtcInputValid(amount.get()));
|
amountValidationResult.set(isBtcInputValid(amount.get()));
|
||||||
if (minAmount.get() != null)
|
if (minAmount.get() != null)
|
||||||
minAmountValidationResult.setValue(isBtcInputValid(minAmount.get()));
|
minAmountValidationResult.set(isBtcInputValid(minAmount.get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAmountToModel() {
|
private void setAmountToModel() {
|
||||||
model().amountAsCoin.setValue(parseToCoinWith4Decimals(amount.get()));
|
model.amountAsCoin.set(parseToCoinWith4Decimals(amount.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setMinAmountToModel() {
|
private void setMinAmountToModel() {
|
||||||
model().minAmountAsCoin.setValue(parseToCoinWith4Decimals(minAmount.get()));
|
model.minAmountAsCoin.set(parseToCoinWith4Decimals(minAmount.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setPriceToModel() {
|
private void setPriceToModel() {
|
||||||
model().priceAsFiat.setValue(parseToFiatWith2Decimals(price.get()));
|
model.priceAsFiat.set(parseToFiatWith2Decimals(price.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setVolumeToModel() {
|
private void setVolumeToModel() {
|
||||||
model().volumeAsFiat.setValue(parseToFiatWith2Decimals(volume.get()));
|
model.volumeAsFiat.set(parseToFiatWith2Decimals(volume.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Package scope for testing
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
InputValidator.ValidationResult isBtcInputValid(String input) {
|
private InputValidator.ValidationResult isBtcInputValid(String input) {
|
||||||
|
|
||||||
return btcValidator.validate(input);
|
return btcValidator.validate(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
InputValidator.ValidationResult isFiatInputValid(String input) {
|
private InputValidator.ValidationResult isFiatInputValid(String input) {
|
||||||
|
|
||||||
return fiatValidator.validate(input);
|
return fiatValidator.validate(input);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,9 @@
|
||||||
<Label GridPane.rowIndex="1" fx:id="buyLabel"/>
|
<Label GridPane.rowIndex="1" fx:id="buyLabel"/>
|
||||||
<HBox GridPane.rowIndex="1" GridPane.columnIndex="1" spacing="5" GridPane.hgrow="NEVER" alignment="CENTER_LEFT">
|
<HBox GridPane.rowIndex="1" GridPane.columnIndex="1" spacing="5" GridPane.hgrow="NEVER" alignment="CENTER_LEFT">
|
||||||
<ValidatingTextField fx:id="amountTextField" prefWidth="100.0" alignment="CENTER_RIGHT"/>
|
<ValidatingTextField fx:id="amountTextField" prefWidth="100.0" alignment="CENTER_RIGHT"/>
|
||||||
<Label text="BTC @"/>
|
<Label text="BTC for price of "/>
|
||||||
<ValidatingTextField fx:id="priceTextField" prefWidth="100.0" alignment="CENTER_RIGHT"/>
|
<ValidatingTextField fx:id="priceTextField" prefWidth="100.0" alignment="CENTER_RIGHT"/>
|
||||||
<Label text="EUR ="/>
|
<Label text="EUR/BTC ="/>
|
||||||
<ValidatingTextField fx:id="volumeTextField" prefWidth="100.0" alignment="CENTER_RIGHT"/>
|
<ValidatingTextField fx:id="volumeTextField" prefWidth="100.0" alignment="CENTER_RIGHT"/>
|
||||||
<Label text="EUR in total"/>
|
<Label text="EUR in total"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|
Loading…
Add table
Reference in a new issue