mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-23 06:55:08 +01:00
Add dropdown autocomplete on Market, Buy, Sell tabs; fix #2226
Basic autocomplete feature for all dropdowns on the major tabs: * Market / Offer Book * Market / Trades * Buy BTC * Sell BTC Known limitations: * Autocomplete still missing from Settings, Account, DAO tabs * Minor UX glitches remain despite lots of debugging and polishing Related issues: * fix #2226 * partially addressed #2712 * superseded #112
This commit is contained in:
parent
3ccae6922d
commit
3f1b188e21
11 changed files with 441 additions and 225 deletions
|
@ -1234,6 +1234,13 @@ textfield */
|
||||||
-fx-alignment: center-left;
|
-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 {
|
.currency-label-small {
|
||||||
-fx-font-size: 0.692em;
|
-fx-font-size: 0.692em;
|
||||||
-fx-text-fill: -bs-rd-font-lighter;
|
-fx-text-fill: -bs-rd-font-lighter;
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.desktop.components;
|
||||||
|
|
||||||
|
import bisq.common.UserThread;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXComboBox;
|
||||||
|
import com.jfoenix.skins.JFXComboBoxListViewSkin;
|
||||||
|
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyEvent;
|
||||||
|
|
||||||
|
import javafx.event.Event;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements searchable dropdown (an autocomplete like experience).
|
||||||
|
*
|
||||||
|
* Clients must use setAutocompleteItems() instead of setItems().
|
||||||
|
*
|
||||||
|
* @param <T> type of the ComboBox item; in the simplest case this can be a String
|
||||||
|
*/
|
||||||
|
public class AutocompleteComboBox<T> extends JFXComboBox<T> {
|
||||||
|
private ArrayList<T> completeList;
|
||||||
|
private ArrayList<T> matchingList;
|
||||||
|
private JFXComboBoxListViewSkin comboBoxListViewSkin;
|
||||||
|
|
||||||
|
public AutocompleteComboBox() {
|
||||||
|
this(FXCollections.observableArrayList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private AutocompleteComboBox(ObservableList<T> items) {
|
||||||
|
super(items);
|
||||||
|
setEditable(true);
|
||||||
|
clearOnFocus();
|
||||||
|
setEmptySkinToGetMoreControlOverListView();
|
||||||
|
fixSpaceKey();
|
||||||
|
setAutocompleteItems(items);
|
||||||
|
reactToQueryChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the complete list of ComboBox items. Use this instead of setItems().
|
||||||
|
*/
|
||||||
|
public void setAutocompleteItems(List<T> items) {
|
||||||
|
completeList = new ArrayList<>(items);
|
||||||
|
matchingList = new ArrayList<>(completeList);
|
||||||
|
setValue(null);
|
||||||
|
getSelectionModel().clearSelection();
|
||||||
|
setItems(FXCollections.observableList(matchingList));
|
||||||
|
getEditor().setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<Event> 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear selection and query when ComboBox gets new focus. This is usually what user
|
||||||
|
// wants - to have a blank slate for a new search. The primary motivation though
|
||||||
|
// was to work around UX glitches related to (starting) editing text when combobox
|
||||||
|
// had specific item selected.
|
||||||
|
private void clearOnFocus() {
|
||||||
|
getEditor().focusedProperty().addListener((observableValue, hadFocus, hasFocus) -> {
|
||||||
|
if (!hadFocus && hasFocus) {
|
||||||
|
removeFilter();
|
||||||
|
forceRedraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ComboBox API does not provide enough control over the underlying
|
||||||
|
// ListView that is used as a dropdown. The only way to get this control
|
||||||
|
// is to set custom ListViewSkin. The default skin is null and so useless.
|
||||||
|
private void setEmptySkinToGetMoreControlOverListView() {
|
||||||
|
comboBoxListViewSkin = new JFXComboBoxListViewSkin<>(this);
|
||||||
|
setSkin(comboBoxListViewSkin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default pressing [SPACE] caused editor text to reset. The solution
|
||||||
|
// is to suppress relevant event on the underlying ListViewSkin.
|
||||||
|
private void fixSpaceKey() {
|
||||||
|
comboBoxListViewSkin.getPopupContent().addEventFilter(KeyEvent.ANY, (KeyEvent event) -> {
|
||||||
|
if (event.getCode() == KeyCode.SPACE)
|
||||||
|
event.consume();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filterBy(String query) {
|
||||||
|
ArrayList<T> newMatchingList = new ArrayList<>();
|
||||||
|
for (T item : completeList)
|
||||||
|
if (StringUtils.containsIgnoreCase(asString(item), query))
|
||||||
|
newMatchingList.add(item);
|
||||||
|
matchingList = newMatchingList;
|
||||||
|
setValue(null);
|
||||||
|
getSelectionModel().clearSelection();
|
||||||
|
setItems(FXCollections.observableList(matchingList));
|
||||||
|
int pos = getEditor().getCaretPosition();
|
||||||
|
if (pos > query.length()) pos = query.length();
|
||||||
|
getEditor().setText(query);
|
||||||
|
getEditor().positionCaret(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reactToQueryChanges() {
|
||||||
|
getEditor().addEventHandler(KeyEvent.KEY_RELEASED, (KeyEvent event) -> {
|
||||||
|
UserThread.execute(() -> {
|
||||||
|
String query = getEditor().getText();
|
||||||
|
var exactMatch = completeList.stream().anyMatch(item -> asString(item).equalsIgnoreCase(query));
|
||||||
|
if (!exactMatch) {
|
||||||
|
if (query.isEmpty())
|
||||||
|
removeFilter();
|
||||||
|
else
|
||||||
|
filterBy(query);
|
||||||
|
forceRedraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFilter() {
|
||||||
|
matchingList = new ArrayList<>(completeList);
|
||||||
|
setValue(null);
|
||||||
|
getSelectionModel().clearSelection();
|
||||||
|
setItems(FXCollections.observableList(matchingList));
|
||||||
|
getEditor().setText("");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forceRedraw() {
|
||||||
|
adjustVisibleRowCount();
|
||||||
|
if (matchingListSize() > 0) {
|
||||||
|
comboBoxListViewSkin.getPopupContent().autosize();
|
||||||
|
show();
|
||||||
|
} else {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustVisibleRowCount() {
|
||||||
|
setVisibleRowCount(Math.min(10, matchingListSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String asString(T item) {
|
||||||
|
return getConverter().toString(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int matchingListSize() {
|
||||||
|
return matchingList.size();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,154 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Bisq.
|
|
||||||
*
|
|
||||||
* Bisq is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.desktop.components;
|
|
||||||
|
|
||||||
import bisq.common.UserThread;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXComboBox;
|
|
||||||
import com.jfoenix.skins.JFXComboBoxListViewSkin;
|
|
||||||
|
|
||||||
import javafx.scene.control.skin.ComboBoxListViewSkin;
|
|
||||||
import javafx.scene.input.KeyCode;
|
|
||||||
import javafx.scene.input.KeyEvent;
|
|
||||||
|
|
||||||
import javafx.event.Event;
|
|
||||||
import javafx.event.EventHandler;
|
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.collections.transformation.FilteredList;
|
|
||||||
|
|
||||||
public class SearchComboBox<T> extends JFXComboBox<T> {
|
|
||||||
@SuppressWarnings("CanBeFinal")
|
|
||||||
private FilteredList<T> filteredList;
|
|
||||||
private ComboBoxListViewSkin comboBoxListViewSkin;
|
|
||||||
|
|
||||||
public SearchComboBox() {
|
|
||||||
this(FXCollections.observableArrayList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private SearchComboBox(ObservableList<T> items) {
|
|
||||||
super(items);
|
|
||||||
setEditable(true);
|
|
||||||
setEmptySkinToGetMoreControlOverListView();
|
|
||||||
fixSpaceKey();
|
|
||||||
wrapItemsInFilteredList();
|
|
||||||
reactToQueryChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The ComboBox API does not provide enough control over the underlying
|
|
||||||
// ListView that is used as a dropdown. The only way to get this control
|
|
||||||
// is to set custom ListViewSkin. Default skin is null and so useless.
|
|
||||||
private void setEmptySkinToGetMoreControlOverListView() {
|
|
||||||
comboBoxListViewSkin = new JFXComboBoxListViewSkin<>(this);
|
|
||||||
setSkin(comboBoxListViewSkin);
|
|
||||||
}
|
|
||||||
|
|
||||||
// By default pressing [SPACE] caused editor text to reset. The solution
|
|
||||||
// is to suppress relevant event on the underlying ListViewSkin.
|
|
||||||
private void fixSpaceKey() {
|
|
||||||
comboBoxListViewSkin.getPopupContent().addEventFilter(KeyEvent.ANY, (KeyEvent event) -> {
|
|
||||||
if (event.getCode() == KeyCode.SPACE)
|
|
||||||
event.consume();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Whenever ComboBox.setItems() is called we need to intercept it
|
|
||||||
// and wrap the physical list in a FilteredList view.
|
|
||||||
// The default predicate is null meaning no filtering occurs.
|
|
||||||
private void wrapItemsInFilteredList() {
|
|
||||||
itemsProperty().addListener((obsValue, oldList, newList) -> {
|
|
||||||
filteredList = new FilteredList<>(newList);
|
|
||||||
setItems(filteredList);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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<T> unfilteredItems() {
|
|
||||||
return (ObservableList<T>) filteredList.getSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String asString(T item) {
|
|
||||||
return getConverter().toString(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int filteredItemsSize() {
|
|
||||||
return filteredList.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeFilter() {
|
|
||||||
filteredList.setPredicate(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void filterBy(String query) {
|
|
||||||
filteredList.setPredicate(item ->
|
|
||||||
StringUtils.containsIgnoreCase(asString(item), 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<Event> 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) {
|
|
||||||
comboBoxListViewSkin.getPopupContent().autosize();
|
|
||||||
show();
|
|
||||||
} else {
|
|
||||||
hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deactivate() {
|
|
||||||
setOnHidden(null);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,7 +19,7 @@ package bisq.desktop.components.paymentmethods;
|
||||||
|
|
||||||
import bisq.desktop.components.InputTextField;
|
import bisq.desktop.components.InputTextField;
|
||||||
import bisq.desktop.components.NewBadge;
|
import bisq.desktop.components.NewBadge;
|
||||||
import bisq.desktop.components.SearchComboBox;
|
import bisq.desktop.components.AutocompleteComboBox;
|
||||||
import bisq.desktop.main.overlays.popups.Popup;
|
import bisq.desktop.main.overlays.popups.Popup;
|
||||||
import bisq.desktop.util.FormBuilder;
|
import bisq.desktop.util.FormBuilder;
|
||||||
import bisq.desktop.util.Layout;
|
import bisq.desktop.util.Layout;
|
||||||
|
@ -55,12 +55,8 @@ import javafx.scene.layout.VBox;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
|
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextField;
|
import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextField;
|
||||||
import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon;
|
import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon;
|
||||||
import static bisq.desktop.util.FormBuilder.addLabelCheckBox;
|
import static bisq.desktop.util.FormBuilder.addLabelCheckBox;
|
||||||
|
@ -219,7 +215,7 @@ public class AssetsForm extends PaymentMethodForm {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void addTradeCurrencyComboBox() {
|
protected void addTradeCurrencyComboBox() {
|
||||||
currencyComboBox = FormBuilder.<TradeCurrency>addLabelSearchComboBox(gridPane, ++gridRow, Res.get("payment.altcoin"),
|
currencyComboBox = FormBuilder.<TradeCurrency>addLabelAutocompleteComboBox(gridPane, ++gridRow, Res.get("payment.altcoin"),
|
||||||
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
|
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
|
||||||
currencyComboBox.setPromptText(Res.get("payment.select.altcoin"));
|
currencyComboBox.setPromptText(Res.get("payment.select.altcoin"));
|
||||||
currencyComboBox.setButtonCell(getComboBoxButtonCell(Res.get("payment.select.altcoin"), currencyComboBox));
|
currencyComboBox.setButtonCell(getComboBoxButtonCell(Res.get("payment.select.altcoin"), currencyComboBox));
|
||||||
|
@ -228,8 +224,9 @@ public class AssetsForm extends PaymentMethodForm {
|
||||||
currencyComboBox.setPromptText("");
|
currencyComboBox.setPromptText("");
|
||||||
});
|
});
|
||||||
|
|
||||||
currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getActiveSortedCryptoCurrencies(assetService, filterManager)));
|
((AutocompleteComboBox) currencyComboBox).setAutocompleteItems(CurrencyUtil.getActiveSortedCryptoCurrencies(assetService, filterManager));
|
||||||
currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 10));
|
currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 10));
|
||||||
|
|
||||||
currencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
|
currencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
|
||||||
@Override
|
@Override
|
||||||
public String toString(TradeCurrency tradeCurrency) {
|
public String toString(TradeCurrency tradeCurrency) {
|
||||||
|
@ -238,14 +235,13 @@ public class AssetsForm extends PaymentMethodForm {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TradeCurrency fromString(String s) {
|
public TradeCurrency fromString(String s) {
|
||||||
Optional<TradeCurrency> tradeCurrencyOptional = currencyComboBox.getItems().stream().
|
return currencyComboBox.getItems().stream().
|
||||||
filter(tradeCurrency -> tradeCurrency.getNameAndCode().equals(s)).
|
filter(item -> item.getNameAndCode().equals(s)).
|
||||||
findAny();
|
findAny().orElse(null);
|
||||||
return tradeCurrencyOptional.orElse(null);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
((SearchComboBox) currencyComboBox).setOnChangeConfirmed(e -> {
|
((AutocompleteComboBox) currencyComboBox).setOnChangeConfirmed(e -> {
|
||||||
addressInputTextField.resetValidation();
|
addressInputTextField.resetValidation();
|
||||||
addressInputTextField.validate();
|
addressInputTextField.validate();
|
||||||
paymentAccount.setSingleTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem());
|
paymentAccount.setSingleTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem());
|
||||||
|
|
|
@ -25,6 +25,7 @@ import bisq.desktop.components.AutoTooltipLabel;
|
||||||
import bisq.desktop.components.AutoTooltipTableColumn;
|
import bisq.desktop.components.AutoTooltipTableColumn;
|
||||||
import bisq.desktop.components.ColoredDecimalPlacesWithZerosText;
|
import bisq.desktop.components.ColoredDecimalPlacesWithZerosText;
|
||||||
import bisq.desktop.components.PeerInfoIconSmall;
|
import bisq.desktop.components.PeerInfoIconSmall;
|
||||||
|
import bisq.desktop.components.AutocompleteComboBox;
|
||||||
import bisq.desktop.main.MainView;
|
import bisq.desktop.main.MainView;
|
||||||
import bisq.desktop.main.offer.BuyOfferView;
|
import bisq.desktop.main.offer.BuyOfferView;
|
||||||
import bisq.desktop.main.offer.SellOfferView;
|
import bisq.desktop.main.offer.SellOfferView;
|
||||||
|
@ -93,7 +94,7 @@ import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import static bisq.desktop.util.FormBuilder.addTopLabelComboBox;
|
import static bisq.desktop.util.FormBuilder.addTopLabelAutocompleteComboBox;
|
||||||
import static bisq.desktop.util.Layout.INITIAL_WINDOW_HEIGHT;
|
import static bisq.desktop.util.Layout.INITIAL_WINDOW_HEIGHT;
|
||||||
|
|
||||||
@FxmlView
|
@FxmlView
|
||||||
|
@ -108,7 +109,7 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
|
||||||
private TableView<OfferListItem> sellOfferTableView;
|
private TableView<OfferListItem> sellOfferTableView;
|
||||||
private AreaChart<Number, Number> areaChart;
|
private AreaChart<Number, Number> areaChart;
|
||||||
private AnchorPane chartPane;
|
private AnchorPane chartPane;
|
||||||
private ComboBox<CurrencyListItem> currencyComboBox;
|
private AutocompleteComboBox<CurrencyListItem> currencyComboBox;
|
||||||
private Subscription tradeCurrencySubscriber;
|
private Subscription tradeCurrencySubscriber;
|
||||||
private final StringProperty volumeColumnLabel = new SimpleStringProperty();
|
private final StringProperty volumeColumnLabel = new SimpleStringProperty();
|
||||||
private final StringProperty priceColumnLabel = new SimpleStringProperty();
|
private final StringProperty priceColumnLabel = new SimpleStringProperty();
|
||||||
|
@ -146,11 +147,8 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
createListener();
|
createListener();
|
||||||
|
|
||||||
final Tuple3<VBox, Label, ComboBox<CurrencyListItem>> currencyComboBoxTuple = addTopLabelComboBox(Res.get("shared.currency"),
|
final Tuple3<VBox, Label, AutocompleteComboBox<CurrencyListItem>> currencyComboBoxTuple = addTopLabelAutocompleteComboBox(Res.get("shared.currency"), 0);
|
||||||
Res.get("list.currency.select"), 0);
|
|
||||||
this.currencyComboBox = currencyComboBoxTuple.third;
|
this.currencyComboBox = currencyComboBoxTuple.third;
|
||||||
this.currencyComboBox.setButtonCell(GUIUtil.getCurrencyListItemButtonCell(Res.get("shared.oneOffer"),
|
|
||||||
Res.get("shared.multipleOffers"), model.preferences));
|
|
||||||
this.currencyComboBox.setCellFactory(GUIUtil.getCurrencyListItemCellFactory(Res.get("shared.oneOffer"),
|
this.currencyComboBox.setCellFactory(GUIUtil.getCurrencyListItemCellFactory(Res.get("shared.oneOffer"),
|
||||||
Res.get("shared.multipleOffers"), model.preferences));
|
Res.get("shared.multipleOffers"), model.preferences));
|
||||||
|
|
||||||
|
@ -191,13 +189,19 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
|
||||||
model.setSelectedTabIndex(tabPaneSelectionModel.getSelectedIndex());
|
model.setSelectedTabIndex(tabPaneSelectionModel.getSelectedIndex());
|
||||||
tabPaneSelectionModel.selectedIndexProperty().addListener(selectedTabIndexListener);
|
tabPaneSelectionModel.selectedIndexProperty().addListener(selectedTabIndexListener);
|
||||||
|
|
||||||
currencyComboBox.setItems(model.getCurrencyListItems());
|
currencyComboBox.setConverter(new CurrencyListItemStringConverter(currencyComboBox));
|
||||||
currencyComboBox.setVisibleRowCount(12);
|
currencyComboBox.getEditor().getStyleClass().add("combo-box-editor-bold");
|
||||||
|
|
||||||
if (model.getSelectedCurrencyListItem().isPresent())
|
currencyComboBox.setAutocompleteItems(model.getCurrencyListItems());
|
||||||
|
currencyComboBox.setVisibleRowCount(10);
|
||||||
|
|
||||||
|
if (model.getSelectedCurrencyListItem().isPresent()) {
|
||||||
|
CurrencyListItem selectedItem = model.getSelectedCurrencyListItem().get();
|
||||||
currencyComboBox.getSelectionModel().select(model.getSelectedCurrencyListItem().get());
|
currencyComboBox.getSelectionModel().select(model.getSelectedCurrencyListItem().get());
|
||||||
|
currencyComboBox.getEditor().setText(new CurrencyListItemStringConverter(currencyComboBox).toString(selectedItem));
|
||||||
|
}
|
||||||
|
|
||||||
currencyComboBox.setOnAction(e -> {
|
currencyComboBox.setOnChangeConfirmed(e -> {
|
||||||
CurrencyListItem selectedItem = currencyComboBox.getSelectionModel().getSelectedItem();
|
CurrencyListItem selectedItem = currencyComboBox.getSelectionModel().getSelectedItem();
|
||||||
if (selectedItem != null) {
|
if (selectedItem != null) {
|
||||||
model.onSetTradeCurrency(selectedItem.tradeCurrency);
|
model.onSetTradeCurrency(selectedItem.tradeCurrency);
|
||||||
|
@ -281,6 +285,26 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
|
||||||
updateChartData();
|
updateChartData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class CurrencyListItemStringConverter extends StringConverter<CurrencyListItem> {
|
||||||
|
private ComboBox<CurrencyListItem> comboBox;
|
||||||
|
|
||||||
|
CurrencyListItemStringConverter(ComboBox<CurrencyListItem> comboBox) {
|
||||||
|
this.comboBox = comboBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(CurrencyListItem currencyItem) {
|
||||||
|
return currencyItem != null ? currencyItem.codeDashNameString() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CurrencyListItem fromString(String s) {
|
||||||
|
return comboBox.getItems().stream().
|
||||||
|
filter(currencyItem -> currencyItem.codeDashNameString().equals(s)).
|
||||||
|
findAny().orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void createListener() {
|
private void createListener() {
|
||||||
changeListener = c -> updateChartData();
|
changeListener = c -> updateChartData();
|
||||||
|
|
||||||
|
@ -313,7 +337,6 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
|
||||||
tabPaneSelectionModel.selectedIndexProperty().removeListener(selectedTabIndexListener);
|
tabPaneSelectionModel.selectedIndexProperty().removeListener(selectedTabIndexListener);
|
||||||
model.currencyListItems.getObservableList().removeListener(currencyListItemsListener);
|
model.currencyListItems.getObservableList().removeListener(currencyListItemsListener);
|
||||||
tradeCurrencySubscriber.unsubscribe();
|
tradeCurrencySubscriber.unsubscribe();
|
||||||
currencyComboBox.setOnAction(null);
|
|
||||||
buyOfferTableView.getSelectionModel().selectedItemProperty().removeListener(buyTableRowSelectionListener);
|
buyOfferTableView.getSelectionModel().selectedItemProperty().removeListener(buyTableRowSelectionListener);
|
||||||
sellOfferTableView.getSelectionModel().selectedItemProperty().removeListener(sellTableRowSelectionListener);
|
sellOfferTableView.getSelectionModel().selectedItemProperty().removeListener(sellTableRowSelectionListener);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import bisq.desktop.common.view.FxmlView;
|
||||||
import bisq.desktop.components.AutoTooltipLabel;
|
import bisq.desktop.components.AutoTooltipLabel;
|
||||||
import bisq.desktop.components.AutoTooltipTableColumn;
|
import bisq.desktop.components.AutoTooltipTableColumn;
|
||||||
import bisq.desktop.components.AutoTooltipToggleButton;
|
import bisq.desktop.components.AutoTooltipToggleButton;
|
||||||
|
import bisq.desktop.components.AutocompleteComboBox;
|
||||||
import bisq.desktop.components.ColoredDecimalPlacesWithZerosText;
|
import bisq.desktop.components.ColoredDecimalPlacesWithZerosText;
|
||||||
import bisq.desktop.main.market.trades.charts.price.CandleStickChart;
|
import bisq.desktop.main.market.trades.charts.price.CandleStickChart;
|
||||||
import bisq.desktop.main.market.trades.charts.volume.VolumeChart;
|
import bisq.desktop.main.market.trades.charts.volume.VolumeChart;
|
||||||
|
@ -91,7 +92,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import static bisq.desktop.util.FormBuilder.addTopLabelComboBox;
|
import static bisq.desktop.util.FormBuilder.addTopLabelAutocompleteComboBox;
|
||||||
import static bisq.desktop.util.FormBuilder.getTopLabelWithVBox;
|
import static bisq.desktop.util.FormBuilder.getTopLabelWithVBox;
|
||||||
|
|
||||||
@FxmlView
|
@FxmlView
|
||||||
|
@ -100,7 +101,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
private final BSFormatter formatter;
|
private final BSFormatter formatter;
|
||||||
|
|
||||||
private TableView<TradeStatistics2> tableView;
|
private TableView<TradeStatistics2> tableView;
|
||||||
private ComboBox<CurrencyListItem> currencyComboBox;
|
private AutocompleteComboBox<CurrencyListItem> currencyComboBox;
|
||||||
private VolumeChart volumeChart;
|
private VolumeChart volumeChart;
|
||||||
private CandleStickChart priceChart;
|
private CandleStickChart priceChart;
|
||||||
private NumberAxis priceAxisX, priceAxisY, volumeAxisY, volumeAxisX;
|
private NumberAxis priceAxisX, priceAxisY, volumeAxisY, volumeAxisX;
|
||||||
|
@ -128,6 +129,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
private Pane rootParent;
|
private Pane rootParent;
|
||||||
private ChangeListener<String> priceColumnLabelListener;
|
private ChangeListener<String> priceColumnLabelListener;
|
||||||
private AnchorPane priceChartPane, volumeChartPane;
|
private AnchorPane priceChartPane, volumeChartPane;
|
||||||
|
private static final int SHOW_ALL = 0;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -186,21 +188,26 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
model.setSelectedTabIndex(tabPaneSelectionModel.getSelectedIndex());
|
model.setSelectedTabIndex(tabPaneSelectionModel.getSelectedIndex());
|
||||||
tabPaneSelectionModel.selectedIndexProperty().addListener(selectedTabIndexListener);
|
tabPaneSelectionModel.selectedIndexProperty().addListener(selectedTabIndexListener);
|
||||||
|
|
||||||
currencyComboBox.setItems(model.getCurrencyListItems());
|
currencyComboBox.setConverter(new CurrencyStringConverter(currencyComboBox));
|
||||||
currencyComboBox.setVisibleRowCount(12);
|
currencyComboBox.getEditor().getStyleClass().add("combo-box-editor-bold");
|
||||||
|
|
||||||
|
currencyComboBox.setAutocompleteItems(model.getCurrencyListItems());
|
||||||
|
currencyComboBox.setVisibleRowCount(10);
|
||||||
|
|
||||||
if (model.showAllTradeCurrenciesProperty.get())
|
if (model.showAllTradeCurrenciesProperty.get())
|
||||||
currencyComboBox.getSelectionModel().select(0);
|
currencyComboBox.getSelectionModel().select(SHOW_ALL);
|
||||||
else if (model.getSelectedCurrencyListItem().isPresent())
|
else if (model.getSelectedCurrencyListItem().isPresent())
|
||||||
currencyComboBox.getSelectionModel().select(model.getSelectedCurrencyListItem().get());
|
currencyComboBox.getSelectionModel().select(model.getSelectedCurrencyListItem().get());
|
||||||
|
currencyComboBox.getEditor().setText(new CurrencyStringConverter(currencyComboBox).toString(currencyComboBox.getSelectionModel().getSelectedItem()));
|
||||||
|
|
||||||
currencyComboBox.setOnAction(e -> {
|
currencyComboBox.setOnChangeConfirmed(e -> {
|
||||||
|
if (currencyComboBox.getEditor().getText().isEmpty())
|
||||||
|
currencyComboBox.getSelectionModel().select(SHOW_ALL);
|
||||||
CurrencyListItem selectedItem = currencyComboBox.getSelectionModel().getSelectedItem();
|
CurrencyListItem selectedItem = currencyComboBox.getSelectionModel().getSelectedItem();
|
||||||
if (selectedItem != null)
|
if (selectedItem != null)
|
||||||
model.onSetTradeCurrency(selectedItem.tradeCurrency);
|
model.onSetTradeCurrency(selectedItem.tradeCurrency);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
toggleGroup.getToggles().get(model.tickUnit.ordinal()).setSelected(true);
|
toggleGroup.getToggles().get(model.tickUnit.ordinal()).setSelected(true);
|
||||||
|
|
||||||
model.priceItems.addListener(itemsChangeListener);
|
model.priceItems.addListener(itemsChangeListener);
|
||||||
|
@ -240,8 +247,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
currencySelectionSubscriber = currencySelectionBinding.subscribe((observable, oldValue, newValue) -> {
|
currencySelectionSubscriber = currencySelectionBinding.subscribe((observable, oldValue, newValue) -> {});
|
||||||
});
|
|
||||||
|
|
||||||
sortedList = new SortedList<>(model.tradeStatisticsByCurrency);
|
sortedList = new SortedList<>(model.tradeStatisticsByCurrency);
|
||||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||||
|
@ -266,8 +272,6 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void deactivate() {
|
protected void deactivate() {
|
||||||
currencyComboBox.setOnAction(null);
|
|
||||||
|
|
||||||
tabPaneSelectionModel.selectedIndexProperty().removeListener(selectedTabIndexListener);
|
tabPaneSelectionModel.selectedIndexProperty().removeListener(selectedTabIndexListener);
|
||||||
model.priceItems.removeListener(itemsChangeListener);
|
model.priceItems.removeListener(itemsChangeListener);
|
||||||
toggleGroup.selectedToggleProperty().removeListener(timeUnitChangeListener);
|
toggleGroup.selectedToggleProperty().removeListener(timeUnitChangeListener);
|
||||||
|
@ -289,6 +293,34 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
rootParent.heightProperty().removeListener(parentHeightListener);
|
rootParent.heightProperty().removeListener(parentHeightListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class CurrencyStringConverter extends StringConverter<CurrencyListItem> {
|
||||||
|
private ComboBox<CurrencyListItem> comboBox;
|
||||||
|
|
||||||
|
CurrencyStringConverter(ComboBox<CurrencyListItem> 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
|
// Chart
|
||||||
|
@ -467,16 +499,12 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
|
|
||||||
private HBox getToolBox() {
|
private HBox getToolBox() {
|
||||||
|
|
||||||
final Tuple3<VBox, Label, ComboBox<CurrencyListItem>> currencyComboBoxTuple = addTopLabelComboBox(Res.get("shared.currency"),
|
final Tuple3<VBox, Label, AutocompleteComboBox<CurrencyListItem>> currencyComboBoxTuple = addTopLabelAutocompleteComboBox(
|
||||||
Res.get("list.currency.select"));
|
Res.get("shared.currency"));
|
||||||
currencyComboBox = currencyComboBoxTuple.third;
|
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"),
|
currencyComboBox.setCellFactory(GUIUtil.getCurrencyListItemCellFactory(Res.get("shared.trade"),
|
||||||
Res.get("shared.trades"), model.preferences));
|
Res.get("shared.trades"), model.preferences));
|
||||||
|
|
||||||
currencyComboBox.setPromptText(Res.get("list.currency.select"));
|
|
||||||
|
|
||||||
Pane spacer = new Pane();
|
Pane spacer = new Pane();
|
||||||
HBox.setHgrow(spacer, Priority.ALWAYS);
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
|
|
|
@ -298,4 +298,3 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
||||||
void close();
|
void close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import bisq.desktop.common.view.FxmlView;
|
||||||
import bisq.desktop.components.AutoTooltipButton;
|
import bisq.desktop.components.AutoTooltipButton;
|
||||||
import bisq.desktop.components.AutoTooltipLabel;
|
import bisq.desktop.components.AutoTooltipLabel;
|
||||||
import bisq.desktop.components.AutoTooltipTableColumn;
|
import bisq.desktop.components.AutoTooltipTableColumn;
|
||||||
|
import bisq.desktop.components.AutocompleteComboBox;
|
||||||
import bisq.desktop.components.ColoredDecimalPlacesWithZerosText;
|
import bisq.desktop.components.ColoredDecimalPlacesWithZerosText;
|
||||||
import bisq.desktop.components.HyperlinkWithIcon;
|
import bisq.desktop.components.HyperlinkWithIcon;
|
||||||
import bisq.desktop.components.InfoAutoTooltipLabel;
|
import bisq.desktop.components.InfoAutoTooltipLabel;
|
||||||
|
@ -100,6 +101,7 @@ import javafx.beans.value.ObservableValue;
|
||||||
import javafx.collections.ListChangeListener;
|
import javafx.collections.ListChangeListener;
|
||||||
|
|
||||||
import javafx.util.Callback;
|
import javafx.util.Callback;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -117,8 +119,8 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
private final PrivateNotificationManager privateNotificationManager;
|
private final PrivateNotificationManager privateNotificationManager;
|
||||||
private final boolean useDevPrivilegeKeys;
|
private final boolean useDevPrivilegeKeys;
|
||||||
|
|
||||||
private ComboBox<TradeCurrency> currencyComboBox;
|
private AutocompleteComboBox<TradeCurrency> currencyComboBox;
|
||||||
private ComboBox<PaymentMethod> paymentMethodComboBox;
|
private AutocompleteComboBox<PaymentMethod> paymentMethodComboBox;
|
||||||
private AutoTooltipButton createOfferButton;
|
private AutoTooltipButton createOfferButton;
|
||||||
private AutoTooltipTableColumn<OfferBookListItem, OfferBookListItem> amountColumn, volumeColumn, marketColumn,
|
private AutoTooltipTableColumn<OfferBookListItem, OfferBookListItem> amountColumn, volumeColumn, marketColumn,
|
||||||
priceColumn, avatarColumn;
|
priceColumn, avatarColumn;
|
||||||
|
@ -130,6 +132,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
private ListChangeListener<OfferBookListItem> offerListListener;
|
private ListChangeListener<OfferBookListItem> offerListListener;
|
||||||
private ChangeListener<Number> priceFeedUpdateCounterListener;
|
private ChangeListener<Number> priceFeedUpdateCounterListener;
|
||||||
private Subscription currencySelectionSubscriber;
|
private Subscription currencySelectionSubscriber;
|
||||||
|
private static final int SHOW_ALL = 0;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor, lifecycle
|
// Constructor, lifecycle
|
||||||
|
@ -163,10 +166,10 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
hBox.setSpacing(35);
|
hBox.setSpacing(35);
|
||||||
hBox.setPadding(new Insets(10, 0, 0, 0));
|
hBox.setPadding(new Insets(10, 0, 0, 0));
|
||||||
|
|
||||||
final Tuple3<VBox, Label, ComboBox<TradeCurrency>> currencyBoxTuple = FormBuilder.addTopLabelComboBox(
|
final Tuple3<VBox, Label, AutocompleteComboBox<TradeCurrency>> currencyBoxTuple = FormBuilder.addTopLabelAutocompleteComboBox(
|
||||||
Res.get("offerbook.filterByCurrency"), Res.get("list.currency.select"));
|
Res.get("offerbook.filterByCurrency"));
|
||||||
final Tuple3<VBox, Label, ComboBox<PaymentMethod>> paymentBoxTuple = FormBuilder.addTopLabelComboBox(
|
final Tuple3<VBox, Label, AutocompleteComboBox<PaymentMethod>> paymentBoxTuple = FormBuilder.addTopLabelAutocompleteComboBox(
|
||||||
Res.get("offerbook.filterByPaymentMethod"), Res.get("shared.selectPaymentMethod"));
|
Res.get("offerbook.filterByPaymentMethod"));
|
||||||
|
|
||||||
createOfferButton = new AutoTooltipButton();
|
createOfferButton = new AutoTooltipButton();
|
||||||
createOfferButton.setMinHeight(40);
|
createOfferButton.setMinHeight(40);
|
||||||
|
@ -191,8 +194,6 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
currencyComboBox = currencyBoxTuple.third;
|
currencyComboBox = currencyBoxTuple.third;
|
||||||
|
|
||||||
paymentMethodComboBox = paymentBoxTuple.third;
|
paymentMethodComboBox = paymentBoxTuple.third;
|
||||||
paymentMethodComboBox.setVisibleRowCount(12);
|
|
||||||
paymentMethodComboBox.setButtonCell(GUIUtil.getPaymentMethodButtonCell());
|
|
||||||
paymentMethodComboBox.setCellFactory(GUIUtil.getPaymentMethodCellFactory());
|
paymentMethodComboBox.setCellFactory(GUIUtil.getPaymentMethodCellFactory());
|
||||||
|
|
||||||
tableView = new TableView<>();
|
tableView = new TableView<>();
|
||||||
|
@ -262,22 +263,27 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void activate() {
|
protected void activate() {
|
||||||
currencyComboBox.setItems(model.getTradeCurrencies());
|
|
||||||
currencyComboBox.setCellFactory(GUIUtil.getTradeCurrencyCellFactory(Res.get("shared.oneOffer"),
|
currencyComboBox.setCellFactory(GUIUtil.getTradeCurrencyCellFactory(Res.get("shared.oneOffer"),
|
||||||
Res.get("shared.multipleOffers"),
|
Res.get("shared.multipleOffers"),
|
||||||
(model.getDirection() == OfferPayload.Direction.BUY ? model.getSellOfferCounts() : model.getBuyOfferCounts())));
|
(model.getDirection() == OfferPayload.Direction.BUY ? model.getSellOfferCounts() : model.getBuyOfferCounts())));
|
||||||
|
|
||||||
currencyComboBox.setButtonCell(GUIUtil.getTradeCurrencyButtonCell(Res.get("shared.oneOffer"),
|
currencyComboBox.setConverter(new CurrencyStringConverter(currencyComboBox));
|
||||||
Res.get("shared.multipleOffers"),
|
currencyComboBox.getEditor().getStyleClass().add("combo-box-editor-bold");
|
||||||
(model.getDirection() == OfferPayload.Direction.BUY ? model.getSellOfferCounts() : model.getBuyOfferCounts())));
|
|
||||||
|
|
||||||
currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 12));
|
currencyComboBox.setAutocompleteItems(model.getTradeCurrencies());
|
||||||
currencyComboBox.setOnAction(e -> model.onSetTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem()));
|
currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 10));
|
||||||
|
|
||||||
|
currencyComboBox.setOnChangeConfirmed(e -> {
|
||||||
|
if (currencyComboBox.getEditor().getText().isEmpty())
|
||||||
|
currencyComboBox.getSelectionModel().select(SHOW_ALL);
|
||||||
|
model.onSetTradeCurrency(currencyComboBox.getSelectionModel().getSelectedItem());
|
||||||
|
});
|
||||||
|
|
||||||
if (model.showAllTradeCurrenciesProperty.get())
|
if (model.showAllTradeCurrenciesProperty.get())
|
||||||
currencyComboBox.getSelectionModel().select(0);
|
currencyComboBox.getSelectionModel().select(SHOW_ALL);
|
||||||
else
|
else
|
||||||
currencyComboBox.getSelectionModel().select(model.getSelectedTradeCurrency());
|
currencyComboBox.getSelectionModel().select(model.getSelectedTradeCurrency());
|
||||||
|
currencyComboBox.getEditor().setText(new CurrencyStringConverter(currencyComboBox).toString(currencyComboBox.getSelectionModel().getSelectedItem()));
|
||||||
|
|
||||||
volumeColumn.sortableProperty().bind(model.showAllTradeCurrenciesProperty.not());
|
volumeColumn.sortableProperty().bind(model.showAllTradeCurrenciesProperty.not());
|
||||||
priceColumn.sortableProperty().bind(model.showAllTradeCurrenciesProperty.not());
|
priceColumn.sortableProperty().bind(model.showAllTradeCurrenciesProperty.not());
|
||||||
|
@ -285,12 +291,23 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
model.priceSortTypeProperty.addListener((observable, oldValue, newValue) -> priceColumn.setSortType(newValue));
|
model.priceSortTypeProperty.addListener((observable, oldValue, newValue) -> priceColumn.setSortType(newValue));
|
||||||
priceColumn.setSortType(model.priceSortTypeProperty.get());
|
priceColumn.setSortType(model.priceSortTypeProperty.get());
|
||||||
|
|
||||||
paymentMethodComboBox.setItems(model.getPaymentMethods());
|
paymentMethodComboBox.setConverter(new PaymentMethodStringConverter(paymentMethodComboBox));
|
||||||
paymentMethodComboBox.setOnAction(e -> model.onSetPaymentMethod(paymentMethodComboBox.getSelectionModel().getSelectedItem()));
|
paymentMethodComboBox.getEditor().getStyleClass().add("combo-box-editor-bold");
|
||||||
|
|
||||||
|
paymentMethodComboBox.setAutocompleteItems(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)
|
if (model.showAllPaymentMethods)
|
||||||
paymentMethodComboBox.getSelectionModel().select(0);
|
paymentMethodComboBox.getSelectionModel().select(SHOW_ALL);
|
||||||
else
|
else
|
||||||
paymentMethodComboBox.getSelectionModel().select(model.selectedPaymentMethod);
|
paymentMethodComboBox.getSelectionModel().select(model.selectedPaymentMethod);
|
||||||
|
paymentMethodComboBox.getEditor().setText(new PaymentMethodStringConverter(paymentMethodComboBox).toString(paymentMethodComboBox.getSelectionModel().getSelectedItem()));
|
||||||
|
|
||||||
createOfferButton.setOnAction(e -> onCreateOffer());
|
createOfferButton.setOnAction(e -> onCreateOffer());
|
||||||
|
|
||||||
|
@ -315,8 +332,8 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
currencySelectionSubscriber = currencySelectionBinding.subscribe((observable, oldValue, newValue) -> {
|
|
||||||
});
|
currencySelectionSubscriber = currencySelectionBinding.subscribe((observable, oldValue, newValue) -> {});
|
||||||
|
|
||||||
tableView.setItems(model.getOfferList());
|
tableView.setItems(model.getOfferList());
|
||||||
|
|
||||||
|
@ -328,8 +345,6 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void deactivate() {
|
protected void deactivate() {
|
||||||
currencyComboBox.setOnAction(null);
|
|
||||||
paymentMethodComboBox.setOnAction(null);
|
|
||||||
createOfferButton.setOnAction(null);
|
createOfferButton.setOnAction(null);
|
||||||
model.getOfferList().comparatorProperty().unbind();
|
model.getOfferList().comparatorProperty().unbind();
|
||||||
|
|
||||||
|
@ -344,6 +359,88 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
currencySelectionSubscriber.unsubscribe();
|
currencySelectionSubscriber.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class CurrencyStringConverter extends StringConverter<TradeCurrency> {
|
||||||
|
private ComboBox<TradeCurrency> comboBox;
|
||||||
|
|
||||||
|
CurrencyStringConverter(ComboBox<TradeCurrency> 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<PaymentMethod> {
|
||||||
|
private ComboBox<PaymentMethod> comboBox;
|
||||||
|
|
||||||
|
PaymentMethodStringConverter(ComboBox<PaymentMethod> 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
|
// API
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -993,4 +1090,3 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||||
return column;
|
return column;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -280,6 +280,9 @@ class OfferBookViewModel extends ActivatableViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSetPaymentMethod(PaymentMethod paymentMethod) {
|
void onSetPaymentMethod(PaymentMethod paymentMethod) {
|
||||||
|
if (paymentMethod == null)
|
||||||
|
return;
|
||||||
|
|
||||||
showAllPaymentMethods = isShowAllEntry(paymentMethod.getId());
|
showAllPaymentMethods = isShowAllEntry(paymentMethod.getId());
|
||||||
if (!showAllPaymentMethods)
|
if (!showAllPaymentMethods)
|
||||||
this.selectedPaymentMethod = paymentMethod;
|
this.selectedPaymentMethod = paymentMethod;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package bisq.desktop.util;
|
package bisq.desktop.util;
|
||||||
|
|
||||||
|
import bisq.core.locale.Res;
|
||||||
import bisq.core.locale.TradeCurrency;
|
import bisq.core.locale.TradeCurrency;
|
||||||
|
|
||||||
public class CurrencyListItem {
|
public class CurrencyListItem {
|
||||||
|
@ -55,4 +56,15 @@ public class CurrencyListItem {
|
||||||
", numTrades=" + numTrades +
|
", 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ import bisq.desktop.components.InfoInputTextField;
|
||||||
import bisq.desktop.components.InfoTextField;
|
import bisq.desktop.components.InfoTextField;
|
||||||
import bisq.desktop.components.InputTextField;
|
import bisq.desktop.components.InputTextField;
|
||||||
import bisq.desktop.components.PasswordTextField;
|
import bisq.desktop.components.PasswordTextField;
|
||||||
import bisq.desktop.components.SearchComboBox;
|
import bisq.desktop.components.AutocompleteComboBox;
|
||||||
import bisq.desktop.components.TextFieldWithCopyIcon;
|
import bisq.desktop.components.TextFieldWithCopyIcon;
|
||||||
import bisq.desktop.components.TextFieldWithIcon;
|
import bisq.desktop.components.TextFieldWithIcon;
|
||||||
import bisq.desktop.components.TitledGroupBg;
|
import bisq.desktop.components.TitledGroupBg;
|
||||||
|
@ -913,6 +913,21 @@ public class FormBuilder {
|
||||||
return new Tuple3<>(vBox, label, comboBox);
|
return new Tuple3<>(vBox, label, comboBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> Tuple3<VBox, Label, AutocompleteComboBox<T>> addTopLabelAutocompleteComboBox(String title) {
|
||||||
|
return addTopLabelAutocompleteComboBox(title, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Tuple3<VBox, Label, AutocompleteComboBox<T>> addTopLabelAutocompleteComboBox(String title, int top) {
|
||||||
|
Label label = getTopLabel(title);
|
||||||
|
VBox vBox = getTopLabelVBox(top);
|
||||||
|
|
||||||
|
final AutocompleteComboBox<T> comboBox = new AutocompleteComboBox<>();
|
||||||
|
|
||||||
|
vBox.getChildren().addAll(label, comboBox);
|
||||||
|
|
||||||
|
return new Tuple3<>(vBox, label, comboBox);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static VBox getTopLabelVBox(int top) {
|
private static VBox getTopLabelVBox(int top) {
|
||||||
VBox vBox = new VBox();
|
VBox vBox = new VBox();
|
||||||
|
@ -984,15 +999,12 @@ public class FormBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Label + SearchComboBox
|
// Label + AutocompleteComboBox
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public static <T> Tuple2<Label, ComboBox<T>> addLabelSearchComboBox(GridPane gridPane, int rowIndex, String title, double top) {
|
public static <T> Tuple2<Label, ComboBox<T>> addLabelAutocompleteComboBox(GridPane gridPane, int rowIndex, String title, double top) {
|
||||||
|
AutocompleteComboBox<T> comboBox = new AutocompleteComboBox<>();
|
||||||
SearchComboBox<T> comboBox = new SearchComboBox<>();
|
|
||||||
|
|
||||||
final Tuple2<Label, VBox> labelVBoxTuple2 = addTopLabelWithVBox(gridPane, rowIndex, title, comboBox, top);
|
final Tuple2<Label, VBox> labelVBoxTuple2 = addTopLabelWithVBox(gridPane, rowIndex, title, comboBox, top);
|
||||||
|
|
||||||
return new Tuple2<>(labelVBoxTuple2.first, comboBox);
|
return new Tuple2<>(labelVBoxTuple2.first, comboBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue