diff --git a/desktop/src/main/java/bisq/desktop/bisq.css b/desktop/src/main/java/bisq/desktop/bisq.css index e7b6e8703b6..6e9cd3523e5 100644 --- a/desktop/src/main/java/bisq/desktop/bisq.css +++ b/desktop/src/main/java/bisq/desktop/bisq.css @@ -1400,6 +1400,13 @@ textfield */ -fx-alignment: center-left; } +.combo-box-editor-bold { + -fx-font-weight: bold; + -fx-padding: 5 8 5 8 !important; + -fx-text-fill: -bs-rd-black; + -fx-font-family: "IBM Plex Sans Medium"; +} + .currency-label-small { -fx-font-size: 0.692em; -fx-text-fill: -bs-rd-font-lighter; diff --git a/desktop/src/main/java/bisq/desktop/components/SearchComboBox.java b/desktop/src/main/java/bisq/desktop/components/SearchComboBox.java index 4d30b23e5cc..4b084bcdc4d 100644 --- a/desktop/src/main/java/bisq/desktop/components/SearchComboBox.java +++ b/desktop/src/main/java/bisq/desktop/components/SearchComboBox.java @@ -28,6 +28,8 @@ import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; +import javafx.beans.value.ChangeListener; + import javafx.event.Event; import javafx.event.EventHandler; @@ -39,6 +41,7 @@ public class SearchComboBox extends JFXComboBox { @SuppressWarnings("CanBeFinal") private FilteredList filteredList; private ComboBoxListViewSkin comboBoxListViewSkin; + private ChangeListener queryChangedListener; public SearchComboBox() { this(FXCollections.observableArrayList()); @@ -50,7 +53,58 @@ private SearchComboBox(ObservableList items) { setEmptySkinToGetMoreControlOverListView(); fixSpaceKey(); wrapItemsInFilteredList(); - reactToQueryChanges(); + prepareQueryChangedListener(); + } + + /** + * Activates dropdown reaction to query changes. This is separate from instantiation + * to give clients more control over when exactly the autocomplete mechanism is + * showing / resizing / hiding the dropdown in response to query text changes. + * + * Mind that query text changes are triggered *not* only by the user typing in, + * but also internally with JavaFX ComboBox. For that reason clients may prefer + * to deactivate() and then activate() when it makes sense. This allows to avoid + * some UX glitches. + */ + public void activate() { + removeFilter(); + adjustVisibleRowCount(); + getEditor().textProperty().addListener(queryChangedListener); + } + + /** + * See activate() for "why" and "when". + */ + public void deactivate() { + getEditor().textProperty().removeListener(queryChangedListener); + setOnHidden(null); + } + + /** + * Triggered when value change is *confirmed*. In practical terms + * this is when user clicks item on the dropdown or hits [ENTER] + * while typing in the text. + * + * This is in contrast to onAction event that is triggered + * on every (unconfirmed) value change. The onAction is not really + * suitable for the search enabled ComboBox. + */ + public final void setOnChangeConfirmed(EventHandler eh) { + setOnHidden(e -> { + var inputText = getEditor().getText(); + + // Case 1: fire if input text selects (matches) an item + var selectedItem = getSelectionModel().getSelectedItem(); + var inputTextItem = getConverter().fromString(inputText); + if (selectedItem != null && selectedItem.equals(inputTextItem)) { + eh.handle(e); + return; + } + + // Case 2: fire if the text is empty to support special "show all" case + if (inputText.isEmpty()) + eh.handle(e); + }); } // The ComboBox API does not provide enough control over the underlying @@ -80,22 +134,6 @@ private void wrapItemsInFilteredList() { }); } - // Whenever query changes we need to reset the list-filter and refresh the ListView - private void reactToQueryChanges() { - getEditor().textProperty().addListener((observable, oldQuery, query) -> { - var exactMatch = unfilteredItems().stream().anyMatch(item -> asString(item).equalsIgnoreCase(query)); - if (!exactMatch) { - UserThread.execute(() -> { - if (query.isEmpty()) - removeFilter(); - else - filterBy(query); - forceRedraw(); - }); - } - }); - } - private ObservableList unfilteredItems() { return (ObservableList) filteredList.getSource(); } @@ -104,7 +142,7 @@ private String asString(T item) { return getConverter().toString(item); } - private int filteredItemsSize() { + private int filteredListSize() { return filteredList.size(); } @@ -118,29 +156,9 @@ private void filterBy(String query) { ); } - /** - * Triggered when value change is *confirmed*. In practical terms - * this is when user clicks item on the dropdown or hits [ENTER] - * while typing in the text. - * - * This is in contrast to onAction event that is triggered - * on every (unconfirmed) value change. The onAction is not really - * suitable for the search enabled ComboBox. - */ - public final void setOnChangeConfirmed(EventHandler eh) { - setOnHidden(e -> { - var selectedItem = getSelectionModel().getSelectedItem(); - var selectedItemText = asString(selectedItem); - var inputText = getEditor().getText(); - if (inputText.equals(selectedItemText)) { - eh.handle(e); - } - }); - } - private void forceRedraw() { - setVisibleRowCount(Math.min(10, filteredItemsSize())); - if (filteredItemsSize() > 0) { + adjustVisibleRowCount(); + if (filteredListSize() > 0) { comboBoxListViewSkin.getPopupContent().autosize(); show(); } else { @@ -148,7 +166,23 @@ private void forceRedraw() { } } - public void deactivate() { - setOnHidden(null); + private void adjustVisibleRowCount() { + setVisibleRowCount(Math.min(10, filteredListSize())); + } + + private void prepareQueryChangedListener() { + queryChangedListener = (observable, oldQuery, query) -> { + UserThread.execute(() -> { + var exactMatch = unfilteredItems().stream().anyMatch(item -> asString(item).equalsIgnoreCase(query)); + if (!exactMatch) { + if (query.isEmpty()) + removeFilter(); + else + filterBy(query); + forceRedraw(); + } + }); + }; } + } diff --git a/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java index a7634741eeb..9c72075e691 100644 --- a/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java +++ b/desktop/src/main/java/bisq/desktop/components/paymentmethods/AssetsForm.java @@ -59,8 +59,6 @@ import javafx.util.StringConverter; -import java.util.Optional; - import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextField; import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon; import static bisq.desktop.util.FormBuilder.addLabelCheckBox; @@ -238,10 +236,9 @@ public String toString(TradeCurrency tradeCurrency) { @Override public TradeCurrency fromString(String s) { - Optional tradeCurrencyOptional = currencyComboBox.getItems().stream(). - filter(tradeCurrency -> tradeCurrency.getNameAndCode().equals(s)). - findAny(); - return tradeCurrencyOptional.orElse(null); + return currencyComboBox.getItems().stream(). + filter(item -> item.getNameAndCode().equals(s)). + findAny().orElse(null); } }); @@ -251,5 +248,7 @@ public TradeCurrency fromString(String s) { paymentAccount.setSingleTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem()); updateFromInputs(); }); + + ((SearchComboBox) currencyComboBox).activate(); } } diff --git a/desktop/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartView.java b/desktop/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartView.java index 66392ab4967..43292144913 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartView.java +++ b/desktop/src/main/java/bisq/desktop/main/market/offerbook/OfferBookChartView.java @@ -25,6 +25,7 @@ import bisq.desktop.components.AutoTooltipTableColumn; import bisq.desktop.components.ColoredDecimalPlacesWithZerosText; import bisq.desktop.components.PeerInfoIconSmall; +import bisq.desktop.components.SearchComboBox; import bisq.desktop.main.MainView; import bisq.desktop.main.offer.BuyOfferView; import bisq.desktop.main.offer.SellOfferView; @@ -93,7 +94,7 @@ import java.util.function.Function; import java.util.function.Supplier; -import static bisq.desktop.util.FormBuilder.addTopLabelComboBox; +import static bisq.desktop.util.FormBuilder.addTopLabelSearchComboBox; import static bisq.desktop.util.Layout.INITIAL_WINDOW_HEIGHT; @FxmlView @@ -108,7 +109,7 @@ public class OfferBookChartView extends ActivatableViewAndModel sellOfferTableView; private AreaChart areaChart; private AnchorPane chartPane; - private ComboBox currencyComboBox; + private SearchComboBox currencyComboBox; private Subscription tradeCurrencySubscriber; private final StringProperty volumeColumnLabel = new SimpleStringProperty(); private final StringProperty priceColumnLabel = new SimpleStringProperty(); @@ -146,8 +147,7 @@ public OfferBookChartView(OfferBookChartViewModel model, Navigation navigation, public void initialize() { createListener(); - final Tuple3> currencyComboBoxTuple = addTopLabelComboBox(Res.get("shared.currency"), - Res.get("list.currency.select"), 0); + final Tuple3> currencyComboBoxTuple = addTopLabelSearchComboBox(Res.get("shared.currency"), 0); this.currencyComboBox = currencyComboBoxTuple.third; this.currencyComboBox.setButtonCell(GUIUtil.getCurrencyListItemButtonCell(Res.get("shared.oneOffer"), Res.get("shared.multipleOffers"), model.preferences)); @@ -191,13 +191,14 @@ protected void activate() { model.setSelectedTabIndex(tabPaneSelectionModel.getSelectedIndex()); tabPaneSelectionModel.selectedIndexProperty().addListener(selectedTabIndexListener); - currencyComboBox.setItems(model.getCurrencyListItems()); - currencyComboBox.setVisibleRowCount(12); + currencyComboBox.setConverter(new CurrencyListItemStringConverter(currencyComboBox)); + currencyComboBox.getEditor().getStyleClass().add("combo-box-editor-bold"); - if (model.getSelectedCurrencyListItem().isPresent()) - currencyComboBox.getSelectionModel().select(model.getSelectedCurrencyListItem().get()); + if (currencyComboBox.getItems().isEmpty()) + currencyComboBox.setItems(model.getCurrencyListItems()); + currencyComboBox.setVisibleRowCount(10); - currencyComboBox.setOnAction(e -> { + currencyComboBox.setOnChangeConfirmed(e -> { CurrencyListItem selectedItem = currencyComboBox.getSelectionModel().getSelectedItem(); if (selectedItem != null) { model.onSetTradeCurrency(selectedItem.tradeCurrency); @@ -205,6 +206,11 @@ protected void activate() { } }); + if (model.getSelectedCurrencyListItem().isPresent()) + currencyComboBox.getSelectionModel().select(model.getSelectedCurrencyListItem().get()); + + currencyComboBox.activate(); + model.currencyListItems.getObservableList().addListener(currencyListItemsListener); model.getOfferBookListItems().addListener(changeListener); @@ -281,6 +287,28 @@ public Number fromString(String string) { updateChartData(); } + static class CurrencyListItemStringConverter extends StringConverter { + + private ComboBox comboBox; + + CurrencyListItemStringConverter(ComboBox comboBox) { + this.comboBox = comboBox; + } + + @Override + public String toString(CurrencyListItem currencyItem) { + return currencyItem != null ? currencyItem.codeDashNameString() : ""; + } + + @Override + public CurrencyListItem fromString(String s) { + Optional currencyItemOptional = comboBox.getItems().stream(). + filter(currencyItem -> currencyItem.codeDashNameString().equals(s)). + findAny(); + return currencyItemOptional.orElse(null); + } + } + private void createListener() { changeListener = c -> updateChartData(); @@ -309,11 +337,11 @@ private void createListener() { @Override protected void deactivate() { + currencyComboBox.deactivate(); model.getOfferBookListItems().removeListener(changeListener); tabPaneSelectionModel.selectedIndexProperty().removeListener(selectedTabIndexListener); model.currencyListItems.getObservableList().removeListener(currencyListItemsListener); tradeCurrencySubscriber.unsubscribe(); - currencyComboBox.setOnAction(null); buyOfferTableView.getSelectionModel().selectedItemProperty().removeListener(buyTableRowSelectionListener); sellOfferTableView.getSelectionModel().selectedItemProperty().removeListener(sellTableRowSelectionListener); } diff --git a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java index c0f835c9556..a8c3b4d69ae 100644 --- a/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java +++ b/desktop/src/main/java/bisq/desktop/main/market/trades/TradesChartsView.java @@ -23,6 +23,7 @@ import bisq.desktop.components.AutoTooltipTableColumn; import bisq.desktop.components.AutoTooltipToggleButton; import bisq.desktop.components.ColoredDecimalPlacesWithZerosText; +import bisq.desktop.components.SearchComboBox; import bisq.desktop.main.market.trades.charts.price.CandleStickChart; import bisq.desktop.main.market.trades.charts.volume.VolumeChart; import bisq.desktop.util.CurrencyListItem; @@ -91,7 +92,7 @@ import org.jetbrains.annotations.NotNull; -import static bisq.desktop.util.FormBuilder.addTopLabelComboBox; +import static bisq.desktop.util.FormBuilder.addTopLabelSearchComboBox; import static bisq.desktop.util.FormBuilder.getTopLabelWithVBox; @FxmlView @@ -100,7 +101,7 @@ public class TradesChartsView extends ActivatableViewAndModel tableView; - private ComboBox currencyComboBox; + private SearchComboBox currencyComboBox; private VolumeChart volumeChart; private CandleStickChart priceChart; private NumberAxis priceAxisX, priceAxisY, volumeAxisY, volumeAxisX; @@ -128,6 +129,7 @@ public class TradesChartsView extends ActivatableViewAndModel priceColumnLabelListener; private AnchorPane priceChartPane, volumeChartPane; + private static final int SHOW_ALL = 0; /////////////////////////////////////////////////////////////////////////////////////////// @@ -186,20 +188,27 @@ protected void activate() { model.setSelectedTabIndex(tabPaneSelectionModel.getSelectedIndex()); tabPaneSelectionModel.selectedIndexProperty().addListener(selectedTabIndexListener); - currencyComboBox.setItems(model.getCurrencyListItems()); - currencyComboBox.setVisibleRowCount(12); + currencyComboBox.setConverter(new CurrencyListItemStringConverter(currencyComboBox)); + currencyComboBox.getEditor().getStyleClass().add("combo-box-editor-bold"); + + if (currencyComboBox.getItems().isEmpty()) + currencyComboBox.setItems(model.getCurrencyListItems()); + currencyComboBox.setVisibleRowCount(10); if (model.showAllTradeCurrenciesProperty.get()) - currencyComboBox.getSelectionModel().select(0); + currencyComboBox.getSelectionModel().select(SHOW_ALL); else if (model.getSelectedCurrencyListItem().isPresent()) currencyComboBox.getSelectionModel().select(model.getSelectedCurrencyListItem().get()); - currencyComboBox.setOnAction(e -> { + currencyComboBox.setOnChangeConfirmed(e -> { + if (currencyComboBox.getEditor().getText().isEmpty()) + currencyComboBox.getSelectionModel().select(SHOW_ALL); CurrencyListItem selectedItem = currencyComboBox.getSelectionModel().getSelectedItem(); if (selectedItem != null) model.onSetTradeCurrency(selectedItem.tradeCurrency); }); + currencyComboBox.activate(); toggleGroup.getToggles().get(model.tickUnit.ordinal()).setSelected(true); @@ -266,7 +275,7 @@ else if (model.getSelectedCurrencyListItem().isPresent()) @Override protected void deactivate() { - currencyComboBox.setOnAction(null); + currencyComboBox.deactivate(); tabPaneSelectionModel.selectedIndexProperty().removeListener(selectedTabIndexListener); model.priceItems.removeListener(itemsChangeListener); @@ -289,6 +298,36 @@ protected void deactivate() { rootParent.heightProperty().removeListener(parentHeightListener); } + static class CurrencyListItemStringConverter extends StringConverter { + + private ComboBox comboBox; + + CurrencyListItemStringConverter(ComboBox comboBox) { + this.comboBox = comboBox; + } + + @Override + public String toString(CurrencyListItem currencyItem) { + return currencyItem != null ? currencyItem.codeDashNameString() : ""; + } + + @Override + public CurrencyListItem fromString(String query) { + if (comboBox.getItems().isEmpty()) + return null; + if (query.isEmpty()) + return specialShowAllItem(); + return comboBox.getItems().stream(). + filter(currencyItem -> currencyItem.codeDashNameString().equals(query)). + findAny(). + orElse(null); + } + + private CurrencyListItem specialShowAllItem() { + return comboBox.getItems().get(0); + } + } + /////////////////////////////////////////////////////////////////////////////////////////// // Chart @@ -467,16 +506,13 @@ public Number fromString(String string) { private HBox getToolBox() { - final Tuple3> currencyComboBoxTuple = addTopLabelComboBox(Res.get("shared.currency"), - Res.get("list.currency.select")); + final Tuple3> currencyComboBoxTuple = addTopLabelSearchComboBox(Res.get("shared.currency")); currencyComboBox = currencyComboBoxTuple.third; currencyComboBox.setButtonCell(GUIUtil.getCurrencyListItemButtonCell(Res.get("shared.trade"), Res.get("shared.trades"), model.preferences)); currencyComboBox.setCellFactory(GUIUtil.getCurrencyListItemCellFactory(Res.get("shared.trade"), Res.get("shared.trades"), model.preferences)); - currencyComboBox.setPromptText(Res.get("list.currency.select")); - Pane spacer = new Pane(); HBox.setHgrow(spacer, Priority.ALWAYS); diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java index 1f6efadec7c..127deb6f106 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookView.java @@ -27,6 +27,7 @@ import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.components.InfoAutoTooltipLabel; import bisq.desktop.components.PeerInfoIcon; +import bisq.desktop.components.SearchComboBox; import bisq.desktop.components.TitledGroupBg; import bisq.desktop.main.MainView; import bisq.desktop.main.account.AccountView; @@ -100,6 +101,7 @@ import javafx.collections.ListChangeListener; import javafx.util.Callback; +import javafx.util.StringConverter; import java.util.Comparator; import java.util.Optional; @@ -117,8 +119,8 @@ public class OfferBookView extends ActivatableViewAndModel currencyComboBox; - private ComboBox paymentMethodComboBox; + private SearchComboBox currencyComboBox; + private SearchComboBox paymentMethodComboBox; private AutoTooltipButton createOfferButton; private AutoTooltipTableColumn amountColumn, volumeColumn, marketColumn, priceColumn, avatarColumn; @@ -131,6 +133,8 @@ public class OfferBookView extends ActivatableViewAndModel priceFeedUpdateCounterListener; private Subscription currencySelectionSubscriber; + private static final int SHOW_ALL = 0; // index of the "Show all" item on ComboBox lists + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor, lifecycle /////////////////////////////////////////////////////////////////////////////////////////// @@ -163,10 +167,10 @@ public void initialize() { hBox.setSpacing(35); hBox.setPadding(new Insets(10, 0, 0, 0)); - final Tuple3> currencyBoxTuple = FormBuilder.addTopLabelComboBox( - Res.get("offerbook.filterByCurrency"), Res.get("list.currency.select")); - final Tuple3> paymentBoxTuple = FormBuilder.addTopLabelComboBox( - Res.get("offerbook.filterByPaymentMethod"), Res.get("shared.selectPaymentMethod")); + final Tuple3> currencyBoxTuple = FormBuilder.addTopLabelSearchComboBox( + Res.get("offerbook.filterByCurrency")); + final Tuple3> paymentBoxTuple = FormBuilder.addTopLabelSearchComboBox( + Res.get("offerbook.filterByPaymentMethod")); createOfferButton = new AutoTooltipButton(); createOfferButton.setMinHeight(40); @@ -189,11 +193,16 @@ public void initialize() { root.getChildren().add(anchorPane); currencyComboBox = currencyBoxTuple.third; + currencyComboBox.setCellFactory(GUIUtil.getTradeCurrencyCellFactory(Res.get("shared.oneOffer"), + Res.get("shared.multipleOffers"), + (model.getDirection() == OfferPayload.Direction.BUY ? model.getSellOfferCounts() : model.getBuyOfferCounts()))); + currencyComboBox.setButtonCell(GUIUtil.getTradeCurrencyButtonCell(Res.get("shared.oneOffer"), + Res.get("shared.multipleOffers"), + (model.getDirection() == OfferPayload.Direction.BUY ? model.getSellOfferCounts() : model.getBuyOfferCounts()))); paymentMethodComboBox = paymentBoxTuple.third; - paymentMethodComboBox.setVisibleRowCount(12); - paymentMethodComboBox.setButtonCell(GUIUtil.getPaymentMethodButtonCell()); paymentMethodComboBox.setCellFactory(GUIUtil.getPaymentMethodCellFactory()); + paymentMethodComboBox.setButtonCell(GUIUtil.getPaymentMethodButtonCell()); tableView = new TableView<>(); @@ -262,36 +271,52 @@ public void initialize() { @Override protected void activate() { - currencyComboBox.setItems(model.getTradeCurrencies()); - currencyComboBox.setCellFactory(GUIUtil.getTradeCurrencyCellFactory(Res.get("shared.oneOffer"), - Res.get("shared.multipleOffers"), - (model.getDirection() == OfferPayload.Direction.BUY ? model.getSellOfferCounts() : model.getBuyOfferCounts()))); + currencyComboBox.setConverter(new CurrencyStringConverter(currencyComboBox)); + currencyComboBox.getEditor().getStyleClass().add("combo-box-editor-bold"); - currencyComboBox.setButtonCell(GUIUtil.getTradeCurrencyButtonCell(Res.get("shared.oneOffer"), - Res.get("shared.multipleOffers"), - (model.getDirection() == OfferPayload.Direction.BUY ? model.getSellOfferCounts() : model.getBuyOfferCounts()))); + if (currencyComboBox.getItems().isEmpty()) + currencyComboBox.setItems(model.getTradeCurrencies()); + currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 10)); - currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 12)); - currencyComboBox.setOnAction(e -> model.onSetTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem())); + currencyComboBox.setOnChangeConfirmed(e -> { + if (currencyComboBox.getEditor().getText().isEmpty()) + currencyComboBox.getSelectionModel().select(SHOW_ALL); + model.onSetTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem()); + }); if (model.showAllTradeCurrenciesProperty.get()) - currencyComboBox.getSelectionModel().select(0); + currencyComboBox.getSelectionModel().select(SHOW_ALL); else currencyComboBox.getSelectionModel().select(model.getSelectedTradeCurrency()); + currencyComboBox.activate(); + volumeColumn.sortableProperty().bind(model.showAllTradeCurrenciesProperty.not()); priceColumn.sortableProperty().bind(model.showAllTradeCurrenciesProperty.not()); model.getOfferList().comparatorProperty().bind(tableView.comparatorProperty()); model.priceSortTypeProperty.addListener((observable, oldValue, newValue) -> priceColumn.setSortType(newValue)); priceColumn.setSortType(model.priceSortTypeProperty.get()); - paymentMethodComboBox.setItems(model.getPaymentMethods()); - paymentMethodComboBox.setOnAction(e -> model.onSetPaymentMethod(paymentMethodComboBox.getSelectionModel().getSelectedItem())); + paymentMethodComboBox.setConverter(new PaymentMethodStringConverter(paymentMethodComboBox)); + paymentMethodComboBox.getEditor().getStyleClass().add("combo-box-editor-bold"); + + if (paymentMethodComboBox.getItems().isEmpty()) + paymentMethodComboBox.setItems(model.getPaymentMethods()); + paymentMethodComboBox.setVisibleRowCount(Math.min(paymentMethodComboBox.getItems().size(), 10)); + + paymentMethodComboBox.setOnChangeConfirmed(e -> { + if (paymentMethodComboBox.getEditor().getText().isEmpty()) + paymentMethodComboBox.getSelectionModel().select(SHOW_ALL); + model.onSetPaymentMethod(paymentMethodComboBox.getSelectionModel().getSelectedItem()); + }); + if (model.showAllPaymentMethods) - paymentMethodComboBox.getSelectionModel().select(0); + paymentMethodComboBox.getSelectionModel().select(SHOW_ALL); else paymentMethodComboBox.getSelectionModel().select(model.selectedPaymentMethod); + paymentMethodComboBox.activate(); + createOfferButton.setOnAction(e -> onCreateOffer()); MonadicBinding currencySelectionBinding = EasyBind.combine( @@ -315,8 +340,8 @@ protected void activate() { return null; }); - currencySelectionSubscriber = currencySelectionBinding.subscribe((observable, oldValue, newValue) -> { - }); + + currencySelectionSubscriber = currencySelectionBinding.subscribe((observable, oldValue, newValue) -> {}); tableView.setItems(model.getOfferList()); @@ -328,8 +353,9 @@ protected void activate() { @Override protected void deactivate() { - currencyComboBox.setOnAction(null); - paymentMethodComboBox.setOnAction(null); + currencyComboBox.deactivate(); + paymentMethodComboBox.deactivate(); + createOfferButton.setOnAction(null); model.getOfferList().comparatorProperty().unbind(); @@ -344,6 +370,88 @@ protected void deactivate() { currencySelectionSubscriber.unsubscribe(); } + static class CurrencyStringConverter extends StringConverter { + private ComboBox comboBox; + + CurrencyStringConverter(ComboBox comboBox) { + this.comboBox = comboBox; + } + + @Override + public String toString(TradeCurrency item) { + return item != null ? asString(item) : ""; + } + + @Override + public TradeCurrency fromString(String query) { + if (comboBox.getItems().isEmpty()) + return null; + if (query.isEmpty()) + return specialShowAllItem(); + return comboBox.getItems().stream(). + filter(item -> asString(item).equals(query)). + findAny().orElse(null); + } + + private String asString(TradeCurrency item) { + if (isSpecialShowAllItem(item)) + return Res.get(GUIUtil.SHOW_ALL_FLAG); + if (isSpecialEditItem(item)) + return Res.get(GUIUtil.EDIT_FLAG); + return item.getCode() + " - " + item.getName(); + } + + private boolean isSpecialShowAllItem(TradeCurrency item) { + return item.getCode().equals(GUIUtil.SHOW_ALL_FLAG); + } + + private boolean isSpecialEditItem(TradeCurrency item) { + return item.getCode().equals(GUIUtil.EDIT_FLAG); + } + + private TradeCurrency specialShowAllItem() { + return comboBox.getItems().get(SHOW_ALL); + } + } + + static class PaymentMethodStringConverter extends StringConverter { + private ComboBox comboBox; + + PaymentMethodStringConverter(ComboBox comboBox) { + this.comboBox = comboBox; + } + + @Override + public String toString(PaymentMethod item) { + return item != null ? asString(item) : ""; + } + + @Override + public PaymentMethod fromString(String query) { + if (comboBox.getItems().isEmpty()) + return null; + if (query.isEmpty()) + return specialShowAllItem(); + return comboBox.getItems().stream(). + filter(item -> asString(item).equals(query)). + findAny().orElse(null); + } + + private String asString(PaymentMethod item) { + if (isSpecialShowAllItem(item)) + return Res.get(GUIUtil.SHOW_ALL_FLAG); + return Res.get(item.getId()); + } + + private boolean isSpecialShowAllItem(PaymentMethod item) { + return item.getId().equals(GUIUtil.SHOW_ALL_FLAG); + } + + private PaymentMethod specialShowAllItem() { + return comboBox.getItems().get(SHOW_ALL); + } + } + /////////////////////////////////////////////////////////////////////////////////////////// // API /////////////////////////////////////////////////////////////////////////////////////////// @@ -993,4 +1101,3 @@ public void updateItem(final OfferBookListItem newItem, boolean empty) { return column; } } - diff --git a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java index b6bcef8e242..dfc527a600c 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/offerbook/OfferBookViewModel.java @@ -280,6 +280,9 @@ private void applyPriceSortTypeProperty(String code) { } void onSetPaymentMethod(PaymentMethod paymentMethod) { + if (paymentMethod == null) + return; + showAllPaymentMethods = isShowAllEntry(paymentMethod.getId()); if (!showAllPaymentMethods) this.selectedPaymentMethod = paymentMethod; diff --git a/desktop/src/main/java/bisq/desktop/util/CurrencyListItem.java b/desktop/src/main/java/bisq/desktop/util/CurrencyListItem.java index 6d492f5a9ed..47d23d17224 100644 --- a/desktop/src/main/java/bisq/desktop/util/CurrencyListItem.java +++ b/desktop/src/main/java/bisq/desktop/util/CurrencyListItem.java @@ -17,6 +17,7 @@ package bisq.desktop.util; +import bisq.core.locale.Res; import bisq.core.locale.TradeCurrency; public class CurrencyListItem { @@ -55,4 +56,15 @@ public String toString() { ", numTrades=" + numTrades + '}'; } + + public String codeDashNameString() { + if (isSpecialShowAllItem()) + return Res.get(GUIUtil.SHOW_ALL_FLAG); + else + return tradeCurrency.getCode() + " - " + tradeCurrency.getName(); + } + + private boolean isSpecialShowAllItem() { + return tradeCurrency.getCode().equals(GUIUtil.SHOW_ALL_FLAG); + } } diff --git a/desktop/src/main/java/bisq/desktop/util/FormBuilder.java b/desktop/src/main/java/bisq/desktop/util/FormBuilder.java index 09ce2e8eb56..c71eb189b25 100644 --- a/desktop/src/main/java/bisq/desktop/util/FormBuilder.java +++ b/desktop/src/main/java/bisq/desktop/util/FormBuilder.java @@ -913,6 +913,21 @@ public static Tuple3> addTopLabelComboBox(String ti return new Tuple3<>(vBox, label, comboBox); } + public static Tuple3> addTopLabelSearchComboBox(String title) { + return addTopLabelSearchComboBox(title, 0); + } + + public static Tuple3> addTopLabelSearchComboBox(String title, int top) { + Label label = getTopLabel(title); + VBox vBox = getTopLabelVBox(top); + + final SearchComboBox comboBox = new SearchComboBox<>(); + + vBox.getChildren().addAll(label, comboBox); + + return new Tuple3<>(vBox, label, comboBox); + } + @NotNull private static VBox getTopLabelVBox(int top) { VBox vBox = new VBox();