diff --git a/core/src/main/java/haveno/core/util/VolumeUtil.java b/core/src/main/java/haveno/core/util/VolumeUtil.java index b74a70f226..050b7b1a4d 100644 --- a/core/src/main/java/haveno/core/util/VolumeUtil.java +++ b/core/src/main/java/haveno/core/util/VolumeUtil.java @@ -51,6 +51,7 @@ import org.bitcoinj.utils.MonetaryFormat; import java.math.BigInteger; import java.text.DecimalFormat; import java.text.NumberFormat; +import java.util.Collection; import java.util.Locale; public class VolumeUtil { @@ -187,4 +188,35 @@ public class VolumeUtil { private static MonetaryFormat getMonetaryFormat(String currencyCode) { return CurrencyUtil.isVolumeRoundedToNearestUnit(currencyCode) ? VOLUME_FORMAT_UNIT : VOLUME_FORMAT_PRECISE; } + + public static Volume sum(Collection<Volume> volumes) { + if (volumes == null || volumes.isEmpty()) { + return null; + } + Volume sum = null; + for (Volume volume : volumes) { + if (sum == null) { + sum = volume; + } else { + if (!sum.getCurrencyCode().equals(volume.getCurrencyCode())) { + throw new IllegalArgumentException("Cannot sum volumes with different currencies"); + } + sum = add(sum, volume); + } + } + return sum; + } + + public static Volume add(Volume volume1, Volume volume2) { + if (volume1 == null) return volume2; + if (volume2 == null) return volume1; + if (!volume1.getCurrencyCode().equals(volume2.getCurrencyCode())) { + throw new IllegalArgumentException("Cannot add volumes with different currencies"); + } + if (volume1.getMonetary() instanceof CryptoMoney) { + return new Volume(((CryptoMoney) volume1.getMonetary()).add((CryptoMoney) volume2.getMonetary())); + } else { + return new Volume(((TraditionalMoney) volume1.getMonetary()).add((TraditionalMoney) volume2.getMonetary())); + } + } } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index c214a016dd..a0fd2962b1 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -315,9 +315,6 @@ market.tabs.spreadCurrency=Offers by Currency market.tabs.spreadPayment=Offers by Payment Method market.tabs.trades=Trades -# OfferBookView -market.offerBook.filterPrompt=Filter - # OfferBookChartView market.offerBook.sellOffersHeaderLabel=Sell {0} to market.offerBook.buyOffersHeaderLabel=Buy {0} from @@ -410,8 +407,10 @@ shared.notSigned.noNeedAlts=Cryptocurrency accounts do not feature signing or ag offerbook.nrOffers=No. of offers: {0} offerbook.volume={0} (min - max) +offerbook.volumeTotal={0} {1} offerbook.deposit=Deposit XMR (%) offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.XMRTotal=XMR ({0}) offerbook.createNewOffer=Create offer to {0} {1} offerbook.createOfferDisabled.tooltip=You can only create one offer at a time @@ -1162,8 +1161,6 @@ support.tab.refund.support=Refund support.tab.arbitration.support=Arbitration support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets -support.filter=Search disputes -support.filter.prompt=Enter trade ID, date, onion address or account data support.tab.SignedOffers=Signed Offers support.prompt.signedOffer.penalty.msg=This will charge the maker a penalty fee and return the remaining trade funds to their wallet. Are you sure you want to send?\n\n\ Offer ID: {0}\n\ @@ -1337,6 +1334,7 @@ setting.preferences.displayOptions=Display options setting.preferences.showOwnOffers=Show my own offers in offer book setting.preferences.useAnimations=Use animations setting.preferences.useDarkMode=Use dark mode +setting.preferences.useLightMode=Use light mode setting.preferences.sortWithNumOffers=Sort market lists with no. of offers/trades setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -2012,6 +2010,7 @@ offerDetailsWindow.confirm.takerCrypto=Confirm: Take offer to {0} {1} offerDetailsWindow.creationDate=Creation date offerDetailsWindow.makersOnion=Maker's onion address offerDetailsWindow.challenge=Offer passphrase +offerDetailsWindow.challenge.copy=Copy passphrase to share with your peer qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index ce414afabd..53658b4038 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -315,7 +315,6 @@ market.tabs.spreadPayment=Nabídky podle způsobů platby market.tabs.trades=Obchody # OfferBookView -market.offerBook.filterPrompt=Filtr # OfferBookChartView market.offerBook.sellOffersHeaderLabel=Prodat {0} kupujícímu @@ -1125,8 +1124,6 @@ support.tab.refund.support=Vrácení peněz support.tab.arbitration.support=Arbitráž support.tab.legacyArbitration.support=Starší arbitráž support.tab.ArbitratorsSupportTickets=Úkoly pro {0} -support.filter=Hledat spory -support.filter.prompt=Zadejte ID obchodu, datum, onion adresu nebo údaje o účtu support.tab.SignedOffers=Podepsané nabídky support.prompt.signedOffer.penalty.msg=Tím se tvůrci účtuje sankční poplatek a zbývající prostředky z obchodu se vrátí do jeho peněženky. Jste si jisti, že chcete odeslat?\n\n\ ID nabídky: {0}\n\ @@ -1300,6 +1297,7 @@ setting.preferences.displayOptions=Zobrazit možnosti setting.preferences.showOwnOffers=Zobrazit mé vlastní nabídky v seznamu nabídek setting.preferences.useAnimations=Použít animace setting.preferences.useDarkMode=Použít tmavý režim +setting.preferences.useLightMode=Použijte světlý režim setting.preferences.sortWithNumOffers=Seřadit seznamy trhů s počtem nabídek/obchodů setting.preferences.onlyShowPaymentMethodsFromAccount=Skrýt nepodporované způsoby platby setting.preferences.denyApiTaker=Odmítat příjemce, kteří používají API @@ -1975,6 +1973,7 @@ offerDetailsWindow.confirm.takerCrypto=Potvrďte: Přijmout nabídku {0} {1} offerDetailsWindow.creationDate=Datum vzniku offerDetailsWindow.makersOnion=Onion adresa tvůrce offerDetailsWindow.challenge=Passphrase nabídky +offerDetailsWindow.challenge.copy=Zkopírujte přístupovou frázi pro sdílení s protějškem qRCodeWindow.headline=QR Kód qRCodeWindow.msg=Použijte tento QR kód k financování vaší peněženky Haveno z vaší externí peněženky. diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 43b664b1cf..57fd93df67 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -927,8 +927,6 @@ support.tab.mediation.support=Mediation support.tab.arbitration.support=Vermittlung support.tab.legacyArbitration.support=Legacy-Vermittlung support.tab.ArbitratorsSupportTickets={0} Tickets -support.filter=Konflikte durchsuchen -support.filter.prompt=Tragen sie Handel ID, Datum, Onion Adresse oder Kontodaten support.sigCheck.button=Signatur überprüfen support.sigCheck.popup.info=Fügen Sie die Zusammenfassungsnachricht des Schiedsverfahrens ein. Mit diesem Tool kann jeder Benutzer überprüfen, ob die Unterschrift des Schiedsrichters mit der Zusammenfassungsnachricht übereinstimmt. @@ -1035,6 +1033,7 @@ setting.preferences.displayOptions=Darstellungsoptionen setting.preferences.showOwnOffers=Eigenen Angebote im Angebotsbuch zeigen setting.preferences.useAnimations=Animationen abspielen setting.preferences.useDarkMode=Nacht-Modus benutzen +setting.preferences.useLightMode=Leichtmodus verwenden setting.preferences.sortWithNumOffers=Marktlisten nach Anzahl der Angebote/Trades sortieren setting.preferences.onlyShowPaymentMethodsFromAccount=Nicht unterstützte Zahlungsmethoden ausblenden setting.preferences.denyApiTaker=Taker die das API nutzen vermeiden @@ -1477,6 +1476,7 @@ offerDetailsWindow.confirm.taker=Bestätigen: Angebot annehmen monero zu {0} offerDetailsWindow.creationDate=Erstellungsdatum offerDetailsWindow.makersOnion=Onion-Adresse des Erstellers offerDetailsWindow.challenge=Angebots-Passphrase +offerDetailsWindow.challenge.copy=Passphrase kopieren, um sie mit Ihrem Handelspartner zu teilen qRCodeWindow.headline=QR Code qRCodeWindow.msg=Bitte nutzen Sie diesen QR Code um Ihr Haveno Wallet von Ihrem externen Wallet aufzuladen. diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index a4001cb075..b5754592dd 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -928,8 +928,6 @@ support.tab.mediation.support=Mediación support.tab.arbitration.support=Arbitraje support.tab.legacyArbitration.support=Legado de arbitraje support.tab.ArbitratorsSupportTickets=Tickets de {0} -support.filter=Buscar disputas -support.filter.prompt=Introduzca ID de transacción, fecha, dirección onion o datos de cuenta. support.sigCheck.button=Comprobar firma support.sigCheck.popup.info=Pegue el mensaje resumido del proceso de arbitraje. Con esta herramienta, cualquier usuario puede verificar si la firma del árbitro coincide con el mensaje resumido. @@ -1036,6 +1034,7 @@ setting.preferences.displayOptions=Mostrar opciones setting.preferences.showOwnOffers=Mostrar mis propias ofertas en el libro de ofertas setting.preferences.useAnimations=Usar animaciones setting.preferences.useDarkMode=Usar modo oscuro +setting.preferences.useLightMode=Usar modo claro setting.preferences.sortWithNumOffers=Ordenar listas de mercado por número de ofertas/intercambios setting.preferences.onlyShowPaymentMethodsFromAccount=Ocultar métodos de pago no soportados setting.preferences.denyApiTaker=Denegar tomadores usando la misma API @@ -1478,6 +1477,7 @@ offerDetailsWindow.confirm.taker=Confirmar: Tomar oferta {0} monero offerDetailsWindow.creationDate=Fecha de creación offerDetailsWindow.makersOnion=Dirección onion del creador offerDetailsWindow.challenge=Frase de contraseña de la oferta +offerDetailsWindow.challenge.copy=Copiar frase de contraseña para compartir con tu contraparte qRCodeWindow.headline=Código QR qRCodeWindow.msg=Por favor, utilice este código QR para fondear su billetera Haveno desde su billetera externa. diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 891dc297da..44693d7e01 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=فیلتر shared.enabled=Enabled @@ -926,8 +926,6 @@ support.tab.mediation.support=Mediation support.tab.arbitration.support=Arbitration support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets -support.filter=Search disputes -support.filter.prompt=Enter trade ID, date, onion address or account data support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1031,7 +1029,8 @@ setting.preferences.addCrypto=افزودن آلتکوین setting.preferences.displayOptions=نمایش گزینهها setting.preferences.showOwnOffers=نمایش پیشنهادهای من در دفتر پیشنهاد setting.preferences.useAnimations=استفاده از انیمیشنها -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=حالت تاریک را استفاده کنید +setting.preferences.useLightMode=حالت روشن را استفاده کنید setting.preferences.sortWithNumOffers=مرتب سازی لیستها با تعداد معاملات/پیشنهادها setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1473,6 +1472,7 @@ offerDetailsWindow.confirm.taker=تأیید: پیشنهاد را به {0} بپذ offerDetailsWindow.creationDate=تاریخ ایجاد offerDetailsWindow.makersOnion=آدرس Onion سفارش گذار offerDetailsWindow.challenge=Passphrase de l'offre +offerDetailsWindow.challenge.copy=عبارت عبور را برای به اشتراکگذاری با همتا کپی کنید qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 0a0e1274ad..478ab4c6c3 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -929,8 +929,6 @@ support.tab.mediation.support=Médiation support.tab.arbitration.support=Arbitrage support.tab.legacyArbitration.support=Conclusion d'arbitrage support.tab.ArbitratorsSupportTickets=Tickets de {0} -support.filter=Chercher les litiges -support.filter.prompt=Saisissez l'ID du trade, la date, l'adresse "onion" ou les données du compte. support.sigCheck.button=Vérifier la signature support.sigCheck.popup.info=Collez le message récapitulatif du processus d'arbitrage. Avec cet outil, n'importe quel utilisateur peut vérifier si la signature de l'arbitre correspond au message récapitulatif. @@ -1037,6 +1035,7 @@ setting.preferences.displayOptions=Afficher les options setting.preferences.showOwnOffers=Montrer mes ordres dans le livre des ordres setting.preferences.useAnimations=Utiliser des animations setting.preferences.useDarkMode=Utiliser le mode sombre +setting.preferences.useLightMode=Utiliser le mode clair setting.preferences.sortWithNumOffers=Trier les listes de marché avec le nombre d'ordres/de transactions setting.preferences.onlyShowPaymentMethodsFromAccount=Masquer les méthodes de paiement non supportées setting.preferences.denyApiTaker=Refuser les preneurs utilisant l'API @@ -1479,6 +1478,7 @@ offerDetailsWindow.confirm.taker=Confirmer: Acceptez l''ordre de {0} monero offerDetailsWindow.creationDate=Date de création offerDetailsWindow.makersOnion=Adresse onion du maker offerDetailsWindow.challenge=Phrase secrète de l'offre +offerDetailsWindow.challenge.copy=Copier la phrase secrète à partager avec votre pair qRCodeWindow.headline=QR Code qRCodeWindow.msg=Veuillez utiliser le code QR pour recharger du portefeuille externe au portefeuille Haveno. diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 7847646806..784b65234f 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=Al momento, hai troppe transazioni non confermate. Per favore riprova più tardi. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=Filtro shared.enabled=Enabled @@ -927,8 +927,6 @@ support.tab.mediation.support=Mediazione support.tab.arbitration.support=Arbitrato support.tab.legacyArbitration.support=Arbitrato Legacy support.tab.ArbitratorsSupportTickets=I ticket di {0} -support.filter=Search disputes -support.filter.prompt=Inserisci ID commerciale, data, indirizzo onion o dati dell'account support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1034,6 +1032,7 @@ setting.preferences.displayOptions=Mostra opzioni setting.preferences.showOwnOffers=Mostra le mie offerte nel libro delle offerte setting.preferences.useAnimations=Usa animazioni setting.preferences.useDarkMode=Usa modalità notte +setting.preferences.useLightMode=Usa la modalità chiara setting.preferences.sortWithNumOffers=Ordina le liste di mercato con n. di offerte/scambi setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1476,6 +1475,7 @@ offerDetailsWindow.confirm.taker=Conferma: Accetta l'offerta a {0} monero offerDetailsWindow.creationDate=Data di creazione offerDetailsWindow.makersOnion=Indirizzo .onion del maker offerDetailsWindow.challenge=Passphrase dell'offerta +offerDetailsWindow.challenge.copy=Copia la frase segreta da condividere con il tuo interlocutore qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 9036bd8e2d..ccc24ab657 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -927,8 +927,6 @@ support.tab.mediation.support=調停 support.tab.arbitration.support=仲裁 support.tab.legacyArbitration.support=レガシー仲裁 support.tab.ArbitratorsSupportTickets={0} のチケット -support.filter=係争を検索 -support.filter.prompt=トレードID、日付、onionアドレスまたはアカウントデータを入力してください support.sigCheck.button=Check signature support.sigCheck.popup.info=仲裁プロセスの要約メッセージを貼り付けてください。このツールを使用すると、どんなユーザーでも仲裁者の署名が要約メッセージと一致するかどうかを確認できます。 @@ -1035,6 +1033,7 @@ setting.preferences.displayOptions=表示設定 setting.preferences.showOwnOffers=オファーブックに自分のオファーを表示 setting.preferences.useAnimations=アニメーションを使用 setting.preferences.useDarkMode=ダークモードを利用 +setting.preferences.useLightMode=ライトモードを使用する setting.preferences.sortWithNumOffers=市場リストをオファー/トレードの数で並び替える setting.preferences.onlyShowPaymentMethodsFromAccount=サポートされていない支払い方法を非表示にする setting.preferences.denyApiTaker=APIを使用するテイカーを拒否する @@ -1477,6 +1476,7 @@ offerDetailsWindow.confirm.taker=承認: ビットコインを{0}オファーを offerDetailsWindow.creationDate=作成日 offerDetailsWindow.makersOnion=メイカーのonionアドレス offerDetailsWindow.challenge=オファーパスフレーズ +offerDetailsWindow.challenge.copy=ピアと共有するためにパスフレーズをコピーする qRCodeWindow.headline=QRコード qRCodeWindow.msg=外部ウォレットからHavenoウォレットへ送金するのに、このQRコードを利用して下さい。 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index f6520e8436..c28a530456 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -221,7 +221,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=No momento, você possui muitas transações não-confirmadas. Tente novamente mais tarde. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=Filtro shared.enabled=Enabled @@ -929,8 +929,6 @@ support.tab.mediation.support=Mediação support.tab.arbitration.support=Arbitragem support.tab.legacyArbitration.support=Arbitração antiga support.tab.ArbitratorsSupportTickets=Tickets de {0} -support.filter=Search disputes -support.filter.prompt=Insira ID da negociação. data. endereço onion ou dados da conta support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1036,6 +1034,7 @@ setting.preferences.displayOptions=Opções de exibição setting.preferences.showOwnOffers=Exibir minhas ofertas no livro de ofertas setting.preferences.useAnimations=Usar animações setting.preferences.useDarkMode=Usar modo escuro +setting.preferences.useLightMode=Usar modo claro setting.preferences.sortWithNumOffers=Ordenar pelo nº de ofertas/negociações setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1480,6 +1479,7 @@ offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} monero offerDetailsWindow.creationDate=Criada em offerDetailsWindow.makersOnion=Endereço onion do ofertante offerDetailsWindow.challenge=Passphrase da oferta +offerDetailsWindow.challenge.copy=Copiar frase secreta para compartilhar com seu par qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index c69236f8b1..d14b41668c 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=Filtro shared.enabled=Enabled @@ -926,8 +926,6 @@ support.tab.mediation.support=Mediação support.tab.arbitration.support=Arbitragem support.tab.legacyArbitration.support=Arbitragem Antiga support.tab.ArbitratorsSupportTickets=Bilhetes de {0} -support.filter=Search disputes -support.filter.prompt=Insira o ID do negócio, data, endereço onion ou dados da conta support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1033,6 +1031,7 @@ setting.preferences.displayOptions=Mostrar opções setting.preferences.showOwnOffers=Mostrar as minhas próprias ofertas no livro de ofertas setting.preferences.useAnimations=Usar animações setting.preferences.useDarkMode=Usar o modo escuro +setting.preferences.useLightMode=Usar modo claro setting.preferences.sortWithNumOffers=Ordenar listas de mercado por nº de ofertas/negociações: setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1473,6 +1472,7 @@ offerDetailsWindow.confirm.taker=Confirmar: Aceitar oferta de {0} monero offerDetailsWindow.creationDate=Data de criação offerDetailsWindow.makersOnion=Endereço onion do ofertante offerDetailsWindow.challenge=Passphrase da oferta +offerDetailsWindow.challenge.copy=Copiar frase secreta para compartilhar com seu parceiro qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index f5ded7cdb2..848b765ae2 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=Фильтр shared.enabled=Enabled @@ -926,8 +926,6 @@ support.tab.mediation.support=Mediation support.tab.arbitration.support=Arbitration support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets -support.filter=Search disputes -support.filter.prompt=Введите идентификатор сделки, дату, onion-адрес или данные учётной записи support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1031,7 +1029,8 @@ setting.preferences.addCrypto=Добавить альткойн setting.preferences.displayOptions=Параметры отображения setting.preferences.showOwnOffers=Показать мои предложения в списке предложений setting.preferences.useAnimations=Использовать анимацию -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=Использовать тёмный режим +setting.preferences.useLightMode=Использовать светлый режим setting.preferences.sortWithNumOffers=Сортировать списки по кол-ву предложений/сделок setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1474,6 +1473,7 @@ offerDetailsWindow.confirm.taker=Подтвердите: принять пред offerDetailsWindow.creationDate=Дата создания offerDetailsWindow.makersOnion=Onion-адрес мейкера offerDetailsWindow.challenge=Пароль предложения +offerDetailsWindow.challenge.copy=Скопируйте кодовую фразу, чтобы поделиться с партнёром qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index d2d89f3b83..df37fc3f28 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=ตัวกรอง shared.enabled=Enabled @@ -926,8 +926,6 @@ support.tab.mediation.support=Mediation support.tab.arbitration.support=Arbitration support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets -support.filter=Search disputes -support.filter.prompt=Enter trade ID, date, onion address or account data support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1031,7 +1029,8 @@ setting.preferences.addCrypto=เพิ่ม crypto setting.preferences.displayOptions=แสดงตัวเลือกเพิ่มเติม setting.preferences.showOwnOffers=แสดงข้อเสนอของฉันเองในสมุดข้อเสนอ setting.preferences.useAnimations=ใช้ภาพเคลื่อนไหว -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=ใช้โหมดมืด +setting.preferences.useLightMode=ใช้โหมดสว่าง setting.preferences.sortWithNumOffers=จัดเรียงรายการโดยเลขของข้อเสนอ / การซื้อขาย setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1474,6 +1473,7 @@ offerDetailsWindow.confirm.taker=ยืนยัน: รับข้อเสน offerDetailsWindow.creationDate=วันที่สร้าง offerDetailsWindow.makersOnion=ที่อยู่ onion ของผู้สร้าง offerDetailsWindow.challenge=รหัสผ่านสำหรับข้อเสนอ +offerDetailsWindow.challenge.copy=คัดลอกวลีรหัสเพื่อแชร์กับเพื่อนของคุณ qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index 426c99500f..9b7f12e466 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -231,7 +231,7 @@ shared.delayedPayoutTxId=Gecikmiş ödeme işlem kimliği shared.delayedPayoutTxReceiverAddress=Gecikmiş ödeme işlemi gönderildi shared.unconfirmedTransactionsLimitReached=Şu anda çok fazla onaylanmamış işleminiz var. Lütfen daha sonra tekrar deneyin. shared.numItemsLabel=Girdi sayısı: {0} -shared.filter=Filtrele +shared.filter=Filtre shared.enabled=Etkin shared.pending=Beklemede shared.me=Ben @@ -1120,8 +1120,6 @@ support.tab.refund.support=Geri Ödeme support.tab.arbitration.support=Arbitraj support.tab.legacyArbitration.support=Eski Arbitraj support.tab.ArbitratorsSupportTickets={0}'nin biletleri -support.filter=Uyuşmazlıkları ara -support.filter.prompt=İşlem ID'si, tarih, onion adresi veya hesap verilerini girin support.tab.SignedOffers=İmzalı Teklifler support.prompt.signedOffer.penalty.msg=Bu, üreticiden bir ceza ücreti alacak ve kalan işlem fonlarını cüzdanına iade edecektir. Göndermek istediğinizden emin misiniz?\n\n\ Teklif ID'si: {0}\n\ @@ -1295,6 +1293,7 @@ setting.preferences.displayOptions=Görüntüleme seçenekleri setting.preferences.showOwnOffers=Teklif defterinde kendi tekliflerini göster setting.preferences.useAnimations=Animasyonları kullan setting.preferences.useDarkMode=Karanlık modu kullan +setting.preferences.useLightMode=Aydınlık modu kullan setting.preferences.sortWithNumOffers=Piyasaları teklif sayısına göre sırala setting.preferences.onlyShowPaymentMethodsFromAccount=Olmayan ödeme yöntemlerini gizle setting.preferences.denyApiTaker=API kullanan alıcıları reddet @@ -1970,6 +1969,7 @@ offerDetailsWindow.confirm.takerCrypto=Onayla: {0} {1} teklifi al offerDetailsWindow.creationDate=Oluşturma tarihi offerDetailsWindow.makersOnion=Yapıcı'nın onion adresi offerDetailsWindow.challenge=Teklif şifresi +offerDetailsWindow.challenge.copy=Parolanızı eşinizle paylaşmak için kopyalayın qRCodeWindow.headline=QR Kodu qRCodeWindow.msg=Harici cüzdanınızdan Haveno cüzdanınızı finanse etmek için bu QR kodunu kullanın. diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 4df8f44b16..52854a4208 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=Delayed payout transaction ID shared.delayedPayoutTxReceiverAddress=Delayed payout transaction sent to shared.unconfirmedTransactionsLimitReached=You have too many unconfirmed transactions at the moment. Please try again later. shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=Bộ lọc shared.enabled=Enabled @@ -928,8 +928,6 @@ support.tab.mediation.support=Mediation support.tab.arbitration.support=Arbitration support.tab.legacyArbitration.support=Legacy Arbitration support.tab.ArbitratorsSupportTickets={0}'s tickets -support.filter=Search disputes -support.filter.prompt=Nhập ID giao dịch, ngày tháng, địa chỉ onion hoặc dữ liệu tài khoản support.sigCheck.button=Check signature support.sigCheck.popup.header=Verify dispute result signature @@ -1033,7 +1031,8 @@ setting.preferences.addCrypto=Bổ sung crypto setting.preferences.displayOptions=Hiển thị các phương án setting.preferences.showOwnOffers=Hiển thị Báo giá của tôi trong danh mục Báo giá setting.preferences.useAnimations=Sử dụng hoạt ảnh -setting.preferences.useDarkMode=Use dark mode +setting.preferences.useDarkMode=Sử dụng chế độ tối +setting.preferences.useLightMode=Sử dụng chế độ sáng setting.preferences.sortWithNumOffers=Sắp xếp danh sách thị trường với số chào giá/giao dịch setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1476,6 +1475,7 @@ offerDetailsWindow.confirm.taker=Xác nhận: Nhận chào giáo cho {0} monero offerDetailsWindow.creationDate=Ngày tạo offerDetailsWindow.makersOnion=Địa chỉ onion của người tạo offerDetailsWindow.challenge=Mã bảo vệ giao dịch +offerDetailsWindow.challenge.copy=Sao chép cụm mật khẩu để chia sẻ với đối tác của bạn qRCodeWindow.headline=QR Code qRCodeWindow.msg=Please use this QR code for funding your Haveno wallet from your external wallet. diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 1e65527036..7e6b9be4d5 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -927,8 +927,6 @@ support.tab.mediation.support=调解 support.tab.arbitration.support=仲裁 support.tab.legacyArbitration.support=历史仲裁 support.tab.ArbitratorsSupportTickets={0} 的工单 -support.filter=查找纠纷 -support.filter.prompt=输入 交易 ID、日期、洋葱地址或账户信息 support.sigCheck.button=Check signature support.sigCheck.popup.info=请粘贴仲裁过程的摘要信息。使用这个工具,任何用户都可以检查仲裁者的签名是否与摘要信息相符。 @@ -1035,6 +1033,7 @@ setting.preferences.displayOptions=显示选项 setting.preferences.showOwnOffers=在报价列表中显示我的报价 setting.preferences.useAnimations=使用动画 setting.preferences.useDarkMode=使用夜间模式 +setting.preferences.useLightMode=使用浅色模式 setting.preferences.sortWithNumOffers=使用“报价ID/交易ID”筛选列表 setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1478,6 +1477,7 @@ offerDetailsWindow.confirm.taker=确定:下单买入 {0} 比特币 offerDetailsWindow.creationDate=创建时间 offerDetailsWindow.makersOnion=卖家的匿名地址 offerDetailsWindow.challenge=提供密码 +offerDetailsWindow.challenge.copy=复制助记词以与您的交易对手共享 qRCodeWindow.headline=二维码 qRCodeWindow.msg=请使用二维码从外部钱包充值至 Haveno 钱包 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 05f8bb5e8d..a1694e3999 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -218,7 +218,7 @@ shared.delayedPayoutTxId=延遲支付交易 ID shared.delayedPayoutTxReceiverAddress=延遲交易交易已發送至 shared.unconfirmedTransactionsLimitReached=你現在有過多的未確認交易。請稍後嘗試 shared.numItemsLabel=Number of entries: {0} -shared.filter=Filter +shared.filter=篩選 shared.enabled=啟用 @@ -927,8 +927,6 @@ support.tab.mediation.support=調解 support.tab.arbitration.support=仲裁 support.tab.legacyArbitration.support=歷史仲裁 support.tab.ArbitratorsSupportTickets={0} 的工單 -support.filter=查找糾紛 -support.filter.prompt=輸入 交易 ID、日期、洋葱地址或賬户信息 support.sigCheck.button=Check signature support.sigCheck.popup.info=請貼上仲裁程序的摘要訊息。利用這個工具,任何使用者都可以檢查仲裁者的簽名是否與摘要訊息相符。 @@ -1035,6 +1033,7 @@ setting.preferences.displayOptions=顯示選項 setting.preferences.showOwnOffers=在報價列表中顯示我的報價 setting.preferences.useAnimations=使用動畫 setting.preferences.useDarkMode=使用夜間模式 +setting.preferences.useLightMode=使用淺色模式 setting.preferences.sortWithNumOffers=使用“報價ID/交易ID”篩選列表 setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods setting.preferences.denyApiTaker=Deny takers using the API @@ -1478,6 +1477,7 @@ offerDetailsWindow.confirm.taker=確定:下單買入 {0} 比特幣 offerDetailsWindow.creationDate=創建時間 offerDetailsWindow.makersOnion=賣家的匿名地址 offerDetailsWindow.challenge=提供密碼 +offerDetailsWindow.challenge.copy=複製密語以與對方分享 qRCodeWindow.headline=二維碼 qRCodeWindow.msg=請使用二維碼從外部錢包充值至 Haveno 錢包 diff --git a/desktop/src/main/java/haveno/desktop/CandleStickChart.css b/desktop/src/main/java/haveno/desktop/CandleStickChart.css index 376e0db666..4e5a05d7ea 100644 --- a/desktop/src/main/java/haveno/desktop/CandleStickChart.css +++ b/desktop/src/main/java/haveno/desktop/CandleStickChart.css @@ -62,6 +62,8 @@ -demo-bar-fill: -bs-sell; -fx-background-color: -demo-bar-fill; -fx-background-insets: 0; + -fx-background-radius: 2px; + -fx-border-radius: 2px; } .candlestick-bar.close-above-open { @@ -80,6 +82,8 @@ -fx-padding: 5; -fx-background-color: -bs-volume-transparent; -fx-background-insets: 0; + -fx-background-radius: 2px; + -fx-border-radius: 2px; } .chart-alternative-row-fill { diff --git a/desktop/src/main/java/haveno/desktop/app/HavenoApp.java b/desktop/src/main/java/haveno/desktop/app/HavenoApp.java index 16370c2cdb..41b5ad09bc 100644 --- a/desktop/src/main/java/haveno/desktop/app/HavenoApp.java +++ b/desktop/src/main/java/haveno/desktop/app/HavenoApp.java @@ -74,6 +74,7 @@ import javafx.scene.Scene; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; import javafx.stage.Modality; import javafx.stage.Screen; import javafx.stage.Stage; @@ -223,6 +224,9 @@ public class HavenoApp extends Application implements UncaughtExceptionHandler { CssTheme.loadSceneStyles(scene, preferences.getCssTheme(), config.useDevModeHeader); }); CssTheme.loadSceneStyles(scene, preferences.getCssTheme(), config.useDevModeHeader); + + // set initial background color + scene.setFill(CssTheme.isDarkTheme() ? Color.BLACK : Color.WHITE); return scene; } diff --git a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java index ed7e956eba..73e78ab6d7 100644 --- a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java +++ b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java @@ -216,7 +216,10 @@ public class HavenoAppMain extends HavenoExecutable { // Set the dialog content VBox vbox = new VBox(10); - vbox.getChildren().addAll(new ImageView(ImageUtil.getImageByPath("logo_splash.png")), passwordField, errorMessageField, versionField); + ImageView logoImageView = new ImageView(ImageUtil.getImageByPath("logo_splash_light.png")); + logoImageView.setFitWidth(342); + logoImageView.setPreserveRatio(true); + vbox.getChildren().addAll(logoImageView, passwordField, errorMessageField, versionField); vbox.setAlignment(Pos.TOP_CENTER); getDialogPane().setContent(vbox); diff --git a/desktop/src/main/java/haveno/desktop/components/AddressTextField.java b/desktop/src/main/java/haveno/desktop/components/AddressTextField.java index ec6f284379..8edfbd222a 100644 --- a/desktop/src/main/java/haveno/desktop/components/AddressTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/AddressTextField.java @@ -20,14 +20,17 @@ package haveno.desktop.components; import com.jfoenix.controls.JFXTextField; import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; +import haveno.common.UserThread; import haveno.common.util.Utilities; import haveno.core.locale.Res; import haveno.desktop.main.overlays.popups.Popup; import haveno.desktop.util.GUIUtil; +import haveno.desktop.util.Layout; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; +import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.Tooltip; import javafx.scene.layout.AnchorPane; @@ -55,6 +58,7 @@ public class AddressTextField extends AnchorPane { textField.setId("address-text-field"); textField.setEditable(false); textField.setLabelFloat(true); + textField.getStyleClass().add("label-float"); textField.setPromptText(label); textField.textProperty().bind(address); @@ -70,28 +74,32 @@ public class AddressTextField extends AnchorPane { textField.focusTraversableProperty().set(focusTraversableProperty().get()); Label extWalletIcon = new Label(); - extWalletIcon.setLayoutY(3); + extWalletIcon.setLayoutY(Layout.FLOATING_ICON_Y); extWalletIcon.getStyleClass().addAll("icon", "highlight"); extWalletIcon.setTooltip(new Tooltip(tooltipText)); AwesomeDude.setIcon(extWalletIcon, AwesomeIcon.SIGNIN); extWalletIcon.setOnMouseClicked(e -> openWallet()); - Label copyIcon = new Label(); - copyIcon.setLayoutY(3); - copyIcon.getStyleClass().addAll("icon", "highlight"); - Tooltip.install(copyIcon, new Tooltip(Res.get("addressTextField.copyToClipboard"))); - AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); - copyIcon.setOnMouseClicked(e -> { + Label copyLabel = new Label(); + copyLabel.setLayoutY(Layout.FLOATING_ICON_Y); + copyLabel.getStyleClass().addAll("icon", "highlight"); + Tooltip.install(copyLabel, new Tooltip(Res.get("addressTextField.copyToClipboard"))); + copyLabel.setGraphic(GUIUtil.getCopyIcon()); + copyLabel.setOnMouseClicked(e -> { if (address.get() != null && address.get().length() > 0) Utilities.copyToClipboard(address.get()); + Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); + Node node = (Node) e.getSource(); + UserThread.runAfter(() -> tp.hide(), 1); + tp.show(node, e.getScreenX() + Layout.PADDING, e.getScreenY() + Layout.PADDING); }); - AnchorPane.setRightAnchor(copyIcon, 30.0); + AnchorPane.setRightAnchor(copyLabel, 30.0); AnchorPane.setRightAnchor(extWalletIcon, 5.0); AnchorPane.setRightAnchor(textField, 55.0); AnchorPane.setLeftAnchor(textField, 0.0); - getChildren().addAll(textField, copyIcon, extWalletIcon); + getChildren().addAll(textField, copyLabel, extWalletIcon); } private void openWallet() { diff --git a/desktop/src/main/java/haveno/desktop/components/AutoTooltipButton.java b/desktop/src/main/java/haveno/desktop/components/AutoTooltipButton.java index 83261ba212..11521e93a1 100644 --- a/desktop/src/main/java/haveno/desktop/components/AutoTooltipButton.java +++ b/desktop/src/main/java/haveno/desktop/components/AutoTooltipButton.java @@ -31,15 +31,15 @@ public class AutoTooltipButton extends JFXButton { } public AutoTooltipButton(String text) { - super(text.toUpperCase()); + super(text); } public AutoTooltipButton(String text, Node graphic) { - super(text.toUpperCase(), graphic); + super(text, graphic); } public void updateText(String text) { - setText(text.toUpperCase()); + setText(text); } @Override diff --git a/desktop/src/main/java/haveno/desktop/components/AutocompleteComboBox.java b/desktop/src/main/java/haveno/desktop/components/AutocompleteComboBox.java index 2b1adc5769..e6701cdb4c 100644 --- a/desktop/src/main/java/haveno/desktop/components/AutocompleteComboBox.java +++ b/desktop/src/main/java/haveno/desktop/components/AutocompleteComboBox.java @@ -24,6 +24,7 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.Event; import javafx.event.EventHandler; +import javafx.scene.control.ListView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import org.apache.commons.lang3.StringUtils; @@ -44,6 +45,7 @@ public class AutocompleteComboBox<T> extends JFXComboBox<T> { private List<? extends T> extendedList; private List<T> matchingList; private JFXComboBoxListViewSkin<T> comboBoxListViewSkin; + private boolean selectAllShortcut = false; public AutocompleteComboBox() { this(FXCollections.observableArrayList()); @@ -153,6 +155,27 @@ public class AutocompleteComboBox<T> extends JFXComboBox<T> { private void reactToQueryChanges() { getEditor().addEventHandler(KeyEvent.KEY_RELEASED, (KeyEvent event) -> { + + // ignore ctrl and command keys + if (event.getCode() == KeyCode.CONTROL || event.getCode() == KeyCode.COMMAND || event.getCode() == KeyCode.META) { + event.consume(); + return; + } + + // handle select all + boolean isSelectAll = event.getCode() == KeyCode.A && (event.isControlDown() || event.isMetaDown()); + if (isSelectAll) { + getEditor().selectAll(); + selectAllShortcut = true; + event.consume(); + return; + } + if (event.getCode() == KeyCode.A && selectAllShortcut) { // 'A' can be received after ctrl/cmd + selectAllShortcut = false; + event.consume(); + return; + } + UserThread.execute(() -> { String query = getEditor().getText(); var exactMatch = list.stream().anyMatch(item -> asString(item).equalsIgnoreCase(query)); @@ -180,6 +203,10 @@ public class AutocompleteComboBox<T> extends JFXComboBox<T> { if (matchingListSize() > 0) { comboBoxListViewSkin.getPopupContent().autosize(); show(); + if (comboBoxListViewSkin.getPopupContent() instanceof ListView<?> listView) { + listView.applyCss(); + listView.layout(); + } } else { hide(); } diff --git a/desktop/src/main/java/haveno/desktop/components/BalanceTextField.java b/desktop/src/main/java/haveno/desktop/components/BalanceTextField.java index 4e00789dc8..7775b0b812 100644 --- a/desktop/src/main/java/haveno/desktop/components/BalanceTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/BalanceTextField.java @@ -47,6 +47,7 @@ public class BalanceTextField extends AnchorPane { public BalanceTextField(String label) { textField = new HavenoTextField(); textField.setLabelFloat(true); + textField.getStyleClass().add("label-float"); textField.setPromptText(label); textField.setFocusTraversable(false); textField.setEditable(false); diff --git a/desktop/src/main/java/haveno/desktop/components/ExplorerAddressTextField.java b/desktop/src/main/java/haveno/desktop/components/ExplorerAddressTextField.java index 7dff009b9e..7e31272ca8 100644 --- a/desktop/src/main/java/haveno/desktop/components/ExplorerAddressTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/ExplorerAddressTextField.java @@ -23,6 +23,7 @@ import de.jensd.fx.fontawesome.AwesomeIcon; import haveno.common.util.Utilities; import haveno.core.locale.Res; import haveno.core.user.Preferences; +import haveno.desktop.util.GUIUtil; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; @@ -38,19 +39,19 @@ public class ExplorerAddressTextField extends AnchorPane { @Getter private final TextField textField; - private final Label copyIcon, missingAddressWarningIcon; + private final Label copyLabel, missingAddressWarningIcon; /////////////////////////////////////////////////////////////////////////////////////////// // Constructor /////////////////////////////////////////////////////////////////////////////////////////// public ExplorerAddressTextField() { - copyIcon = new Label(); - copyIcon.setLayoutY(3); - copyIcon.getStyleClass().addAll("icon", "highlight"); - copyIcon.setTooltip(new Tooltip(Res.get("explorerAddressTextField.copyToClipboard"))); - AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); - AnchorPane.setRightAnchor(copyIcon, 30.0); + copyLabel = new Label(); + copyLabel.setLayoutY(3); + copyLabel.getStyleClass().addAll("icon", "highlight"); + copyLabel.setTooltip(new Tooltip(Res.get("explorerAddressTextField.copyToClipboard"))); + copyLabel.setGraphic(GUIUtil.getCopyIcon()); + AnchorPane.setRightAnchor(copyLabel, 30.0); Tooltip tooltip = new Tooltip(Res.get("explorerAddressTextField.blockExplorerIcon.tooltip")); @@ -71,27 +72,27 @@ public class ExplorerAddressTextField extends AnchorPane { AnchorPane.setRightAnchor(textField, 80.0); AnchorPane.setLeftAnchor(textField, 0.0); textField.focusTraversableProperty().set(focusTraversableProperty().get()); - getChildren().addAll(textField, missingAddressWarningIcon, copyIcon); + getChildren().addAll(textField, missingAddressWarningIcon, copyLabel); } public void setup(@Nullable String address) { if (address == null) { textField.setText(Res.get("shared.na")); textField.setId("address-text-field-error"); - copyIcon.setVisible(false); - copyIcon.setManaged(false); + copyLabel.setVisible(false); + copyLabel.setManaged(false); missingAddressWarningIcon.setVisible(true); missingAddressWarningIcon.setManaged(true); return; } textField.setText(address); - copyIcon.setOnMouseClicked(e -> Utilities.copyToClipboard(address)); + copyLabel.setOnMouseClicked(e -> Utilities.copyToClipboard(address)); } public void cleanup() { textField.setOnMouseClicked(null); - copyIcon.setOnMouseClicked(null); + copyLabel.setOnMouseClicked(null); textField.setText(""); } } diff --git a/desktop/src/main/java/haveno/desktop/components/FundsTextField.java b/desktop/src/main/java/haveno/desktop/components/FundsTextField.java index 0804750972..aa01093469 100644 --- a/desktop/src/main/java/haveno/desktop/components/FundsTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/FundsTextField.java @@ -17,9 +17,10 @@ package haveno.desktop.components; -import de.jensd.fx.fontawesome.AwesomeIcon; import haveno.common.util.Utilities; import haveno.core.locale.Res; +import haveno.desktop.util.GUIUtil; +import haveno.desktop.util.Layout; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; @@ -29,8 +30,6 @@ import javafx.scene.layout.AnchorPane; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static haveno.desktop.util.FormBuilder.getIcon; - public class FundsTextField extends InfoTextField { public static final Logger log = LoggerFactory.getLogger(FundsTextField.class); @@ -46,11 +45,12 @@ public class FundsTextField extends InfoTextField { textField.textProperty().unbind(); textField.textProperty().bind(Bindings.concat(textProperty())); // TODO: removed `, " ", fundsStructure` for haveno to fix "Funds needed: .123 XMR (null)" bug - Label copyIcon = getIcon(AwesomeIcon.COPY); - copyIcon.setLayoutY(5); - copyIcon.getStyleClass().addAll("icon", "highlight"); - Tooltip.install(copyIcon, new Tooltip(Res.get("shared.copyToClipboard"))); - copyIcon.setOnMouseClicked(e -> { + Label copyLabel = new Label(); + copyLabel.setLayoutY(Layout.FLOATING_ICON_Y); + copyLabel.getStyleClass().addAll("icon", "highlight"); + Tooltip.install(copyLabel, new Tooltip(Res.get("shared.copyToClipboard"))); + copyLabel.setGraphic(GUIUtil.getCopyIcon()); + copyLabel.setOnMouseClicked(e -> { String text = getText(); if (text != null && text.length() > 0) { String copyText; @@ -64,11 +64,11 @@ public class FundsTextField extends InfoTextField { } }); - AnchorPane.setRightAnchor(copyIcon, 30.0); + AnchorPane.setRightAnchor(copyLabel, 30.0); AnchorPane.setRightAnchor(infoIcon, 62.0); AnchorPane.setRightAnchor(textField, 55.0); - getChildren().add(copyIcon); + getChildren().add(copyLabel); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/desktop/src/main/java/haveno/desktop/components/HavenoTextField.java b/desktop/src/main/java/haveno/desktop/components/HavenoTextField.java index 96c9e2571d..bb8c24eecf 100644 --- a/desktop/src/main/java/haveno/desktop/components/HavenoTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/HavenoTextField.java @@ -1,16 +1,18 @@ package haveno.desktop.components; import com.jfoenix.controls.JFXTextField; +import haveno.desktop.util.GUIUtil; import javafx.scene.control.Skin; public class HavenoTextField extends JFXTextField { public HavenoTextField(String value) { super(value); + GUIUtil.applyFilledStyle(this); } public HavenoTextField() { - super(); + this(null); } @Override diff --git a/desktop/src/main/java/haveno/desktop/components/InfoTextField.java b/desktop/src/main/java/haveno/desktop/components/InfoTextField.java index beafcf9494..7e47b9339f 100644 --- a/desktop/src/main/java/haveno/desktop/components/InfoTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/InfoTextField.java @@ -21,6 +21,7 @@ import com.jfoenix.controls.JFXTextField; import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; import haveno.desktop.components.controlsfx.control.PopOver; +import haveno.desktop.util.Layout; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.scene.Node; @@ -51,13 +52,14 @@ public class InfoTextField extends AnchorPane { arrowLocation = PopOver.ArrowLocation.RIGHT_TOP; textField = new HavenoTextField(); textField.setLabelFloat(true); + textField.getStyleClass().add("label-float"); textField.setEditable(false); textField.textProperty().bind(text); textField.setFocusTraversable(false); textField.setId("info-field"); infoIcon = getIcon(AwesomeIcon.INFO_SIGN); - infoIcon.setLayoutY(5); + infoIcon.setLayoutY(Layout.FLOATING_ICON_Y - 2); infoIcon.getStyleClass().addAll("icon", "info"); AnchorPane.setRightAnchor(infoIcon, 7.0); diff --git a/desktop/src/main/java/haveno/desktop/components/InputTextField.java b/desktop/src/main/java/haveno/desktop/components/InputTextField.java index 8c4ace02b8..b46e7f84b1 100644 --- a/desktop/src/main/java/haveno/desktop/components/InputTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/InputTextField.java @@ -20,6 +20,7 @@ package haveno.desktop.components; import com.jfoenix.controls.JFXTextField; import haveno.core.util.validation.InputValidator; +import haveno.desktop.util.GUIUtil; import haveno.desktop.util.validation.JFXInputValidator; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -67,6 +68,7 @@ public class InputTextField extends JFXTextField { public InputTextField() { super(); + GUIUtil.applyFilledStyle(this); getValidators().add(jfxValidationWrapper); diff --git a/desktop/src/main/java/haveno/desktop/components/PeerInfoIcon.java b/desktop/src/main/java/haveno/desktop/components/PeerInfoIcon.java index da8dcb4a70..af854c437f 100644 --- a/desktop/src/main/java/haveno/desktop/components/PeerInfoIcon.java +++ b/desktop/src/main/java/haveno/desktop/components/PeerInfoIcon.java @@ -124,7 +124,8 @@ public class PeerInfoIcon extends Group { numTradesPane.relocate(scaleFactor * 18, scaleFactor * 14); numTradesPane.setMouseTransparent(true); ImageView numTradesCircle = new ImageView(); - numTradesCircle.setId("image-green_circle"); + numTradesCircle.setId("image-green_circle_solid"); + numTradesLabel = new AutoTooltipLabel(); numTradesLabel.relocate(scaleFactor * 5, scaleFactor * 1); numTradesLabel.setId("ident-num-label"); @@ -134,7 +135,7 @@ public class PeerInfoIcon extends Group { tagPane.relocate(Math.round(scaleFactor * 18), scaleFactor * -2); tagPane.setMouseTransparent(true); ImageView tagCircle = new ImageView(); - tagCircle.setId("image-blue_circle"); + tagCircle.setId("image-blue_circle_solid"); tagLabel = new AutoTooltipLabel(); tagLabel.relocate(Math.round(scaleFactor * 5), scaleFactor * 1); tagLabel.setId("ident-num-label"); diff --git a/desktop/src/main/java/haveno/desktop/components/TextFieldWithCopyIcon.java b/desktop/src/main/java/haveno/desktop/components/TextFieldWithCopyIcon.java index d337916da3..1dbd47eaaa 100644 --- a/desktop/src/main/java/haveno/desktop/components/TextFieldWithCopyIcon.java +++ b/desktop/src/main/java/haveno/desktop/components/TextFieldWithCopyIcon.java @@ -18,12 +18,15 @@ package haveno.desktop.components; import com.jfoenix.controls.JFXTextField; -import de.jensd.fx.fontawesome.AwesomeDude; -import de.jensd.fx.fontawesome.AwesomeIcon; + +import haveno.common.UserThread; import haveno.common.util.Utilities; import haveno.core.locale.Res; +import haveno.desktop.util.GUIUtil; +import haveno.desktop.util.Layout; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; +import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; @@ -45,12 +48,13 @@ public class TextFieldWithCopyIcon extends AnchorPane { } public TextFieldWithCopyIcon(String customStyleClass) { - Label copyIcon = new Label(); - copyIcon.setLayoutY(3); - copyIcon.getStyleClass().addAll("icon", "highlight"); - copyIcon.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); - AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); - copyIcon.setOnMouseClicked(e -> { + Label copyLabel = new Label(); + copyLabel.setLayoutY(Layout.FLOATING_ICON_Y); + copyLabel.getStyleClass().addAll("icon", "highlight"); + if (customStyleClass != null) copyLabel.getStyleClass().add(customStyleClass + "-icon"); + copyLabel.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); + copyLabel.setGraphic(GUIUtil.getCopyIcon()); + copyLabel.setOnMouseClicked(e -> { String text = getText(); if (text != null && text.length() > 0) { String copyText; @@ -70,17 +74,25 @@ public class TextFieldWithCopyIcon extends AnchorPane { copyText = text; } Utilities.copyToClipboard(copyText); + Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); + Node node = (Node) e.getSource(); + UserThread.runAfter(() -> tp.hide(), 1); + tp.show(node, e.getScreenX() + Layout.PADDING, e.getScreenY() + Layout.PADDING); } }); textField = new JFXTextField(); textField.setEditable(false); if (customStyleClass != null) textField.getStyleClass().add(customStyleClass); textField.textProperty().bindBidirectional(text); - AnchorPane.setRightAnchor(copyIcon, 5.0); + AnchorPane.setRightAnchor(copyLabel, 5.0); AnchorPane.setRightAnchor(textField, 30.0); AnchorPane.setLeftAnchor(textField, 0.0); + AnchorPane.setTopAnchor(copyLabel, 0.0); + AnchorPane.setBottomAnchor(copyLabel, 0.0); + AnchorPane.setTopAnchor(textField, 0.0); + AnchorPane.setBottomAnchor(textField, 0.0); textField.focusTraversableProperty().set(focusTraversableProperty().get()); - getChildren().addAll(textField, copyIcon); + getChildren().addAll(textField, copyLabel); } public void setPromptText(String value) { diff --git a/desktop/src/main/java/haveno/desktop/components/TextFieldWithIcon.java b/desktop/src/main/java/haveno/desktop/components/TextFieldWithIcon.java index 2e66a0e026..1db14cf73a 100644 --- a/desktop/src/main/java/haveno/desktop/components/TextFieldWithIcon.java +++ b/desktop/src/main/java/haveno/desktop/components/TextFieldWithIcon.java @@ -21,6 +21,7 @@ import com.jfoenix.controls.JFXTextField; import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; import haveno.common.UserThread; +import haveno.desktop.util.Layout; import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.control.TextField; @@ -53,10 +54,10 @@ public class TextFieldWithIcon extends AnchorPane { iconLabel = new Label(); iconLabel.setLayoutX(0); - iconLabel.setLayoutY(3); + iconLabel.setLayoutY(Layout.FLOATING_ICON_Y); dummyTextField.widthProperty().addListener((observable, oldValue, newValue) -> { - iconLabel.setLayoutX(dummyTextField.widthProperty().get() + 20); + iconLabel.setLayoutX(dummyTextField.widthProperty().get() + 20 + Layout.FLOATING_ICON_Y); }); getChildren().addAll(textField, dummyTextField, iconLabel); diff --git a/desktop/src/main/java/haveno/desktop/components/TxIdTextField.java b/desktop/src/main/java/haveno/desktop/components/TxIdTextField.java index e9ced56cdf..2a55dba7b0 100644 --- a/desktop/src/main/java/haveno/desktop/components/TxIdTextField.java +++ b/desktop/src/main/java/haveno/desktop/components/TxIdTextField.java @@ -29,7 +29,10 @@ import haveno.core.user.Preferences; import haveno.core.xmr.wallet.XmrWalletService; import haveno.desktop.components.indicator.TxConfidenceIndicator; import haveno.desktop.util.GUIUtil; +import haveno.desktop.util.Layout; import javafx.beans.value.ChangeListener; +import javafx.scene.Cursor; +import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; @@ -51,7 +54,7 @@ public class TxIdTextField extends AnchorPane { private final TextField textField; private final Tooltip progressIndicatorTooltip; private final TxConfidenceIndicator txConfidenceIndicator; - private final Label copyIcon, blockExplorerIcon, missingTxWarningIcon; + private final Label copyLabel, blockExplorerIcon, missingTxWarningIcon; private MoneroWalletListener walletListener; private ChangeListener<Number> tradeListener; @@ -70,16 +73,17 @@ public class TxIdTextField extends AnchorPane { txConfidenceIndicator.setProgress(0); txConfidenceIndicator.setVisible(false); AnchorPane.setRightAnchor(txConfidenceIndicator, 0.0); - AnchorPane.setTopAnchor(txConfidenceIndicator, 3.0); + AnchorPane.setTopAnchor(txConfidenceIndicator, Layout.FLOATING_ICON_Y); progressIndicatorTooltip = new Tooltip("-"); txConfidenceIndicator.setTooltip(progressIndicatorTooltip); - copyIcon = new Label(); - copyIcon.setLayoutY(3); - copyIcon.getStyleClass().addAll("icon", "highlight"); - copyIcon.setTooltip(new Tooltip(Res.get("txIdTextField.copyIcon.tooltip"))); - AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY); - AnchorPane.setRightAnchor(copyIcon, 30.0); + copyLabel = new Label(); + copyLabel.setLayoutY(Layout.FLOATING_ICON_Y); + copyLabel.getStyleClass().addAll("icon", "highlight"); + copyLabel.setTooltip(new Tooltip(Res.get("txIdTextField.copyIcon.tooltip"))); + copyLabel.setGraphic(GUIUtil.getCopyIcon()); + copyLabel.setCursor(Cursor.HAND); + AnchorPane.setRightAnchor(copyLabel, 30.0); Tooltip tooltip = new Tooltip(Res.get("txIdTextField.blockExplorerIcon.tooltip")); @@ -89,7 +93,7 @@ public class TxIdTextField extends AnchorPane { AwesomeDude.setIcon(blockExplorerIcon, AwesomeIcon.EXTERNAL_LINK); blockExplorerIcon.setMinWidth(20); AnchorPane.setRightAnchor(blockExplorerIcon, 52.0); - AnchorPane.setTopAnchor(blockExplorerIcon, 4.0); + AnchorPane.setTopAnchor(blockExplorerIcon, Layout.FLOATING_ICON_Y); missingTxWarningIcon = new Label(); missingTxWarningIcon.getStyleClass().addAll("icon", "error-icon"); @@ -97,7 +101,7 @@ public class TxIdTextField extends AnchorPane { missingTxWarningIcon.setTooltip(new Tooltip(Res.get("txIdTextField.missingTx.warning.tooltip"))); missingTxWarningIcon.setMinWidth(20); AnchorPane.setRightAnchor(missingTxWarningIcon, 52.0); - AnchorPane.setTopAnchor(missingTxWarningIcon, 4.0); + AnchorPane.setTopAnchor(missingTxWarningIcon, Layout.FLOATING_ICON_Y); missingTxWarningIcon.setVisible(false); missingTxWarningIcon.setManaged(false); @@ -108,7 +112,7 @@ public class TxIdTextField extends AnchorPane { AnchorPane.setRightAnchor(textField, 80.0); AnchorPane.setLeftAnchor(textField, 0.0); textField.focusTraversableProperty().set(focusTraversableProperty().get()); - getChildren().addAll(textField, missingTxWarningIcon, blockExplorerIcon, copyIcon, txConfidenceIndicator); + getChildren().addAll(textField, missingTxWarningIcon, blockExplorerIcon, copyLabel, txConfidenceIndicator); } public void setup(@Nullable String txId) { @@ -131,8 +135,8 @@ public class TxIdTextField extends AnchorPane { textField.setId("address-text-field-error"); blockExplorerIcon.setVisible(false); blockExplorerIcon.setManaged(false); - copyIcon.setVisible(false); - copyIcon.setManaged(false); + copyLabel.setVisible(false); + copyLabel.setManaged(false); txConfidenceIndicator.setVisible(false); missingTxWarningIcon.setVisible(true); missingTxWarningIcon.setManaged(true); @@ -158,7 +162,13 @@ public class TxIdTextField extends AnchorPane { textField.setText(txId); textField.setOnMouseClicked(mouseEvent -> openBlockExplorer(txId)); blockExplorerIcon.setOnMouseClicked(mouseEvent -> openBlockExplorer(txId)); - copyIcon.setOnMouseClicked(e -> Utilities.copyToClipboard(txId)); + copyLabel.setOnMouseClicked(e -> { + Utilities.copyToClipboard(txId); + Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); + Node node = (Node) e.getSource(); + UserThread.runAfter(() -> tp.hide(), 1); + tp.show(node, e.getScreenX() + Layout.PADDING, e.getScreenY() + Layout.PADDING); + }); txConfidenceIndicator.setVisible(true); // update off main thread @@ -177,7 +187,7 @@ public class TxIdTextField extends AnchorPane { trade = null; textField.setOnMouseClicked(null); blockExplorerIcon.setOnMouseClicked(null); - copyIcon.setOnMouseClicked(null); + copyLabel.setOnMouseClicked(null); textField.setText(""); } diff --git a/desktop/src/main/java/haveno/desktop/components/controlsfx/control/PopOver.java b/desktop/src/main/java/haveno/desktop/components/controlsfx/control/PopOver.java index f04dee5960..85b7eaceba 100644 --- a/desktop/src/main/java/haveno/desktop/components/controlsfx/control/PopOver.java +++ b/desktop/src/main/java/haveno/desktop/components/controlsfx/control/PopOver.java @@ -494,7 +494,7 @@ public class PopOver extends PopupControl { * @since 1.0 */ public final void hide(Duration fadeOutDuration) { - log.info("hide:" + fadeOutDuration.toString()); + log.debug("hide:" + fadeOutDuration.toString()); //We must remove EventFilter in order to prevent memory leak. if (ownerWindow != null) { ownerWindow.removeEventFilter(WindowEvent.WINDOW_CLOSE_REQUEST, diff --git a/desktop/src/main/java/haveno/desktop/components/list/FilterBox.java b/desktop/src/main/java/haveno/desktop/components/list/FilterBox.java index 9bed491f49..adb4707564 100644 --- a/desktop/src/main/java/haveno/desktop/components/list/FilterBox.java +++ b/desktop/src/main/java/haveno/desktop/components/list/FilterBox.java @@ -17,13 +17,10 @@ package haveno.desktop.components.list; -import haveno.core.locale.Res; -import haveno.desktop.components.AutoTooltipLabel; import haveno.desktop.components.InputTextField; import haveno.desktop.util.filtering.FilterableListItem; import javafx.beans.value.ChangeListener; import javafx.collections.transformation.FilteredList; -import javafx.geometry.Insets; import javafx.scene.control.TableView; import javafx.scene.layout.HBox; @@ -37,13 +34,10 @@ public class FilterBox extends HBox { super(); setSpacing(5.0); - AutoTooltipLabel label = new AutoTooltipLabel(Res.get("shared.filter")); - HBox.setMargin(label, new Insets(5.0, 0, 0, 10.0)); - textField = new InputTextField(); textField.setMinWidth(500); - getChildren().addAll(label, textField); + getChildren().addAll(textField); } public void initialize(FilteredList<? extends FilterableListItem> filteredList, @@ -67,4 +61,8 @@ public class FilterBox extends HBox { private void applyFilteredListPredicate(String filterString) { filteredList.setPredicate(item -> item.match(filterString)); } + + public void setPromptText(String promptText) { + textField.setPromptText(promptText); + } } diff --git a/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java b/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java index f57c3f154f..7baa76c564 100644 --- a/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java +++ b/desktop/src/main/java/haveno/desktop/components/paymentmethods/AssetsForm.java @@ -36,6 +36,7 @@ import haveno.desktop.components.AutocompleteComboBox; import haveno.desktop.components.InputTextField; import haveno.desktop.main.overlays.popups.Popup; import haveno.desktop.util.FormBuilder; +import haveno.desktop.util.GUIUtil; import haveno.desktop.util.Layout; import javafx.geometry.Insets; import javafx.scene.control.CheckBox; @@ -202,6 +203,8 @@ public class AssetsForm extends PaymentMethodForm { CurrencyUtil.getActiveSortedCryptoCurrencies(filterManager)); currencyComboBox.setVisibleRowCount(Math.min(currencyComboBox.getItems().size(), 10)); + currencyComboBox.setCellFactory(GUIUtil.getTradeCurrencyCellFactoryNameAndCode()); + currencyComboBox.setConverter(new StringConverter<>() { @Override public String toString(TradeCurrency tradeCurrency) { diff --git a/desktop/src/main/java/haveno/desktop/haveno.css b/desktop/src/main/java/haveno/desktop/haveno.css index e3cfac8c0e..7c24526aaf 100644 --- a/desktop/src/main/java/haveno/desktop/haveno.css +++ b/desktop/src/main/java/haveno/desktop/haveno.css @@ -39,7 +39,7 @@ -fx-text-fill: -bs-color-primary; } -.highlight, .highlight-static { +.highlight, .highlight-static, .highlight.label .glyph-icon { -fx-text-fill: -fx-accent; -fx-fill: -fx-accent; } @@ -105,6 +105,11 @@ -fx-font-size: 1.077em; -fx-font-family: "IBM Plex Mono"; -fx-padding: 0 !important; + -fx-border-width: 0; + -fx-text-fill: -bs-rd-font-dark-gray !important; +} + +.confirmation-text-field-as-label-icon { } /* Other UI Elements */ @@ -150,9 +155,9 @@ -fx-text-fill: -bs-rd-font-dark-gray; -fx-font-size: 0.923em; -fx-font-weight: normal; - -fx-background-radius: 2px; - -fx-pref-height: 32; - -fx-min-height: -fx-pref-height; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-min-height: 32; -fx-padding: 0 40 0 40; -fx-effect: dropshadow(gaussian, -bs-text-color-transparent, 2, 0, 0, 0, 1); -fx-cursor: hand; @@ -171,8 +176,12 @@ -fx-text-fill: -bs-background-color; } -.compact-button, .table-cell .jfx-button, .action-button.compact-button { - -fx-padding: 0 10 0 10; +.action-button.compact-button, .compact-button { + -fx-padding: 0 15 0 15; +} + +.table-cell .jfx-button { + -fx-padding: 0 7 0 7; } .tiny-button, @@ -217,10 +226,59 @@ -fx-border-width: 1; } +.jfx-combo-box, .jfx-text-field, .jfx-text-area, .jfx-password-field, .toggle-button-no-slider { + -fx-padding: 7 14 7 14; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-border-color: transparent; +} + .jfx-combo-box { - -jfx-focus-color: -bs-color-primary; - -jfx-unfocus-color: -bs-color-gray-line; - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-form-field; +} + +.input-line, .input-focused-line { + -fx-background-color: transparent; + visibility: hidden; + -fx-max-height: 0; +} + +.jfx-text-field { + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-background-color: -bs-color-background-form-field; +} + +.jfx-text-field.label-float .prompt-container { + -fx-translate-y: 0px; +} + +.jfx-text-field.filled.label-float .prompt-container, +.jfx-text-field.label-float:focused .prompt-container, +.jfx-combo-box.filled.label-float .prompt-container, +.jfx-combo-box.label-float:focused .prompt-container, +.jfx-password-field.filled.label-float .prompt-container, +.jfx-password-field.label-float:focused .prompt-container { + -fx-translate-x: -14px; + -fx-translate-y: -5.5px; +} + +.jfx-combo-box .arrow-button { + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-padding: 0 0 0 10; +} + +.jfx-combo-box:hover { + -fx-cursor: hand; +} + +.jfx-combo-box:editable:hover { + -fx-cursor: null; +} + +.jfx-combo-box .arrow-button:hover { + -fx-cursor: hand; } .jfx-combo-box > .list-cell { @@ -228,15 +286,66 @@ -fx-font-family: "IBM Plex Sans Medium"; } +/* TODO: otherwise combo box with "odd" class is opacity 0.4? */ +.jfx-combo-box > .list-cell:odd, .jfx-combo-box > .list-cell:even { + -fx-opacity: 1.0; +} + +.jfx-combo-box > .list-cell, +.jfx-combo-box > .text-field { + -fx-padding: 0 !important; +} + .jfx-combo-box > .arrow-button > .arrow { -fx-background-color: null; - -fx-border-color: -jfx-unfocus-color; + -fx-border-color: -bs-color-gray-line; -fx-shape: "M 0 0 l 3.5 4 l 3.5 -4"; } +.combo-box-popup { + -fx-background-color: -bs-color-background-pane; + -fx-background-radius: 15; + -fx-border-radius: 15; + -fx-padding: 5; +} + +.combo-box-popup .scroll-pane { + -fx-background-color: -bs-color-background-pane; + -fx-background-radius: 15; + -fx-border-radius: 15; + -fx-padding: 5; +} + +.combo-box-popup > .list-view { + -fx-background-color: -bs-color-background-pane; + -fx-border-color: -bs-color-border-form-field; + -fx-translate-y: 4; + -fx-background-radius: 15; + -fx-border-radius: 15; + -fx-padding: 5; +} + +/* Rounds the first and last list cells to create full round illusion */ +.combo-box-popup .list-cell:first-child { + -fx-background-radius: 10 10 0 0; +} +.combo-box-popup .list-cell:last-child { + -fx-background-radius: 0 0 10 10; +} + +.combo-box-popup .list-cell:hover { + -fx-background-radius: 8; +} + +.combo-box-popup > .list-view:hover { + -fx-cursor: hand; +} + .combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected { -fx-background: -fx-selection-bar; -fx-background-color: -fx-selection-bar; + -fx-background-radius: 15; + -fx-border-radius: 15; } .combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:hover, @@ -301,19 +410,12 @@ tree-table-view:focused { -fx-background-insets: 0; } -.jfx-text-field { - -jfx-focus-color: -bs-color-primary; - -fx-background-color: -bs-background-color; - -fx-background-radius: 3 3 0 0; - -fx-padding: 0.333333em 0.333333em 0.333333em 0.333333em; +/* combo box list view */ +.combo-box .list-view .list-cell:odd { + -fx-background-color: -bs-color-background-pane; } - -.jfx-text-field > .input-line { - -fx-translate-x: -0.333333em; -} - -.jfx-text-field > .input-focused-line { - -fx-translate-x: -0.333333em; +.combo-box .list-view .list-cell:even { + -fx-background-color: -bs-color-background-pane; } .jfx-text-field-top-label { @@ -321,8 +423,7 @@ tree-table-view:focused { } .jfx-text-field:readonly, .hyperlink-with-icon { - -fx-background-color: -bs-color-gray-1; - -fx-padding: 0.333333em 0.333333em 0.333333em 0.333333em; + -fx-background-color: -bs-color-background-form-field-readonly; } .jfx-text-field:readonly > .input-line { @@ -348,23 +449,16 @@ tree-table-view:focused { } .jfx-password-field { - -fx-background-color: -bs-background-color; - -fx-background-radius: 3 3 0 0; - -jfx-focus-color: -bs-color-primary; - -fx-padding: 0.333333em 0.333333em 0.333333em 0.333333em; + -fx-background-color: -bs-color-background-form-field; } .jfx-password-field > .input-line { -fx-translate-x: -0.333333em; } -.jfx-password-field > .input-focused-line { - -fx-translate-x: -0.333333em; -} - -.jfx-text-field:error, .jfx-password-field:error, .jfx-text-area:error { - -jfx-focus-color: -bs-rd-error-red; - -jfx-unfocus-color: -bs-rd-error-red; +.jfx-combo-box:error, +.jfx-text-field:error { + -fx-text-fill: -bs-rd-error-red; } .jfx-text-field .error-label, .jfx-password-field .error-label, .jfx-text-area .error-label { @@ -378,58 +472,69 @@ tree-table-view:focused { -fx-font-size: 1em; } -.input-with-border { - -fx-background-color: -bs-background-color; - -fx-border-width: 1; +.offer-input { + -fx-background-color: -bs-color-background-form-field; -fx-border-color: -bs-background-gray; - -fx-border-radius: 3; -fx-pref-height: 43; -fx-pref-width: 310; - -fx-effect: innershadow(gaussian, -bs-text-color-transparent, 3, 0, 0, 1); -} - -.input-with-border .text-field { - -fx-alignment: center-right; - -fx-pref-height: 43; - -fx-font-size: 1.385em; -} - -.input-with-border > .input-label { - -fx-font-size: 0.692em; - -fx-min-width: 60; - -fx-padding: 16; + -fx-effect: innershadow(gaussian, -bs-text-color-transparent, 3, 0, 0, 0); + -fx-background-radius: 999; + -fx-border-radius: 999; -fx-alignment: center; } -.input-with-border .icon { +.offer-input .text-field { + -fx-alignment: center-right; + -fx-pref-height: 44; + -fx-font-size: 1.385em; + -fx-background-radius: 999 0 0 999; + -fx-border-radius: 999 0 0 999; + -fx-background-color: -bs-color-background-form-field; + -fx-border-color: transparent; +} + +.offer-input > .input-label { + -fx-font-size: 0.692em; + -fx-min-width: 45; + -fx-padding: 8; + -fx-alignment: center; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-background-color: derive(-bs-color-background-form-field, 15%); +} + +.offer-input .icon { -fx-padding: 10; } -.input-with-border-readonly { +.offer-input-readonly { -fx-background-color: -bs-color-gray-1; -fx-border-width: 0; -fx-pref-width: 300; + -fx-background-radius: 999; + -fx-border-radius: 999; } -.input-with-border-readonly .text-field { +.offer-input-readonly .text-field { -fx-alignment: center-right; -fx-font-size: 1em; -fx-background-color: -bs-color-gray-1; + -fx-border-width: 0; } -.input-with-border-readonly .text-field > .input-line { +.offer-input-readonly .text-field > .input-line { -fx-background-color: transparent; } -.input-with-border-readonly > .input-label { +.offer-input-readonly > .input-label { -fx-font-size: 0.692em; -fx-min-width: 30; -fx-padding: 8; -fx-alignment: center; } -.input-with-border-readonly .icon { - -fx-padding: 2; +.offer-input-readonly .icon { + -fx-padding: 3; } .jfx-badge .badge-pane { @@ -456,7 +561,8 @@ tree-table-view:focused { } .jfx-badge { - -fx-padding: -3 0 0 0; + -fx-padding: -2 0 0 0; + -fx-border-insets: 0 0 0 0; } .jfx-toggle-button, @@ -469,11 +575,15 @@ tree-table-view:focused { -jfx-size: 8; } +.jfx-toggle-button:hover { + -fx-cursor: hand; +} + .jfx-text-area { - -jfx-focus-color: -bs-color-primary; - -jfx-unfocus-color: -bs-color-gray-line; - -fx-background-color: -bs-background-color; - -fx-padding: 0.333333em 0.333333em 0.333333em 0.333333em; + -fx-background-color: -bs-color-background-form-field; + -fx-padding: 9 9 9 9; + -fx-background-radius: 15; + -fx-border-radius: 15; } .jfx-text-area:readonly { @@ -484,8 +594,10 @@ tree-table-view:focused { -fx-translate-x: -0.333333em; } -.jfx-text-area > .input-focused-line { - -fx-translate-x: -0.333333em; +.text-area .viewport { + -fx-background-color: transparent; + -fx-background-radius: 15; + -fx-border-radius: 15; } .wallet-seed-words { @@ -501,16 +613,18 @@ tree-table-view:focused { -jfx-default-color: -bs-color-primary; } -.jfx-date-picker .jfx-text-field .jfx-text-area { +.jfx-date-picker { -fx-padding: 0.333333em 0em 0.333333em 0em; } .jfx-date-picker .jfx-text-field .jfx-text-area > .input-line { -fx-translate-x: 0em; + -fx-background-color: transparent; } .jfx-date-picker .jfx-text-field .jfx-text-area > .input-focused-line { -fx-translate-x: 0em; + -fx-background-color: transparent; } .jfx-date-picker > .arrow-button > .arrow { @@ -535,14 +649,14 @@ tree-table-view:focused { .scroll-bar:horizontal .track, .scroll-bar:vertical .track { - -fx-background-color: -bs-background-color; - -fx-border-color: -bs-background-color; + -fx-background-color: -bs-color-background-pane; + -fx-border-color: -bs-color-background-pane; -fx-background-radius: 0; } .scroll-bar:vertical .track-background, .scroll-bar:horizontal .track-background { - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-pane; -fx-background-insets: 0; -fx-background-radius: 0; } @@ -573,7 +687,7 @@ tree-table-view:focused { .scroll-bar:vertical .decrement-button, .scroll-bar:horizontal .increment-button, .scroll-bar:horizontal .decrement-button { - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-pane; -fx-padding: 1; } @@ -582,12 +696,12 @@ tree-table-view:focused { .scroll-bar:horizontal .decrement-arrow, .scroll-bar:vertical .decrement-arrow { -fx-shape: null; - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-pane; } .scroll-bar:vertical:focused, .scroll-bar:horizontal:focused { - -fx-background-color: -bs-background-color, -bs-color-gray-ccc, -bs-color-gray-ddd; + -fx-background-color: -bs-color-background-pane; } /* Behavior */ @@ -632,7 +746,7 @@ tree-table-view:focused { /* Main UI */ #base-content-container { - -fx-background-color: -bs-background-gray; + -fx-background-color: -bs-color-gray-background; } .content-pane { @@ -659,8 +773,8 @@ tree-table-view:focused { -fx-background-color: -bs-rd-nav-background; -fx-border-width: 0 0 1 0; -fx-border-color: -bs-rd-nav-primary-border; - -fx-pref-height: 57; - -fx-padding: 0 11 0 0; + -fx-background-radius: 999; + -fx-border-radius: 999; } .top-navigation .separator:vertical .line { @@ -669,50 +783,54 @@ tree-table-view:focused { -fx-border-insets: 0 0 0 1; } +.nav-logo { + -fx-max-width: 190; + -fx-min-width: 155; +} + .nav-primary { -fx-background-color: -bs-rd-nav-primary-background; - -fx-padding: 0 11 0 11; - -fx-border-width: 0 1 0 0; + -fx-border-width: 0 0 0 0; -fx-border-color: -bs-rd-nav-primary-border; - -fx-min-width: 410; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-padding: 9 0 9 20; } .nav-secondary { - -fx-padding: 0 11 0 11; - -fx-min-width: 296; + -fx-padding: 0 14 0 0; } -.nav-price-balance { - -fx-background-color: -bs-color-gray-background; - -fx-background-radius: 3; - -fx-effect: innershadow(gaussian, -bs-text-color-transparent, 3, 0, 0, 1); - -fx-pref-height: 41; - -fx-padding: 0 10 0 0; -} - -.nav-price-balance .separator:vertical .line { +.nav-separator { + -fx-max-width: 1; + -fx-min-width: 1; -fx-border-color: transparent transparent transparent -bs-rd-separator-dark; -fx-border-width: 1; -fx-border-insets: 0 0 0 1; } -.nav-price-balance .jfx-combo-box > .input-line { - -fx-pref-height: 0px; +.nav-spacer { + -fx-max-width: 10; + -fx-min-width: 10; } -.jfx-badge > .nav-button { + +.jfx-badge > .nav-button, +.jfx-badge > .nav-secondary-button { -fx-translate-y: 1; } .nav-button { -fx-cursor: hand; -fx-background-color: transparent; - -fx-padding: 11; + -fx-padding: 9 15; + -fx-background-radius: 999; + -fx-border-radius: 999; } .nav-button .text { - -fx-font-size: 0.769em; - -fx-font-weight: bold; + -fx-font-size: 0.95em; + -fx-font-weight: 500; -fx-fill: -bs-rd-nav-deselected; } @@ -722,23 +840,84 @@ tree-table-view:focused { .nav-button:selected { -fx-background-color: -bs-background-color; - -fx-border-radius: 4; -fx-effect: dropshadow(gaussian, -bs-text-color-transparent, 4, 0, 0, 0, 2); } +.top-navigation .nav-button:hover { + -fx-background-color: -bs-rd-nav-button-hover; +} + +.nav-primary .nav-button:hover { + -fx-background-color: -bs-rd-nav-primary-button-hover; +} + .nav-button:selected .text { -fx-fill: -bs-rd-nav-selected; } +.nav-secondary-button { + -fx-cursor: hand; + -fx-padding: 9 2 9 2; + -fx-border-insets: 0 12 1 12; + -fx-border-color: transparent; + -fx-border-width: 0 0 1px 0; +} + +.nav-secondary-button .text { + -fx-font-size: 0.95em; + -fx-font-weight: 500; + -fx-fill: -bs-rd-nav-secondary-deselected; +} + +.nav-secondary-button-japanese .text { + -fx-font-size: 1em; +} + +.nav-secondary-button:selected { + -fx-border-color: transparent transparent -bs-rd-nav-secondary-selected transparent; + -fx-border-width: 0 0 1px 0; +} + +.nav-secondary-button:hover { +} + +.nav-secondary-button:selected .text { + -fx-fill: -bs-rd-nav-secondary-selected; +} + .nav-balance-display { -fx-alignment: center-left; -fx-text-fill: -bs-rd-font-balance; } +.nav-price-balance { + -fx-background-color: -bs-rd-nav-background; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-padding: 0 20 0 20; +} + +.nav-price-balance .separator:vertical .line { + -fx-border-color: transparent transparent transparent -bs-rd-separator-dark; + -fx-border-width: 1; + -fx-border-insets: 0 0 0 1; +} + +.nav-price-balance .jfx-combo-box { + -fx-border-color: transparent; + -fx-padding: 0; + -fx-pref-width: 180; +} + +.nav-price-balance .jfx-combo-box > .input-line { + -fx-pref-height: 0px; +} + .nav-balance-label { -fx-font-size: 0.769em; -fx-alignment: center-left; -fx-text-fill: -bs-rd-font-balance-label; + -fx-padding: 0; } #nav-alert-label { @@ -794,6 +973,10 @@ tree-table-view:focused { -fx-text-fill: -bs-background-color; } +.copy-icon-disputes.label .glyph-icon { + -fx-fill: -bs-background-color; +} + .copy-icon:hover { -fx-text-fill: -bs-text-color; } @@ -948,12 +1131,18 @@ textfield */ * * ******************************************************************************/ .table-view .table-row-cell:even .table-cell { - -fx-background-color: derive(-bs-background-color, 5%); - -fx-border-color: derive(-bs-background-color,5%); + -fx-background-color: -bs-color-background-row-even; + -fx-border-color: -bs-color-background-row-even; } .table-view .table-row-cell:odd .table-cell { - -fx-background-color: derive(-bs-background-color,-5%); - -fx-border-color: derive(-bs-background-color,-5%); + -fx-background-color: -bs-color-background-row-odd; + -fx-border-color: -bs-color-background-row-odd; +} +.table-view .table-row-cell.row-faded .table-cell .text { + -fx-fill: -bs-color-table-cell-dim; +} +.cell-faded { + -fx-opacity: 0.4; } .table-view .table-row-cell:hover .table-cell, .table-view .table-row-cell:selected .table-cell { @@ -975,42 +1164,41 @@ textfield */ .table-view .table-cell { -fx-alignment: center-left; - -fx-padding: 2 0 2 0; + -fx-padding: 6 0 4 0; + -fx-text-fill: -bs-text-color; /*-fx-padding: 3 0 2 0;*/ } .table-view .table-cell.last-column { - -fx-alignment: center-right; - -fx-padding: 2 10 2 0; + -fx-padding: 6 0 4 0; } -.table-view .table-cell.last-column.avatar-column { - -fx-alignment: center; - -fx-padding: 2 0 2 0; -} - -.table-view .column-header.last-column { - -fx-padding: 0 10 0 0; -} - -.table-view .column-header.last-column .label { - -fx-alignment: center-right; -} - -.table-view .column-header.last-column.avatar-column { - -fx-padding: 0; -} - -.table-view .column-header.last-column.avatar-column .label { +.table-view .table-cell.avatar-column { -fx-alignment: center; + -fx-padding: 6 0 4 0; } .table-view .table-cell.first-column { - -fx-padding: 2 0 2 10; + -fx-padding: 6 0 4 0; +} + +.table-view .column-header.last-column .label { } .table-view .column-header.first-column { - -fx-padding: 0 0 0 10; + -fx-padding: 0 0 0 0; +} + +.table-view .column-header.last-column { + -fx-padding: 0 0 0 0; +} + +.table-view .column-header.avatar-column { + -fx-padding: 0; +} + +.table-view .column-header.avatar-column .label { + -fx-alignment: center; } .number-column.table-cell { @@ -1019,31 +1207,31 @@ textfield */ } .table-view .filler { - -fx-background-color: -bs-color-gray-0; + -fx-background-color: transparent; } .table-view { -fx-control-inner-background-alt: -fx-control-inner-background; + -fx-padding: 0; +} + +.table-view .column-header-background { + -fx-background-color: -bs-color-background-pane; + -fx-border-color: -bs-color-border-form-field; + -fx-border-width: 0 0 1 0; } .table-view .column-header .label { -fx-alignment: center-left; -fx-font-weight: normal; -fx-font-size: 0.923em; - -fx-padding: 0; + -fx-padding: 6 0 6 0; + -fx-text-fill: -bs-text-color; } .table-view .column-header { - -fx-background-color: -bs-color-gray-0; - -fx-padding: 0; -} - -.table-view .focus { - -fx-alignment: center-left; -} - -.table-view .text { - -fx-fill: -bs-text-color; + -fx-border-color: transparent; + -fx-background-color: -bs-color-background-pane; } /* horizontal scrollbars are never needed and are flickering at scaling so lets turn them off */ @@ -1051,17 +1239,6 @@ textfield */ -fx-opacity: 0; } -.table-view:focused { - -fx-background-color: -fx-box-border, -fx-control-inner-background; - -fx-background-insets: 0, 1; - -fx-padding: 1; -} - -.table-view:focused .table-row-cell:focused { - -fx-background-color: -fx-table-cell-border-color, -fx-background; - -fx-background-insets: 0, 0 0 1 0; -} - .offer-table .table-row-cell { -fx-border-color: -bs-background-color; -fx-table-cell-border-color: -bs-background-color; @@ -1141,6 +1318,46 @@ textfield */ -fx-cell-size: 47px; } +.table-view.offer-table { + -fx-background-radius: 0; + -fx-border-radius: 0; +} + +.table-view.offer-table .column-header.first-column { + -fx-background-radius: 0; + -fx-border-radius: 0; +} + +.table-view.offer-table .column-header.last-column { + -fx-background-radius: 0; + -fx-border-radius: 0; +} + +.table-view.offer-table .table-row-cell { + -fx-background: -fx-accent; + -fx-background-color: -bs-color-gray-6; +} + +.offer-table-top { + -fx-background-color: -bs-color-background-pane; + -fx-padding: 15 15 5 15; + -fx-background-radius: 15 15 0 0; + -fx-border-radius: 15 15 0 0; + -fx-border-width: 0 0 0 0; +} + +.offer-table-top .label { + -fx-text-fill: -bs-text-color; + -fx-font-size: 1.1em; + -fx-font-weight: bold; +} + +.offer-table-top .jfx-button { + -fx-pref-width: 300px; + -fx-min-height: 35px; + -fx-padding: 5 25 5 25; +} + /******************************************************************************* * * * Icons * @@ -1204,17 +1421,28 @@ textfield */ -fx-border-color: -bs-background-gray; } -.text-area-no-border { - -fx-border-color: -bs-background-color; +.text-area-popup { + -fx-border-color: -bs-color-background-popup-blur; } -.text-area-no-border .content { - -fx-background-color: -bs-background-color; +.text-area-popup .content { + -fx-background-color: -bs-color-background-popup-blur; } -.text-area-no-border:focused { - -fx-focus-color: -bs-background-color; - -fx-faint-focus-color: -bs-background-color; +.text-area-popup:focused { + -fx-faint-focus-color: -bs-color-background-popup-blur; +} + +.notification-popup-bg .text-area-popup, .peer-info-popup-bg .text-area-popup { + -fx-border-color: -bs-color-background-popup; +} + +.notification-popup-bg .text-area-popup .content, .peer-info-popup-bg .text-area-popup .content{ + -fx-background-color: -bs-color-background-popup; +} + +.notification-popup-bg .text-area-popup:focused, .peer-info-popup-bg .text-area-popup:focused { + -fx-faint-focus-color: -bs-color-background-popup; } /******************************************************************************* @@ -1238,7 +1466,7 @@ textfield */ } .jfx-tab-pane .headers-region .tab .tab-container .tab-close-button .jfx-rippler { - -jfx-rippler-fill: -fx-accent; + -jfx-rippler-fill: none; } .tab:disabled .jfx-rippler { @@ -1256,7 +1484,7 @@ textfield */ } .jfx-tab-pane .headers-region .tab .tab-container .tab-close-button { - -fx-padding: 0 0 2 0; + -fx-padding: 0 0 0 0; } .jfx-tab-pane .headers-region .tab:selected .tab-container .tab-close-button > .jfx-svg-glyph { @@ -1276,8 +1504,8 @@ textfield */ .jfx-tab-pane .headers-region .tab .tab-container .tab-label { -fx-text-fill: -bs-rd-font-light; - -fx-padding: 14; - -fx-font-size: 0.769em; + -fx-padding: 9 14; + -fx-font-size: .95em; -fx-font-weight: normal; -fx-cursor: hand; } @@ -1291,7 +1519,7 @@ textfield */ } .jfx-tab-pane .headers-region > .tab > .jfx-rippler { - -jfx-rippler-fill: -fx-accent; + -jfx-rippler-fill: none; } .jfx-tab-pane .headers-region .tab:closable { @@ -1395,7 +1623,7 @@ textfield */ } #payment-info { - -fx-background-color: -bs-content-background-gray; + -fx-background-color: -bs-color-gray-fafa; } .toggle-button-active { @@ -1406,6 +1634,19 @@ textfield */ -fx-background-color: -bs-color-gray-1; } +.toggle-button-no-slider { + -fx-border-width: 1px; + -fx-border-color: -bs-color-border-form-field; + -fx-background-insets: 0; + -fx-pref-height: 36px; + -fx-focus-color: transparent; + -fx-faint-focus-color: transparent; +} + +.toggle-button-no-slider:hover { + -fx-cursor: hand; +} + #trade-fee-textfield { -fx-font-size: 0.9em; -fx-alignment: center-right; @@ -1425,7 +1666,7 @@ textfield */ .combo-box-editor-bold { -fx-font-weight: bold; - -fx-padding: 5 8 5 8 !important; + -fx-padding: 0 !important; -fx-text-fill: -bs-text-color; -fx-font-family: "IBM Plex Sans Medium"; } @@ -1456,6 +1697,15 @@ textfield */ -fx-pref-height: 35px; } +.offer-label { + -fx-background-color: rgb(50, 95, 182); + -fx-text-fill: white; + -fx-font-weight: normal; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-padding: 0 6 0 6; +} + /* Offer */ .percentage-label { -fx-alignment: center; @@ -1619,7 +1869,7 @@ textfield */ .titled-group-bg, .titled-group-bg-active { -fx-body-color: -bs-color-gray-background; -fx-border-color: -bs-rd-separator; - -fx-border-width: 0 0 1 0; + -fx-border-width: 0 0 0 0; -fx-background-color: transparent; -fx-background-insets: 0; } @@ -1729,11 +1979,23 @@ textfield */ * * ******************************************************************************/ .grid-pane { - -fx-background-color: -bs-content-background-gray; - -fx-background-radius: 5; - -fx-effect: null; - -fx-effect: dropshadow(gaussian, -bs-color-gray-10, 10, 0, 0, 0); + -fx-background-color: -bs-color-background-popup-blur; -fx-background-insets: 10; + -fx-background-radius: 15; + -fx-border-radius: 15; + -fx-padding: 35, 40, 30, 40; +} + +.grid-pane-separator { + -fx-border-color: -bs-rd-separator; + -fx-border-width: 0 0 1 0; + -fx-translate-y: -2; +} + +.grid-pane .text-area { + -fx-border-width: 1; + -fx-border-color: -bs-color-border-form-field; + -fx-text-fill: -bs-text-color; } /******************************************************************************************************************** @@ -1762,24 +2024,26 @@ textfield */ -fx-text-alignment: center; } -#charts .chart-plot-background, #charts-dao .chart-plot-background { - -fx-background-color: -bs-background-color; +.chart-pane, .chart-plot-background, #charts .chart-plot-background { + -fx-background-color: transparent; } #charts .default-color0.chart-area-symbol { - -fx-background-color: -bs-sell, -bs-background-color; -} - -#charts .default-color1.chart-area-symbol, #charts-dao .default-color0.chart-area-symbol { -fx-background-color: -bs-buy, -bs-background-color; } +#charts .default-color1.chart-area-symbol, #charts-dao .default-color0.chart-area-symbol { + -fx-background-color: -bs-sell, -bs-background-color; +} + #charts .default-color0.chart-series-area-line { - -fx-stroke: -bs-sell; + -fx-stroke: -bs-buy; + -fx-stroke-width: 2px; } #charts .default-color1.chart-series-area-line, #charts-dao .default-color0.chart-series-area-line { - -fx-stroke: -bs-buy; + -fx-stroke: -bs-sell; + -fx-stroke-width: 2px; } /* The .chart-line-symbol rules change the color of the legend symbol */ @@ -1907,13 +2171,6 @@ textfield */ -fx-stroke-width: 2px; } -#charts .default-color0.chart-series-area-fill { - -fx-fill: -bs-sell-transparent; -} - -#charts .default-color1.chart-series-area-fill, #charts-dao .default-color0.chart-series-area-fill { - -fx-fill: -bs-buy-transparent; -} .chart-vertical-grid-lines { -fx-stroke: transparent; } @@ -2013,22 +2270,41 @@ textfield */ -fx-text-fill: -bs-rd-error-red; } -.popup-bg, .notification-popup-bg, .peer-info-popup-bg { +.popup-headline-information.label .glyph-icon, +.popup-headline-warning.label .glyph-icon, +.popup-icon-information.label .glyph-icon, +.popup-icon-warning.label .glyph-icon { + -fx-fill: -bs-color-primary; +} + +.popup-bg { -fx-font-size: 1.077em; - -fx-text-fill: -bs-rd-font-dark; - -fx-background-color: -bs-background-color; - -fx-background-radius: 0; + -fx-background-color: -bs-color-background-popup-blur; -fx-background-insets: 44; - -fx-effect: dropshadow(gaussian, -bs-text-color-transparent-dark, 44, 0, 0, 0); + -fx-background-radius: 15; + -fx-border-radius: 15; +} + +.notification-popup-bg, .peer-info-popup-bg { + -fx-font-size: 0.846em; + -fx-text-fill: -bs-rd-font-dark; + -fx-background-color: -bs-color-background-popup; + -fx-background-insets: 44; + -fx-effect: dropshadow(gaussian, -bs-text-color-dropshadow-light-mode, 44, 0, 0, 0); + -fx-background-radius: 15; + -fx-border-radius: 15; } .popup-bg-top { -fx-font-size: 1.077em; -fx-text-fill: -bs-rd-font-dark; - -fx-background-color: -bs-background-color; - -fx-background-radius: 0; + -fx-background-color: -bs-color-background-popup-blur; -fx-background-insets: 44; - -fx-effect: dropshadow(gaussian, -bs-text-color-transparent-dark, 44, 0, 0, 0); + -fx-background-radius: 0 0 15px 15px; +} + +.popup-dropshadow { + -fx-effect: dropshadow(gaussian, -bs-text-color-dropshadow, 20, 0, 0, 0); } .notification-popup-headline, peer-info-popup-headline { @@ -2037,18 +2313,6 @@ textfield */ -fx-text-fill: -bs-color-primary; } -.notification-popup-bg { - -fx-font-size: 0.846em; - -fx-background-insets: 44; - -fx-effect: dropshadow(gaussian, -bs-text-color-transparent-dark, 44, 0, -1, 3); -} - -.peer-info-popup-bg { - -fx-font-size: 0.846em; - -fx-background-insets: 44; - -fx-effect: dropshadow(gaussian, -bs-text-color-transparent-dark, 44, 0, -1, 3); -} - .account-status-title { -fx-font-size: 0.769em; -fx-font-family: "IBM Plex Sans Medium"; @@ -2069,7 +2333,7 @@ textfield */ } #price-feed-combo > .list-cell { - -fx-text-fill: -bs-text-color; + -fx-text-fill: -bs-rd-font-balance; -fx-font-family: "IBM Plex Sans"; } @@ -2089,42 +2353,48 @@ textfield */ } #toggle-left { - -fx-border-radius: 4 0 0 4; -fx-border-color: -bs-rd-separator-dark; + -fx-border-radius: 4 0 0 4; -fx-border-style: solid; - -fx-border-width: 0 1 0 0; + -fx-border-width: 1 1 1 1; -fx-background-radius: 4 0 0 4; + -fx-border-insets: 0; + -fx-background-insets: 1 1 1 1; -fx-background-color: -bs-background-color; -fx-effect: dropshadow(gaussian, -bs-text-color-transparent, 4, 0, 0, 0, 2); } #toggle-center { - -fx-border-radius: 0; -fx-border-color: -bs-rd-separator-dark; + -fx-border-radius: 0; -fx-border-style: solid; - -fx-border-width: 0 1 0 0; + -fx-border-width: 1 1 1 0; -fx-border-insets: 0; - -fx-background-insets: 0; + -fx-background-insets: 1 1 1 0; -fx-background-radius: 0; -fx-background-color: -bs-background-color; -fx-effect: dropshadow(gaussian, -bs-text-color-transparent, 4, 0, 0, 0, 2); } -#toggle-center:selected, #toggle-left:selected, #toggle-right:selected { - -fx-text-fill: -bs-background-color; - -fx-background-color: -bs-toggle-selected; -} - #toggle-right { + -fx-border-color: -bs-rd-separator-dark; -fx-border-radius: 0 4 4 0; - -fx-border-width: 0; + -fx-border-width: 1 1 1 0; + -fx-border-insets: 0; + -fx-background-insets: 1 1 1 0; -fx-background-radius: 0 4 4 0; -fx-background-color: -bs-background-color; -fx-effect: dropshadow(gaussian, -bs-text-color-transparent, 4, 0, 0, 0, 2); } +#toggle-center:selected, #toggle-left:selected, #toggle-right:selected { + -fx-text-fill: white; + -fx-background-color: -bs-toggle-selected; +} + #toggle-left:hover, #toggle-right:hover, #toggle-center:hover { -fx-background-color: -bs-toggle-selected; + -fx-cursor: hand; } /******************************************************************************************************************** @@ -2136,10 +2406,18 @@ textfield */ -fx-text-fill: -bs-text-color; } +.message.label .glyph-icon { + -fx-fill: -bs-text-color; +} + .my-message { -fx-text-fill: -bs-background-color; } +.my-message.label .glyph-icon { + -fx-fill: -bs-background-color; +} + .message-header { -fx-text-fill: -bs-color-gray-3; -fx-font-size: 0.846em; @@ -2308,18 +2586,103 @@ textfield */ /******************************************************************************************************************** * * - * Popover * + * Popover * * * ********************************************************************************************************************/ .popover > .content { -fx-padding: 10; + -fx-background-color: -bs-color-background-popup; + -fx-border-radius: 3; + -fx-background-radius: 3; + -fx-background-insets: 1; } .popover > .content .default-text { -fx-text-fill: -bs-text-color; } -.popover > .border { - -fx-stroke: linear-gradient(to bottom, -bs-text-color-transparent, -bs-text-color-transparent-dark) !important; - -fx-fill: -bs-background-color !important; +.popover > .content .text-field { + -fx-background-color: -bs-color-background-form-field-readonly !important; + -fx-border-radius: 4; + -fx-background-radius: 4; +} + +.popover > .border { + -fx-stroke: linear-gradient(to bottom, -bs-text-color-transparent, -bs-text-color-dropshadow) !important; + -fx-fill: -bs-color-background-popup !important; +} + +/******************************************************************************************************************** + * * + * Other * + * * + ********************************************************************************************************************/ +.input-with-border { + -fx-border-width: 1; + -fx-border-color: -bs-color-border-form-field; + -fx-border-insets: 1 0 1 0; + -fx-background-insets: 1 0 1 0; +} + +.table-view.non-interactive-table .column-header .label { + -fx-text-fill: -bs-text-color-dim2; +} + +.highlight-text { + -fx-text-fill: -fx-dark-text-color !important; +} + +.grid-pane .text-area, +.flat-text-area-with-border { + -fx-background-radius: 8; + -fx-border-radius: 8; + -fx-font-size: 1.077em; + -fx-font-family: "IBM Plex Sans"; + -fx-font-weight: normal; + -fx-text-fill: -bs-rd-font-dark-gray !important; + -fx-border-width: 1; + -fx-border-color: -bs-color-border-form-field !important; +} + +.grid-pane .text-area:readonly, +.flat-text-area-with-border { + -fx-background-color: transparent !important; +} + +.grid-pane .text-area { + -fx-max-height: 150 !important; +} + +.passphrase-copy-box { + -fx-border-width: 1; + -fx-border-color: -bs-color-border-form-field; + -fx-background-radius: 8; + -fx-border-radius: 8; + -fx-padding: 13; + -fx-background-insets: 0; +} + +.passphrase-copy-box > .jfx-text-field { + -fx-padding: 0; + -fx-background-color: transparent; + -fx-border-width: 0; +} + +.passphrase-copy-box .label { + -fx-text-fill: white; + -fx-padding: 0; +} + +.passphrase-copy-box .jfx-button { + -fx-padding: 5 15 5 15; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-min-height: 0; + -fx-font-size: 1.077em; + -fx-font-family: "IBM Plex Sans"; + -fx-font-weight: normal; +} + +.popup-with-input { + -fx-background-color: -bs-color-background-popup-input; } diff --git a/desktop/src/main/java/haveno/desktop/images.css b/desktop/src/main/java/haveno/desktop/images.css index aacd4c6b1b..1e36427ee9 100644 --- a/desktop/src/main/java/haveno/desktop/images.css +++ b/desktop/src/main/java/haveno/desktop/images.css @@ -1,16 +1,3 @@ -/* splash screen */ -/*noinspection CssUnknownTarget*/ -#image-splash-logo { - -fx-image: url("../../images/logo_splash.png"); -} - -/* splash screen testnet */ -/*noinspection CssUnknownTarget*/ -#image-splash-testnet-logo { - -fx-image: url("../../images/logo_splash_testnet.png"); -} - -/* shared*/ #image-info { -fx-image: url("../../images/info.png"); } @@ -23,16 +10,29 @@ -fx-image: url("../../images/alert_round.png"); } +#image-red_circle_solid { + -fx-image: url("../../images/red_circle_solid.png"); +} + + #image-green_circle { -fx-image: url("../../images/green_circle.png"); } +#image-green_circle_solid { + -fx-image: url("../../images/green_circle_solid.png"); +} + #image-yellow_circle { -fx-image: url("../../images/yellow_circle.png"); } -#image-blue_circle { - -fx-image: url("../../images/blue_circle.png"); +#image-yellow_circle_solid { + -fx-image: url("../../images/yellow_circle_solid.png"); +} + +#image-blue_circle_solid { + -fx-image: url("../../images/blue_circle_solid.png"); } #image-remove { @@ -300,3 +300,59 @@ #image-new-trade-protocol-screenshot { -fx-image: url("../../images/new_trade_protocol_screenshot.png"); } + +#image-support { + -fx-image: url("../../images/support.png"); +} + +#image-account { + -fx-image: url("../../images/account.png"); +} + +#image-settings { + -fx-image: url("../../images/settings.png"); +} + +#image-btc-logo { + -fx-image: url("../../images/btc_logo.png"); +} + +#image-bch-logo { + -fx-image: url("../../images/bch_logo.png"); +} + +#image-dai-erc20-logo { + -fx-image: url("../../images/dai-erc20_logo.png"); +} + +#image-eth-logo { + -fx-image: url("../../images/eth_logo.png"); +} + +#image-ltc-logo { + -fx-image: url("../../images/ltc_logo.png"); +} + +#image-usdc-erc20-logo { + -fx-image: url("../../images/usdc-erc20_logo.png"); +} + +#image-usdt-erc20-logo { + -fx-image: url("../../images/usdt-erc20_logo.png"); +} + +#image-usdt-trc20-logo { + -fx-image: url("../../images/usdt-trc20_logo.png"); +} + +#image-xmr-logo { + -fx-image: url("../../images/xmr_logo.png"); +} + +#image-dark-mode-toggle { + -fx-image: url("../../images/dark_mode_toggle.png"); +} + +#image-light-mode-toggle { + -fx-image: url("../../images/light_mode_toggle.png"); +} diff --git a/desktop/src/main/java/haveno/desktop/main/MainView.java b/desktop/src/main/java/haveno/desktop/main/MainView.java index 7856a2021f..ee7434324c 100644 --- a/desktop/src/main/java/haveno/desktop/main/MainView.java +++ b/desktop/src/main/java/haveno/desktop/main/MainView.java @@ -32,6 +32,7 @@ import haveno.core.locale.GlobalSettings; import haveno.core.locale.LanguageUtil; import haveno.core.locale.Res; import haveno.core.provider.price.MarketPrice; +import haveno.core.user.Preferences; import haveno.desktop.Navigation; import haveno.desktop.common.view.CachingViewLoader; import haveno.desktop.common.view.FxmlView; @@ -73,6 +74,7 @@ import javafx.geometry.Insets; import javafx.geometry.NodeOrientation; import javafx.geometry.Orientation; import javafx.geometry.Pos; +import javafx.scene.Cursor; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; @@ -92,6 +94,7 @@ import static javafx.scene.layout.AnchorPane.setRightAnchor; import static javafx.scene.layout.AnchorPane.setTopAnchor; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; @@ -125,17 +128,19 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { private Label xmrSplashInfo; private Popup p2PNetworkWarnMsgPopup, xmrNetworkWarnMsgPopup; private final TorNetworkSettingsWindow torNetworkSettingsWindow; + private final Preferences preferences; + private static final int networkIconSize = 20; public static StackPane getRootContainer() { return MainView.rootContainer; } public static void blurLight() { - transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, -0.6, false, 5); + transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, -0.6, false, 15); } public static void blurUltraLight() { - transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, -0.6, false, 2); + transitions.blur(MainView.rootContainer, Transitions.DEFAULT_DURATION, -0.6, false, 15); } public static void darken() { @@ -151,12 +156,14 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { CachingViewLoader viewLoader, Navigation navigation, Transitions transitions, - TorNetworkSettingsWindow torNetworkSettingsWindow) { + TorNetworkSettingsWindow torNetworkSettingsWindow, + Preferences preferences) { super(model); this.viewLoader = viewLoader; this.navigation = navigation; MainView.transitions = transitions; this.torNetworkSettingsWindow = torNetworkSettingsWindow; + this.preferences = preferences; } @Override @@ -165,15 +172,15 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { if (LanguageUtil.isDefaultLanguageRTL()) MainView.rootContainer.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT); - ToggleButton marketButton = new NavButton(MarketView.class, Res.get("mainView.menu.market").toUpperCase()); - ToggleButton buyButton = new NavButton(BuyOfferView.class, Res.get("mainView.menu.buyXmr").toUpperCase()); - ToggleButton sellButton = new NavButton(SellOfferView.class, Res.get("mainView.menu.sellXmr").toUpperCase()); - ToggleButton portfolioButton = new NavButton(PortfolioView.class, Res.get("mainView.menu.portfolio").toUpperCase()); - ToggleButton fundsButton = new NavButton(FundsView.class, Res.get("mainView.menu.funds").toUpperCase()); + ToggleButton marketButton = new NavButton(MarketView.class, Res.get("mainView.menu.market")); + ToggleButton buyButton = new NavButton(BuyOfferView.class, Res.get("mainView.menu.buyXmr")); + ToggleButton sellButton = new NavButton(SellOfferView.class, Res.get("mainView.menu.sellXmr")); + ToggleButton portfolioButton = new NavButton(PortfolioView.class, Res.get("mainView.menu.portfolio")); + ToggleButton fundsButton = new NavButton(FundsView.class, Res.get("mainView.menu.funds")); - ToggleButton supportButton = new NavButton(SupportView.class, Res.get("mainView.menu.support")); - ToggleButton accountButton = new NavButton(AccountView.class, Res.get("mainView.menu.account")); - ToggleButton settingsButton = new NavButton(SettingsView.class, Res.get("mainView.menu.settings")); + ToggleButton supportButton = new SecondaryNavButton(SupportView.class, Res.get("mainView.menu.support"), "image-support"); + ToggleButton accountButton = new SecondaryNavButton(AccountView.class, Res.get("mainView.menu.account"), "image-account"); + ToggleButton settingsButton = new SecondaryNavButton(SettingsView.class, Res.get("mainView.menu.settings"), "image-settings"); JFXBadge portfolioButtonWithBadge = new JFXBadge(portfolioButton); JFXBadge supportButtonWithBadge = new JFXBadge(supportButton); @@ -298,47 +305,56 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { } }); - HBox primaryNav = new HBox(marketButton, getNavigationSeparator(), buyButton, getNavigationSeparator(), - sellButton, getNavigationSeparator(), portfolioButtonWithBadge, getNavigationSeparator(), fundsButton); + HBox primaryNav = new HBox(getLogoPane(), marketButton, getNavigationSpacer(), buyButton, getNavigationSpacer(), + sellButton, getNavigationSpacer(), portfolioButtonWithBadge, getNavigationSpacer(), fundsButton); primaryNav.setAlignment(Pos.CENTER_LEFT); primaryNav.getStyleClass().add("nav-primary"); HBox.setHgrow(primaryNav, Priority.SOMETIMES); - HBox secondaryNav = new HBox(supportButtonWithBadge, getNavigationSpacer(), accountButton, - getNavigationSpacer(), settingsButtonWithBadge, getNavigationSpacer()); - secondaryNav.getStyleClass().add("nav-secondary"); - HBox.setHgrow(secondaryNav, Priority.SOMETIMES); - - secondaryNav.setAlignment(Pos.CENTER); - HBox priceAndBalance = new HBox(marketPriceBox.second, getNavigationSeparator(), availableBalanceBox.second, getNavigationSeparator(), pendingBalanceBox.second, getNavigationSeparator(), reservedBalanceBox.second); - priceAndBalance.setMaxHeight(41); priceAndBalance.setAlignment(Pos.CENTER); - priceAndBalance.setSpacing(9); + priceAndBalance.setSpacing(12); priceAndBalance.getStyleClass().add("nav-price-balance"); - HBox navPane = new HBox(primaryNav, secondaryNav, getNavigationSpacer(), - priceAndBalance) {{ - setLeftAnchor(this, 0d); - setRightAnchor(this, 0d); - setTopAnchor(this, 0d); + HBox navPane = new HBox(primaryNav, priceAndBalance) {{ + setLeftAnchor(this, 25d); + setRightAnchor(this, 25d); + setTopAnchor(this, 20d); setPadding(new Insets(0, 0, 0, 0)); getStyleClass().add("top-navigation"); }}; navPane.setAlignment(Pos.CENTER); + HBox secondaryNav = new HBox(supportButtonWithBadge, accountButton, settingsButtonWithBadge); + secondaryNav.getStyleClass().add("nav-secondary"); + secondaryNav.setAlignment(Pos.CENTER_RIGHT); + secondaryNav.setPickOnBounds(false); + HBox.setHgrow(secondaryNav, Priority.ALWAYS); + AnchorPane.setLeftAnchor(secondaryNav, 0.0); + AnchorPane.setRightAnchor(secondaryNav, 0.0); + AnchorPane.setTopAnchor(secondaryNav, 0.0); + + AnchorPane secondaryNavContainer = new AnchorPane() {{ + setId("nav-secondary-container"); + setLeftAnchor(this, 0d); + setRightAnchor(this, 0d); + setTopAnchor(this, 94d); + }}; + secondaryNavContainer.setPickOnBounds(false); + secondaryNavContainer.getChildren().add(secondaryNav); + AnchorPane contentContainer = new AnchorPane() {{ getStyleClass().add("content-pane"); setLeftAnchor(this, 0d); setRightAnchor(this, 0d); - setTopAnchor(this, 57d); + setTopAnchor(this, 95d); setBottomAnchor(this, 0d); }}; - AnchorPane applicationContainer = new AnchorPane(navPane, contentContainer) {{ + AnchorPane applicationContainer = new AnchorPane(navPane, contentContainer, secondaryNavContainer) {{ setId("application-container"); }}; @@ -398,15 +414,32 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { private Separator getNavigationSeparator() { final Separator separator = new Separator(Orientation.VERTICAL); HBox.setHgrow(separator, Priority.ALWAYS); - separator.setMaxHeight(22); separator.setMaxWidth(Double.MAX_VALUE); + separator.getStyleClass().add("nav-separator"); return separator; } + @NotNull + private Pane getLogoPane() { + ImageView logo = new ImageView(); + logo.setId("image-logo-landscape"); + logo.setPreserveRatio(true); + logo.setFitHeight(40); + logo.setSmooth(true); + logo.setCache(true); + + final Pane pane = new Pane(); + HBox.setHgrow(pane, Priority.ALWAYS); + pane.getStyleClass().add("nav-logo"); + pane.getChildren().add(logo); + return pane; + } + @NotNull private Region getNavigationSpacer() { final Region spacer = new Region(); HBox.setHgrow(spacer, Priority.ALWAYS); + spacer.getStyleClass().add("nav-spacer"); return spacer; } @@ -447,7 +480,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { priceComboBox.setVisibleRowCount(12); priceComboBox.setFocusTraversable(false); priceComboBox.setId("price-feed-combo"); - priceComboBox.setPadding(new Insets(0, -4, -4, 0)); priceComboBox.setCellFactory(p -> getPriceFeedComboBoxListCell()); ListCell<PriceFeedComboBoxItem> buttonCell = getPriceFeedComboBoxListCell(); buttonCell.setId("price-feed-combo"); @@ -458,7 +490,6 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { updateMarketPriceLabel(marketPriceLabel); marketPriceLabel.getStyleClass().add("nav-balance-label"); - marketPriceLabel.setPadding(new Insets(-2, 0, 4, 9)); marketPriceBox.getChildren().addAll(priceComboBox, marketPriceLabel); @@ -509,7 +540,10 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { vBox.setId("splash"); ImageView logo = new ImageView(); - logo.setId(Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_MAINNET ? "image-splash-logo" : "image-splash-testnet-logo"); + logo.setId(Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_MAINNET ? "image-logo-splash" : "image-logo-splash-testnet"); + logo.setFitWidth(400); + logo.setPreserveRatio(true); + logo.setSmooth(true); // createBitcoinInfoBox xmrSplashInfo = new AutoTooltipLabel(); @@ -552,7 +586,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { // create P2PNetworkBox splashP2PNetworkLabel = new AutoTooltipLabel(); splashP2PNetworkLabel.setWrapText(true); - splashP2PNetworkLabel.setMaxWidth(500); + splashP2PNetworkLabel.setMaxWidth(700); splashP2PNetworkLabel.setTextAlignment(TextAlignment.CENTER); splashP2PNetworkLabel.getStyleClass().add("sub-info"); splashP2PNetworkLabel.textProperty().bind(model.getP2PNetworkInfo()); @@ -587,9 +621,11 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { ImageView splashP2PNetworkIcon = new ImageView(); splashP2PNetworkIcon.setId("image-connection-tor"); + splashP2PNetworkIcon.setFitWidth(networkIconSize); + splashP2PNetworkIcon.setFitHeight(networkIconSize); splashP2PNetworkIcon.setVisible(false); splashP2PNetworkIcon.setManaged(false); - HBox.setMargin(splashP2PNetworkIcon, new Insets(0, 0, 5, 0)); + HBox.setMargin(splashP2PNetworkIcon, new Insets(0, 0, 0, 0)); splashP2PNetworkIcon.setOnMouseClicked(e -> { torNetworkSettingsWindow.show(); }); @@ -603,6 +639,8 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { splashP2PNetworkIcon.setId(newValue); splashP2PNetworkIcon.setVisible(true); splashP2PNetworkIcon.setManaged(true); + splashP2PNetworkIcon.setFitWidth(networkIconSize); + splashP2PNetworkIcon.setFitHeight(networkIconSize); // if we can connect in 10 sec. we know that tor is working showTorNetworkSettingsTimer.stop(); @@ -725,15 +763,39 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { setRightAnchor(versionBox, 10d); setBottomAnchor(versionBox, 7d); + // Dark mode toggle + ImageView useDarkModeIcon = new ImageView(); + useDarkModeIcon.setId(preferences.getCssTheme() == 1 ? "image-dark-mode-toggle" : "image-light-mode-toggle"); + useDarkModeIcon.setFitHeight(networkIconSize); + useDarkModeIcon.setPreserveRatio(true); + useDarkModeIcon.setPickOnBounds(true); + useDarkModeIcon.setCursor(Cursor.HAND); + setRightAnchor(useDarkModeIcon, 8d); + setBottomAnchor(useDarkModeIcon, 6d); + Tooltip modeToolTip = new Tooltip(); + Tooltip.install(useDarkModeIcon, modeToolTip); + useDarkModeIcon.setOnMouseEntered(e -> modeToolTip.setText(Res.get(preferences.getCssTheme() == 1 ? "setting.preferences.useLightMode" : "setting.preferences.useDarkMode"))); + useDarkModeIcon.setOnMouseClicked(e -> { + preferences.setCssTheme(preferences.getCssTheme() != 1); + }); + preferences.getCssThemeProperty().addListener((observable, oldValue, newValue) -> { + useDarkModeIcon.setId(preferences.getCssTheme() == 1 ? "image-dark-mode-toggle" : "image-light-mode-toggle"); + }); + // P2P Network Label p2PNetworkLabel = new AutoTooltipLabel(); p2PNetworkLabel.setId("footer-pane"); p2PNetworkLabel.textProperty().bind(model.getP2PNetworkInfo()); + double networkIconRightAnchor = 54d; ImageView p2PNetworkIcon = new ImageView(); - setRightAnchor(p2PNetworkIcon, 10d); - setBottomAnchor(p2PNetworkIcon, 5d); + setRightAnchor(p2PNetworkIcon, networkIconRightAnchor); + setBottomAnchor(p2PNetworkIcon, 6d); + p2PNetworkIcon.setPickOnBounds(true); + p2PNetworkIcon.setCursor(Cursor.HAND); p2PNetworkIcon.setOpacity(0.4); + p2PNetworkIcon.setFitWidth(networkIconSize); + p2PNetworkIcon.setFitHeight(networkIconSize); p2PNetworkIcon.idProperty().bind(model.getP2PNetworkIconId()); p2PNetworkLabel.idProperty().bind(model.getP2pNetworkLabelId()); model.getP2pNetworkWarnMsg().addListener((ov, oldValue, newValue) -> { @@ -749,8 +811,12 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { }); ImageView p2PNetworkStatusIcon = new ImageView(); - setRightAnchor(p2PNetworkStatusIcon, 30d); - setBottomAnchor(p2PNetworkStatusIcon, 7d); + p2PNetworkStatusIcon.setPickOnBounds(true); + p2PNetworkStatusIcon.setCursor(Cursor.HAND); + p2PNetworkStatusIcon.setFitWidth(networkIconSize); + p2PNetworkStatusIcon.setFitHeight(networkIconSize); + setRightAnchor(p2PNetworkStatusIcon, networkIconRightAnchor + 22); + setBottomAnchor(p2PNetworkStatusIcon, 6d); Tooltip p2pNetworkStatusToolTip = new Tooltip(); Tooltip.install(p2PNetworkStatusIcon, p2pNetworkStatusToolTip); p2PNetworkStatusIcon.setOnMouseEntered(e -> p2pNetworkStatusToolTip.setText(model.getP2pConnectionSummary())); @@ -791,10 +857,10 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { VBox vBox = new VBox(); vBox.setAlignment(Pos.CENTER_RIGHT); vBox.getChildren().addAll(p2PNetworkLabel, p2pNetworkProgressBar); - setRightAnchor(vBox, 53d); + setRightAnchor(vBox, networkIconRightAnchor + 45); setBottomAnchor(vBox, 5d); - return new AnchorPane(separator, xmrInfoLabel, versionBox, vBox, p2PNetworkStatusIcon, p2PNetworkIcon) {{ + return new AnchorPane(separator, xmrInfoLabel, versionBox, vBox, p2PNetworkStatusIcon, p2PNetworkIcon, useDarkModeIcon) {{ setId("footer-pane"); setMinHeight(30); setMaxHeight(30); @@ -825,6 +891,9 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { this.setToggleGroup(navButtons); this.getStyleClass().add("nav-button"); + this.setMinWidth(Region.USE_PREF_SIZE); // prevent squashing content + this.setPrefWidth(Region.USE_COMPUTED_SIZE); + // Japanese fonts are dense, increase top nav button text size if (model.getPreferences() != null && "ja".equals(model.getPreferences().getUserLanguage())) { this.getStyleClass().add("nav-button-japanese"); @@ -836,4 +905,29 @@ public class MainView extends InitializableView<StackPane, MainViewModel> { } } + + private class SecondaryNavButton extends NavButton { + + SecondaryNavButton(Class<? extends View> viewClass, String title, String iconId) { + super(viewClass, title); + this.getStyleClass().setAll("nav-secondary-button"); + + // Japanese fonts are dense, increase top nav button text size + if (model.getPreferences() != null && "ja".equals(model.getPreferences().getUserLanguage())) { + this.getStyleClass().setAll("nav-secondary-button-japanese"); + } + + // add icon + ImageView imageView = new ImageView(); + imageView.setId(iconId); + imageView.setFitWidth(15); + imageView.setPreserveRatio(true); + setGraphicTextGap(10); + setGraphic(imageView); + + // show cursor hand on any hover + this.setPickOnBounds(true); + } + + } } diff --git a/desktop/src/main/java/haveno/desktop/main/account/AccountView.java b/desktop/src/main/java/haveno/desktop/main/account/AccountView.java index 30c434cccd..028276a83a 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/AccountView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/AccountView.java @@ -86,12 +86,12 @@ public class AccountView extends ActivatableView<TabPane, Void> { root.setTabClosingPolicy(TabPane.TabClosingPolicy.ALL_TABS); - traditionalAccountsTab.setText(Res.get("account.menu.paymentAccount").toUpperCase()); - cryptoAccountsTab.setText(Res.get("account.menu.altCoinsAccountView").toUpperCase()); - passwordTab.setText(Res.get("account.menu.password").toUpperCase()); - seedWordsTab.setText(Res.get("account.menu.seedWords").toUpperCase()); - //walletInfoTab.setText(Res.get("account.menu.walletInfo").toUpperCase()); - backupTab.setText(Res.get("account.menu.backup").toUpperCase()); + traditionalAccountsTab.setText(Res.get("account.menu.paymentAccount")); + cryptoAccountsTab.setText(Res.get("account.menu.altCoinsAccountView")); + passwordTab.setText(Res.get("account.menu.password")); + seedWordsTab.setText(Res.get("account.menu.seedWords")); + //walletInfoTab.setText(Res.get("account.menu.walletInfo")); + backupTab.setText(Res.get("account.menu.backup")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(AccountView.class) == 1) { diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java index 8abcd98a9d..bdadfffbda 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsView.java @@ -216,7 +216,7 @@ public class CryptoAccountsView extends PaymentAccountsView<GridPane, CryptoAcco } removeAccountRows(); addAccountButton.setDisable(false); - accountTitledGroupBg = addTitledGroupBg(root, ++gridRow, 2, Res.get("shared.selectedAccount"), Layout.GROUP_DISTANCE); + accountTitledGroupBg = addTitledGroupBg(root, ++gridRow, 2, "", Layout.GROUP_DISTANCE); paymentMethodForm = getPaymentMethodForm(current); paymentMethodForm.addFormForEditAccount(); gridRow = paymentMethodForm.getGridRow(); diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/notifications/ManageMarketAlertsWindow.java b/desktop/src/main/java/haveno/desktop/main/account/content/notifications/ManageMarketAlertsWindow.java index 9ac8a97d47..6694efedad 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/notifications/ManageMarketAlertsWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/notifications/ManageMarketAlertsWindow.java @@ -26,6 +26,7 @@ import haveno.desktop.components.AutoTooltipButton; import haveno.desktop.components.AutoTooltipLabel; import haveno.desktop.components.AutoTooltipTableColumn; import haveno.desktop.main.overlays.Overlay; +import haveno.desktop.util.GUIUtil; import haveno.desktop.util.ImageUtil; import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.collections.FXCollections; @@ -73,6 +74,7 @@ public class ManageMarketAlertsWindow extends Overlay<ManageMarketAlertsWindow> private void addContent() { TableView<MarketAlertFilter> tableView = new TableView<>(); + GUIUtil.applyTableStyle(tableView); GridPane.setRowIndex(tableView, ++rowIndex); GridPane.setColumnSpan(tableView, 2); GridPane.setMargin(tableView, new Insets(10, 0, 0, 0)); diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java index a5f7332c81..79243691bc 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java @@ -535,7 +535,7 @@ public class TraditionalAccountsView extends PaymentAccountsView<GridPane, Tradi } removeAccountRows(); addAccountButton.setDisable(false); - accountTitledGroupBg = addTitledGroupBg(root, ++gridRow, 2, Res.get("shared.selectedAccount"), Layout.GROUP_DISTANCE); + accountTitledGroupBg = addTitledGroupBg(root, ++gridRow, 2, "", Layout.GROUP_DISTANCE); paymentMethodForm = getPaymentMethodForm(current); if (paymentMethodForm != null) { paymentMethodForm.addFormForEditAccount(); diff --git a/desktop/src/main/java/haveno/desktop/main/funds/FundsView.java b/desktop/src/main/java/haveno/desktop/main/funds/FundsView.java index 588f3eb299..372fff9819 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/FundsView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/FundsView.java @@ -55,9 +55,9 @@ public class FundsView extends ActivatableView<TabPane, Void> { @Override public void initialize() { - depositTab.setText(Res.get("funds.tab.deposit").toUpperCase()); - withdrawalTab.setText(Res.get("funds.tab.withdrawal").toUpperCase()); - transactionsTab.setText(Res.get("funds.tab.transactions").toUpperCase()); + depositTab.setText(Res.get("funds.tab.deposit")); + withdrawalTab.setText(Res.get("funds.tab.withdrawal")); + transactionsTab.setText(Res.get("funds.tab.transactions")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(FundsView.class) == 1) diff --git a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java index 884df454e7..72f7945f65 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java @@ -40,6 +40,7 @@ import com.google.inject.name.Named; import haveno.common.ThreadUtils; import haveno.common.UserThread; import haveno.common.app.DevEnv; +import haveno.common.util.Tuple2; import haveno.common.util.Tuple3; import haveno.core.locale.Res; import haveno.core.trade.HavenoUtils; @@ -89,10 +90,9 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.util.Callback; -import monero.common.MoneroUtils; -import monero.wallet.model.MoneroTxConfig; import monero.wallet.model.MoneroWalletListener; import net.glxn.qrgen.QRCode; import net.glxn.qrgen.image.ImageType; @@ -111,6 +111,7 @@ public class DepositView extends ActivatableView<VBox, Void> { @FXML TableColumn<DepositListItem, DepositListItem> addressColumn, balanceColumn, confirmationsColumn, usageColumn; private ImageView qrCodeImageView; + private StackPane qrCodePane; private AddressTextField addressTextField; private Button generateNewAddressButton; private TitledGroupBg titledGroupBg; @@ -144,6 +145,7 @@ public class DepositView extends ActivatableView<VBox, Void> { @Override public void initialize() { + GUIUtil.applyTableStyle(tableView); paymentLabelString = Res.get("funds.deposit.fundHavenoWallet"); addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); @@ -154,6 +156,7 @@ public class DepositView extends ActivatableView<VBox, Void> { // set loading placeholder Label placeholderLabel = new Label("Loading..."); tableView.setPlaceholder(placeholderLabel); + tableView.getStyleClass().add("non-interactive-table"); ThreadUtils.execute(() -> { @@ -190,19 +193,19 @@ public class DepositView extends ActivatableView<VBox, Void> { titledGroupBg = addTitledGroupBg(gridPane, gridRow, 4, Res.get("funds.deposit.fundWallet")); titledGroupBg.getStyleClass().add("last"); - qrCodeImageView = new ImageView(); - qrCodeImageView.setFitHeight(150); - qrCodeImageView.setFitWidth(150); - qrCodeImageView.getStyleClass().add("qr-code"); - Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow"))); - qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter( + Tuple2<StackPane, ImageView> qrCodeTuple = GUIUtil.getSmallXmrQrCodePane(); + qrCodePane = qrCodeTuple.first; + qrCodeImageView = qrCodeTuple.second; + + Tooltip.install(qrCodePane, new Tooltip(Res.get("shared.openLargeQRWindow"))); + qrCodePane.setOnMouseClicked(e -> UserThread.runAfter( () -> new QRCodeWindow(getPaymentUri()).show(), 200, TimeUnit.MILLISECONDS)); - GridPane.setRowIndex(qrCodeImageView, gridRow); - GridPane.setRowSpan(qrCodeImageView, 4); - GridPane.setColumnIndex(qrCodeImageView, 1); - GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 10)); - gridPane.getChildren().add(qrCodeImageView); + GridPane.setRowIndex(qrCodePane, gridRow); + GridPane.setRowSpan(qrCodePane, 4); + GridPane.setColumnIndex(qrCodePane, 1); + GridPane.setMargin(qrCodePane, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 10)); + gridPane.getChildren().add(qrCodePane); addressTextField = addAddressTextField(gridPane, ++gridRow, Res.get("shared.address"), Layout.FIRST_ROW_DISTANCE); addressTextField.setPaymentLabel(paymentLabelString); @@ -213,8 +216,8 @@ public class DepositView extends ActivatableView<VBox, Void> { titledGroupBg.setVisible(false); titledGroupBg.setManaged(false); - qrCodeImageView.setVisible(false); - qrCodeImageView.setManaged(false); + qrCodePane.setVisible(false); + qrCodePane.setManaged(false); addressTextField.setVisible(false); addressTextField.setManaged(false); amountTextField.setManaged(false); @@ -310,8 +313,8 @@ public class DepositView extends ActivatableView<VBox, Void> { private void fillForm(String address) { titledGroupBg.setVisible(true); titledGroupBg.setManaged(true); - qrCodeImageView.setVisible(true); - qrCodeImageView.setManaged(true); + qrCodePane.setVisible(true); + qrCodePane.setManaged(true); addressTextField.setVisible(true); addressTextField.setManaged(true); amountTextField.setManaged(true); @@ -366,10 +369,7 @@ public class DepositView extends ActivatableView<VBox, Void> { @NotNull private String getPaymentUri() { - return MoneroUtils.getPaymentUri(new MoneroTxConfig() - .setAddress(addressTextField.getAddress()) - .setAmount(HavenoUtils.coinToAtomicUnits(getAmount())) - .setNote(paymentLabelString)); + return GUIUtil.getMoneroURI(addressTextField.getAddress(), HavenoUtils.coinToAtomicUnits(getAmount()), paymentLabelString); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -377,7 +377,6 @@ public class DepositView extends ActivatableView<VBox, Void> { /////////////////////////////////////////////////////////////////////////////////////////// private void setUsageColumnCellFactory() { - usageColumn.getStyleClass().add("last-column"); usageColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); usageColumn.setCellFactory(new Callback<>() { @@ -390,7 +389,9 @@ public class DepositView extends ActivatableView<VBox, Void> { public void updateItem(final DepositListItem item, boolean empty) { super.updateItem(item, empty); if (item != null && !empty) { - setGraphic(new AutoTooltipLabel(item.getUsage())); + Label usageLabel = new AutoTooltipLabel(item.getUsage()); + usageLabel.getStyleClass().add("highlight-text"); + setGraphic(usageLabel); } else { setGraphic(null); } @@ -401,7 +402,6 @@ public class DepositView extends ActivatableView<VBox, Void> { } private void setAddressColumnCellFactory() { - addressColumn.getStyleClass().add("first-column"); addressColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); addressColumn.setCellFactory( @@ -434,6 +434,7 @@ public class DepositView extends ActivatableView<VBox, Void> { private void setBalanceColumnCellFactory() { balanceColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); + balanceColumn.getStyleClass().add("highlight-text"); balanceColumn.setCellFactory(new Callback<>() { @Override diff --git a/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java b/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java index 8cf5700cc6..5570ca1bef 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/locked/LockedView.java @@ -122,6 +122,7 @@ public class LockedView extends ActivatableView<VBox, Void> { addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); balanceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.balanceWithCur", Res.getBaseCurrencyCode()))); + GUIUtil.applyTableStyle(tableView); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.locked.noFunds"))); @@ -250,7 +251,6 @@ public class LockedView extends ActivatableView<VBox, Void> { /////////////////////////////////////////////////////////////////////////////////////////// private void setDateColumnCellFactory() { - dateColumn.getStyleClass().add("first-column"); dateColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); dateColumn.setCellFactory(new Callback<>() { @@ -342,7 +342,6 @@ public class LockedView extends ActivatableView<VBox, Void> { } private void setBalanceColumnCellFactory() { - balanceColumn.getStyleClass().add("last-column"); balanceColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); balanceColumn.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java b/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java index bcef7e6488..3ed2dfea85 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/reserved/ReservedView.java @@ -122,6 +122,7 @@ public class ReservedView extends ActivatableView<VBox, Void> { addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); balanceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.balanceWithCur", Res.getBaseCurrencyCode()))); + GUIUtil.applyTableStyle(tableView); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.reserved.noFunds"))); @@ -249,7 +250,6 @@ public class ReservedView extends ActivatableView<VBox, Void> { /////////////////////////////////////////////////////////////////////////////////////////// private void setDateColumnCellFactory() { - dateColumn.getStyleClass().add("first-column"); dateColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); dateColumn.setCellFactory(new Callback<>() { @@ -313,7 +313,6 @@ public class ReservedView extends ActivatableView<VBox, Void> { } private void setAddressColumnCellFactory() { - addressColumn.getStyleClass().add("last-column"); addressColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); addressColumn.setCellFactory( @@ -341,7 +340,6 @@ public class ReservedView extends ActivatableView<VBox, Void> { } private void setBalanceColumnCellFactory() { - balanceColumn.getStyleClass().add("last-column"); balanceColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); balanceColumn.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml index 24d8f121d7..3f8aa752fd 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml +++ b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml @@ -32,14 +32,14 @@ </padding> <TableView fx:id="tableView" VBox.vgrow="ALWAYS"> <columns> - <TableColumn fx:id="dateColumn" minWidth="180" maxWidth="180"/> - <TableColumn fx:id="detailsColumn" minWidth="220" maxWidth="220"/> - <TableColumn fx:id="addressColumn" minWidth="260"/> + <TableColumn fx:id="dateColumn" minWidth="100" maxWidth="180"/> + <TableColumn fx:id="detailsColumn" minWidth="180" maxWidth="220"/> + <TableColumn fx:id="addressColumn" minWidth="250"/> <TableColumn fx:id="transactionColumn" minWidth="180"/> <TableColumn fx:id="amountColumn" minWidth="110" maxWidth="110"/> <TableColumn fx:id="txFeeColumn" minWidth="110" maxWidth="110"/> - <TableColumn fx:id="confidenceColumn" minWidth="60" maxWidth="130"/> - <TableColumn fx:id="memoColumn" minWidth="40"/> + <TableColumn fx:id="confidenceColumn" minWidth="70" maxWidth="130"/> + <TableColumn fx:id="memoColumn" minWidth="40" maxWidth="250"/> <TableColumn fx:id="revertTxColumn" sortable="false" minWidth="110" maxWidth="110" visible="false"/> </columns> </TableView> diff --git a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java index b882782212..66434a8663 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java @@ -127,6 +127,8 @@ public class TransactionsView extends ActivatableView<VBox, Void> { @Override public void initialize() { + GUIUtil.applyTableStyle(tableView); + dateColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.dateTime"))); detailsColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.details"))); addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); @@ -139,6 +141,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> { tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN); tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.tx.noTxAvailable"))); + tableView.getStyleClass().add("non-interactive-table"); setDateColumnCellFactory(); setDetailsColumnCellFactory(); @@ -169,11 +172,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> { keyEventEventHandler = event -> { // Not intended to be public to users as the feature is not well tested if (Utilities.isAltOrCtrlPressed(KeyCode.R, event)) { - if (revertTxColumn.isVisible()) { - confidenceColumn.getStyleClass().remove("last-column"); - } else { - confidenceColumn.getStyleClass().add("last-column"); - } revertTxColumn.setVisible(!revertTxColumn.isVisible()); } }; @@ -265,7 +263,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> { /////////////////////////////////////////////////////////////////////////////////////////// private void setDateColumnCellFactory() { - dateColumn.getStyleClass().add("first-column"); dateColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); dateColumn.setMaxWidth(200); @@ -400,6 +397,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> { private void setAmountColumnCellFactory() { amountColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); + amountColumn.getStyleClass().add("highlight-text"); amountColumn.setCellFactory( new Callback<>() { @@ -427,6 +425,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> { private void setTxFeeColumnCellFactory() { txFeeColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); + txFeeColumn.getStyleClass().add("highlight-text"); txFeeColumn.setCellFactory( new Callback<>() { @@ -453,6 +452,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> { private void setMemoColumnCellFactory() { memoColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); + memoColumn.getStyleClass().add("highlight-text"); memoColumn.setCellFactory( new Callback<>() { @@ -477,7 +477,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> { } private void setConfidenceColumnCellFactory() { - confidenceColumn.getStyleClass().add("last-column"); confidenceColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); confidenceColumn.setCellFactory( @@ -504,7 +503,6 @@ public class TransactionsView extends ActivatableView<VBox, Void> { } private void setRevertTxColumnCellFactory() { - revertTxColumn.getStyleClass().add("last-column"); revertTxColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); revertTxColumn.setCellFactory( diff --git a/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java b/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java index 730b47adf6..6217bb1e04 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java @@ -144,7 +144,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> { amountLabel = feeTuple3.first; amountTextField = feeTuple3.second; - amountTextField.setMinWidth(180); + amountTextField.setMinWidth(200); HyperlinkWithIcon sendMaxLink = feeTuple3.third; withdrawMemoTextField = addTopLabelInputTextField(gridPane, ++rowIndex, diff --git a/desktop/src/main/java/haveno/desktop/main/market/MarketView.java b/desktop/src/main/java/haveno/desktop/main/market/MarketView.java index 98f5e75490..fc72de153a 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/MarketView.java +++ b/desktop/src/main/java/haveno/desktop/main/market/MarketView.java @@ -88,10 +88,10 @@ public class MarketView extends ActivatableView<TabPane, Void> { @Override public void initialize() { - offerBookTab.setText(Res.get("market.tabs.offerBook").toUpperCase()); - spreadTab.setText(Res.get("market.tabs.spreadCurrency").toUpperCase()); - spreadTabPaymentMethod.setText(Res.get("market.tabs.spreadPayment").toUpperCase()); - tradesTab.setText(Res.get("market.tabs.trades").toUpperCase()); + offerBookTab.setText(Res.get("market.tabs.offerBook")); + spreadTab.setText(Res.get("market.tabs.spreadCurrency")); + spreadTabPaymentMethod.setText(Res.get("market.tabs.spreadPayment")); + tradesTab.setText(Res.get("market.tabs.trades")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(MarketView.class) == 1) diff --git a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java index 3d2ec6d884..e89c78dac9 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java +++ b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java @@ -26,6 +26,7 @@ import haveno.common.util.Tuple3; import haveno.common.util.Tuple4; import haveno.core.locale.CurrencyUtil; import haveno.core.locale.Res; +import haveno.core.monetary.Volume; import haveno.core.offer.Offer; import haveno.core.offer.OfferDirection; import haveno.core.util.FormattingUtils; @@ -65,13 +66,13 @@ import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; +import javafx.scene.control.ContentDisplay; import javafx.scene.control.Label; import javafx.scene.control.SingleSelectionModel; import javafx.scene.control.Tab; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; -import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; @@ -95,7 +96,10 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC private AnchorPane chartPane; private AutocompleteComboBox<CurrencyListItem> currencyComboBox; private Subscription tradeCurrencySubscriber; - private final StringProperty volumeColumnLabel = new SimpleStringProperty(); + private final StringProperty volumeSellColumnLabel = new SimpleStringProperty(); + private final StringProperty volumeBuyColumnLabel = new SimpleStringProperty(); + private final StringProperty amountSellColumnLabel = new SimpleStringProperty(); + private final StringProperty amountBuyColumnLabel = new SimpleStringProperty(); private final StringProperty priceColumnLabel = new SimpleStringProperty(); private AutoTooltipButton sellButton; private AutoTooltipButton buyButton; @@ -106,10 +110,11 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC private ListChangeListener<OfferBookListItem> changeListener; private ListChangeListener<CurrencyListItem> currencyListItemsListener; private final double dataLimitFactor = 3; - private final double initialOfferTableViewHeight = 121; + private final double initialOfferTableViewHeight = 78; // decrease as MainView's content-pane's top anchor increases + private final double offerTableExtraMarginBottom = 0; private final Function<Double, Double> offerTableViewHeight = (screenSize) -> { // initial visible row count=5, header height=30 - double pixelsPerOfferTableRow = (initialOfferTableViewHeight - 30) / 5.0; + double pixelsPerOfferTableRow = (initialOfferTableViewHeight - offerTableExtraMarginBottom) / 5.0; int extraRows = screenSize <= INITIAL_WINDOW_HEIGHT ? 0 : (int) ((screenSize - INITIAL_WINDOW_HEIGHT) / pixelsPerOfferTableRow); return extraRows == 0 ? initialOfferTableViewHeight : Math.ceil(initialOfferTableViewHeight + ((extraRows + 1) * pixelsPerOfferTableRow)); }; @@ -136,6 +141,7 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC this.currencyComboBox = currencyComboBoxTuple.third; this.currencyComboBox.setCellFactory(GUIUtil.getCurrencyListItemCellFactory(Res.get("shared.oneOffer"), Res.get("shared.multipleOffers"), model.preferences)); + this.currencyComboBox.getStyleClass().add("input-with-border"); createChart(); @@ -200,7 +206,6 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC tradeCurrencySubscriber = EasyBind.subscribe(model.selectedTradeCurrencyProperty, tradeCurrency -> { String code = tradeCurrency.getCode(); - volumeColumnLabel.set(Res.get("offerbook.volume", code)); xAxis.setTickLabelFormatter(new StringConverter<>() { final int cryptoPrecision = 3; final DecimalFormat df = new DecimalFormat(",###"); @@ -230,15 +235,21 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC return null; } }); - - String viewBaseCurrencyCode = CurrencyUtil.isCryptoCurrency(code) ? code : Res.getBaseCurrencyCode(); - String viewPriceCurrencyCode = CurrencyUtil.isCryptoCurrency(code) ? Res.getBaseCurrencyCode() : code; + + String viewBaseCurrencyCode = model.isCrypto() ? code : Res.getBaseCurrencyCode(); + String viewPriceCurrencyCode = model.isCrypto() ? Res.getBaseCurrencyCode() : code; sellHeaderLabel.setText(Res.get("market.offerBook.sellOffersHeaderLabel", viewBaseCurrencyCode)); - sellButton.updateText(Res.get("shared.sellCurrency", viewBaseCurrencyCode, viewPriceCurrencyCode)); + sellButton.updateText(Res.get("shared.sellCurrency", viewBaseCurrencyCode)); + sellButton.setGraphic(GUIUtil.getCurrencyIconWithBorder(viewBaseCurrencyCode)); + sellButton.setOnAction(e -> model.goToOfferView(model.isCrypto() ? OfferDirection.SELL : OfferDirection.BUY)); + sellButton.setId("sell-button-big"); buyHeaderLabel.setText(Res.get("market.offerBook.buyOffersHeaderLabel", viewBaseCurrencyCode)); - buyButton.updateText(Res.get("shared.buyCurrency", viewBaseCurrencyCode, viewPriceCurrencyCode)); + buyButton.updateText(Res.get( "shared.buyCurrency", viewBaseCurrencyCode)); + buyButton.setGraphic(GUIUtil.getCurrencyIconWithBorder(viewBaseCurrencyCode)); + buyButton.setOnAction(e -> model.goToOfferView(model.isCrypto() ? OfferDirection.BUY : OfferDirection.SELL)); + buyButton.setId("buy-button-big"); priceColumnLabel.set(Res.get("shared.priceWithCur", viewPriceCurrencyCode)); @@ -288,8 +299,8 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC currencyComboBox.getSelectionModel().select(model.getSelectedCurrencyListItem().get()); }; - buyTableRowSelectionListener = (observable, oldValue, newValue) -> model.goToOfferView(OfferDirection.BUY); sellTableRowSelectionListener = (observable, oldValue, newValue) -> model.goToOfferView(OfferDirection.SELL); + buyTableRowSelectionListener = (observable, oldValue, newValue) -> model.goToOfferView(OfferDirection.BUY); havenoWindowVerticalSizeListener = (observable, oldValue, newValue) -> layout(); } @@ -345,12 +356,27 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC } private synchronized void updateChartData() { - seriesBuy.getData().clear(); + + // update volume headers + Volume volumeSell = model.getTotalVolume(model.isCrypto() ? OfferDirection.BUY : OfferDirection.SELL); + Volume volumeBuy = model.getTotalVolume(model.isCrypto() ? OfferDirection.SELL : OfferDirection.BUY); + String formattedVolumeSell = volumeSell == null ? null : VolumeUtil.formatVolume(volumeSell); + String formattedVolumeBuy = volumeBuy == null ? null : VolumeUtil.formatVolume(volumeBuy); + if (model.getSellData().isEmpty()) formattedVolumeSell = "0.0"; + if (model.getBuyData().isEmpty()) formattedVolumeBuy = "0.0"; + volumeSellColumnLabel.set(Res.get("offerbook.volumeTotal", model.getCurrencyCode(), formattedVolumeSell == null ? "" : "(" + formattedVolumeSell + ")")); + volumeBuyColumnLabel.set(Res.get("offerbook.volumeTotal", model.getCurrencyCode(), formattedVolumeBuy == null ? "" : "(" + formattedVolumeBuy + ")")); + + // update amount headers + amountSellColumnLabel.set(Res.get("offerbook.XMRTotal", "" + model.getTotalAmount(model.isCrypto() ? OfferDirection.BUY : OfferDirection.SELL))); + amountBuyColumnLabel.set(Res.get("offerbook.XMRTotal", "" + model.getTotalAmount(model.isCrypto() ? OfferDirection.SELL : OfferDirection.BUY))); + seriesSell.getData().clear(); + seriesBuy.getData().clear(); areaChart.getData().clear(); - seriesBuy.getData().addAll(filterOutliersBuy(model.getBuyData())); seriesSell.getData().addAll(filterOutliersSell(model.getSellData())); + seriesBuy.getData().addAll(filterOutliersBuy(model.getBuyData())); areaChart.getData().addAll(List.of(seriesBuy, seriesSell)); } @@ -426,10 +452,11 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC private Tuple4<TableView<OfferListItem>, VBox, Button, Label> getOfferTable(OfferDirection direction) { TableView<OfferListItem> tableView = new TableView<>(); + GUIUtil.applyTableStyle(tableView, false); tableView.setMinHeight(initialOfferTableViewHeight); tableView.setPrefHeight(initialOfferTableViewHeight); tableView.setMinWidth(480); - tableView.getStyleClass().add("offer-table"); + tableView.getStyleClass().addAll("offer-table", "non-interactive-table"); // price TableColumn<OfferListItem, OfferListItem> priceColumn = new TableColumn<>(); @@ -484,12 +511,14 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC } }); + boolean isSellTable = model.isSellOffer(direction); + // volume TableColumn<OfferListItem, OfferListItem> volumeColumn = new TableColumn<>(); volumeColumn.setMinWidth(115); volumeColumn.setSortable(false); - volumeColumn.textProperty().bind(volumeColumnLabel); - volumeColumn.getStyleClass().addAll("number-column", "first-column"); + volumeColumn.textProperty().bind(isSellTable ? volumeSellColumnLabel : volumeBuyColumnLabel); + volumeColumn.getStyleClass().addAll("number-column"); volumeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); volumeColumn.setCellFactory( new Callback<>() { @@ -546,7 +575,8 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC }); // amount - TableColumn<OfferListItem, OfferListItem> amountColumn = new AutoTooltipTableColumn<>(Res.get("shared.XMRMinMax")); + TableColumn<OfferListItem, OfferListItem> amountColumn = new TableColumn<>(); + amountColumn.textProperty().bind(isSellTable ? amountSellColumnLabel : amountBuyColumnLabel); amountColumn.setMinWidth(115); amountColumn.setSortable(false); amountColumn.getStyleClass().add("number-column"); @@ -570,10 +600,8 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC } }); - boolean isSellOffer = model.isSellOffer(direction); - // trader avatar - TableColumn<OfferListItem, OfferListItem> avatarColumn = new AutoTooltipTableColumn<>(isSellOffer ? + TableColumn<OfferListItem, OfferListItem> avatarColumn = new AutoTooltipTableColumn<>(isSellTable ? Res.get("shared.sellerUpperCase") : Res.get("shared.buyerUpperCase")) { { setMinWidth(80); @@ -582,7 +610,7 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC } }; - avatarColumn.getStyleClass().addAll("last-column", "avatar-column"); + avatarColumn.getStyleClass().addAll("avatar-column"); avatarColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); avatarColumn.setCellFactory( new Callback<>() { @@ -629,20 +657,16 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC tableView.setPlaceholder(placeholder); HBox titleButtonBox = new HBox(); + titleButtonBox.getStyleClass().add("offer-table-top"); titleButtonBox.setAlignment(Pos.CENTER); Label titleLabel = new AutoTooltipLabel(); titleLabel.getStyleClass().add("table-title"); AutoTooltipButton button = new AutoTooltipButton(); - ImageView iconView = new ImageView(); - iconView.setId(isSellOffer ? "image-buy-white" : "image-sell-white"); - button.setGraphic(iconView); + button.setContentDisplay(ContentDisplay.RIGHT); button.setGraphicTextGap(10); - button.updateText(isSellOffer ? Res.get("market.offerBook.buy") : Res.get("market.offerBook.sell")); button.setMinHeight(32); - button.setId(isSellOffer ? "buy-button-big" : "sell-button-big"); - button.setOnAction(e -> model.goToOfferView(direction)); Region spacer = new Region(); @@ -653,9 +677,9 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC VBox vBox = new VBox(); VBox.setVgrow(tableView, Priority.ALWAYS); vBox.setPadding(new Insets(0, 0, 0, 0)); - vBox.setSpacing(10); + vBox.setSpacing(0); vBox.setFillWidth(true); - vBox.setMinHeight(190); + //vBox.setMinHeight(190); vBox.getChildren().addAll(titleButtonBox, tableView); return new Tuple4<>(tableView, vBox, button, titleLabel); diff --git a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java index c7d0c91277..3e8b52184a 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java @@ -26,6 +26,7 @@ import haveno.core.locale.CurrencyUtil; import haveno.core.locale.GlobalSettings; import haveno.core.locale.TradeCurrency; import haveno.core.monetary.Price; +import haveno.core.monetary.Volume; import haveno.core.offer.Offer; import haveno.core.offer.OfferDirection; import haveno.core.offer.OpenOfferManager; @@ -58,6 +59,7 @@ import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.scene.chart.XYChart; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -212,10 +214,42 @@ class OfferBookChartViewModel extends ActivatableViewModel { } public boolean isSellOffer(OfferDirection direction) { - // for cryptocurrency, buy direction is to buy XMR, so we need sell offers - // for traditional currency, buy direction is to sell XMR, so we need buy offers - boolean isCryptoCurrency = CurrencyUtil.isCryptoCurrency(getCurrencyCode()); - return isCryptoCurrency ? direction == OfferDirection.BUY : direction == OfferDirection.SELL; + return direction == OfferDirection.SELL; + } + + public double getTotalAmount(OfferDirection direction) { + synchronized (offerBookListItems) { + List<Offer> offerList = offerBookListItems.stream() + .map(OfferBookListItem::getOffer) + .filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode()) + && e.getDirection().equals(direction)) + .collect(Collectors.toList()); + BigInteger sum = BigInteger.ZERO; + for (Offer offer : offerList) sum = sum.add(offer.getAmount()); + return HavenoUtils.atomicUnitsToXmr(sum); + } + } + + public Volume getTotalVolume(OfferDirection direction) { + synchronized (offerBookListItems) { + List<Volume> volumes = offerBookListItems.stream() + .map(OfferBookListItem::getOffer) + .filter(e -> e.getCurrencyCode().equals(selectedTradeCurrencyProperty.get().getCode()) + && e.getDirection().equals(direction)) + .map(Offer::getVolume) + .collect(Collectors.toList()); + try { + return VolumeUtil.sum(volumes); + } catch (Exception e) { + // log.error("Cannot compute total volume because prices are unavailable, currency={}, direction={}", + // selectedTradeCurrencyProperty.get().getCode(), direction); + return null; // expected before prices are available + } + } + } + + public boolean isCrypto() { + return CurrencyUtil.isCryptoCurrency(getCurrencyCode()); } public boolean isMyOffer(Offer offer) { diff --git a/desktop/src/main/java/haveno/desktop/main/market/spread/SpreadView.java b/desktop/src/main/java/haveno/desktop/main/market/spread/SpreadView.java index 9adf595ad4..150b7c888a 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/spread/SpreadView.java +++ b/desktop/src/main/java/haveno/desktop/main/market/spread/SpreadView.java @@ -65,6 +65,8 @@ public class SpreadView extends ActivatableViewAndModel<GridPane, SpreadViewMode @Override public void initialize() { tableView = new TableView<>(); + GUIUtil.applyTableStyle(tableView); + tableView.getStyleClass().add("non-interactive-table"); int gridRow = 0; GridPane.setRowIndex(tableView, gridRow); @@ -144,7 +146,7 @@ public class SpreadView extends ActivatableViewAndModel<GridPane, SpreadViewMode setMinWidth(160); } }; - column.getStyleClass().addAll("number-column", "first-column"); + column.getStyleClass().addAll("number-column"); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( new Callback<>() { @@ -259,7 +261,7 @@ public class SpreadView extends ActivatableViewAndModel<GridPane, SpreadViewMode setMinWidth(140); } }; - column.getStyleClass().add("number-column"); + column.getStyleClass().addAll("number-column", "highlight-text"); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( new Callback<>() { @@ -289,7 +291,7 @@ public class SpreadView extends ActivatableViewAndModel<GridPane, SpreadViewMode setMinWidth(110); } }; - column.getStyleClass().addAll("number-column", "last-column"); + column.getStyleClass().addAll("number-column", "highlight-text"); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java b/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java index 97fcce06d1..7470cf1c4b 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java +++ b/desktop/src/main/java/haveno/desktop/main/market/trades/TradesChartsView.java @@ -686,6 +686,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts currencyComboBox = currencyComboBoxTuple.third; currencyComboBox.setCellFactory(GUIUtil.getCurrencyListItemCellFactory(Res.get("shared.trade"), Res.get("shared.trades"), model.preferences)); + currencyComboBox.getStyleClass().add("input-with-border"); Pane spacer = new Pane(); HBox.setHgrow(spacer, Priority.ALWAYS); @@ -730,7 +731,9 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts private void createTable() { tableView = new TableView<>(); + GUIUtil.applyTableStyle(tableView); VBox.setVgrow(tableView, Priority.ALWAYS); + tableView.getStyleClass().add("non-interactive-table"); // date TableColumn<TradeStatistics3ListItem, TradeStatistics3ListItem> dateColumn = new AutoTooltipTableColumn<>(Res.get("shared.dateTime")) { @@ -739,7 +742,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts setMaxWidth(240); } }; - dateColumn.getStyleClass().addAll("number-column", "first-column"); + dateColumn.getStyleClass().addAll("number-column"); dateColumn.setCellValueFactory((tradeStatistics) -> new ReadOnlyObjectWrapper<>(tradeStatistics.getValue())); dateColumn.setCellFactory( new Callback<>() { @@ -865,7 +868,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts // paymentMethod TableColumn<TradeStatistics3ListItem, TradeStatistics3ListItem> paymentMethodColumn = new AutoTooltipTableColumn<>(Res.get("shared.paymentMethod")); - paymentMethodColumn.getStyleClass().add("number-column"); + paymentMethodColumn.getStyleClass().addAll("number-column"); paymentMethodColumn.setCellValueFactory((tradeStatistics) -> new ReadOnlyObjectWrapper<>(tradeStatistics.getValue())); paymentMethodColumn.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java index e28ecff72b..a580d24802 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferView.java @@ -85,6 +85,7 @@ import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import javafx.util.StringConverter; @@ -149,7 +150,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten private ComboBox<PaymentAccount> paymentAccountsComboBox; private ComboBox<TradeCurrency> currencyComboBox; private ImageView qrCodeImageView; - private VBox currencySelection, fixedPriceBox, percentagePriceBox, currencyTextFieldBox, triggerPriceVBox; + private StackPane qrCodePane; + private VBox paymentAccountsSelection, currencySelection, fixedPriceBox, percentagePriceBox, currencyTextFieldBox, triggerPriceVBox; private HBox fundingHBox, firstRowHBox, secondRowHBox, placeOfferBox, amountValueCurrencyBox, priceAsPercentageValueCurrencyBox, volumeValueCurrencyBox, priceValueCurrencyBox, minAmountValueCurrencyBox, securityDepositAndFeeBox, triggerPriceHBox; @@ -308,7 +310,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten if (!result) { new Popup().headLine(Res.get("popup.warning.noTradingAccountSetup.headline")) .instruction(Res.get("popup.warning.noTradingAccountSetup.msg")) - .actionButtonTextWithGoTo("navigation.account") + .actionButtonTextWithGoTo("mainView.menu.account") .onAction(() -> { navigation.setReturnPath(navigation.getCurrentPath()); navigation.navigateTo(MainView.class, AccountView.class, TraditionalAccountsView.class); @@ -425,7 +427,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten totalToPayTextField.setContentForInfoPopOver(createInfoPopover()); }); - paymentAccountsComboBox.setDisable(true); + paymentAccountsSelection.setDisable(true); + currencySelection.setDisable(true); editOfferElements.forEach(node -> { node.setMouseTransparent(true); @@ -449,8 +452,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten private void updateOfferElementsStyle() { GridPane.setColumnSpan(firstRowHBox, 2); - String activeInputStyle = "input-with-border"; - String readOnlyInputStyle = "input-with-border-readonly"; + String activeInputStyle = "offer-input"; + String readOnlyInputStyle = "offer-input-readonly"; amountValueCurrencyBox.getStyleClass().remove(activeInputStyle); amountValueCurrencyBox.getStyleClass().add(readOnlyInputStyle); priceAsPercentageValueCurrencyBox.getStyleClass().remove(activeInputStyle); @@ -709,7 +712,11 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten }; extraInfoFocusedListener = (observable, oldValue, newValue) -> { model.onFocusOutExtraInfoTextArea(oldValue, newValue); - extraInfoTextArea.setText(model.extraInfo.get()); + + // avoid setting text area to empty text because blinking caret does not appear + if (model.extraInfo.get() != null && !model.extraInfo.get().isEmpty()) { + extraInfoTextArea.setText(model.extraInfo.get()); + } }; errorMessageListener = (o, oldValue, newValue) -> { @@ -749,7 +756,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten UserThread.runAfter(() -> new Popup().headLine(Res.get("createOffer.success.headline")) .feedback(Res.get("createOffer.success.info")) .dontShowAgainId(key) - .actionButtonTextWithGoTo("navigation.portfolio.myOpenOffers") + .actionButtonTextWithGoTo("portfolio.tab.openOffers") .onAction(this::closeAndGoToOpenOffers) .onClose(this::closeAndGoToOpenOffers) .show(), @@ -973,7 +980,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten private void addGridPane() { gridPane = new GridPane(); gridPane.getStyleClass().add("content-pane"); - gridPane.setPadding(new Insets(25, 25, -1, 25)); + gridPane.setPadding(new Insets(25, 25, 25, 25)); gridPane.setHgap(5); gridPane.setVgap(5); GUIUtil.setDefaultTwoColumnConstraintsForGridPane(gridPane); @@ -995,8 +1002,9 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten final Tuple3<VBox, Label, ComboBox<TradeCurrency>> currencyBoxTuple = addTopLabelComboBox( Res.get("shared.currency"), Res.get("list.currency.select")); + paymentAccountsSelection = tradingAccountBoxTuple.first; currencySelection = currencyBoxTuple.first; - paymentGroupBox.getChildren().addAll(tradingAccountBoxTuple.first, currencySelection); + paymentGroupBox.getChildren().addAll(paymentAccountsSelection, currencySelection); GridPane.setRowIndex(paymentGroupBox, gridRow); GridPane.setColumnSpan(paymentGroupBox, 2); @@ -1007,11 +1015,13 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten paymentAccountsComboBox = tradingAccountBoxTuple.third; paymentAccountsComboBox.setMinWidth(tradingAccountBoxTuple.first.getMinWidth()); paymentAccountsComboBox.setPrefWidth(tradingAccountBoxTuple.first.getMinWidth()); - editOfferElements.add(tradingAccountBoxTuple.first); + paymentAccountsComboBox.getStyleClass().add("input-with-border"); + editOfferElements.add(paymentAccountsSelection); // we display either currencyComboBox (multi currency account) or currencyTextField (single) currencyComboBox = currencyBoxTuple.third; currencyComboBox.setMaxWidth(tradingAccountBoxTuple.first.getMinWidth() / 2); + currencyComboBox.getStyleClass().add("input-with-border"); editOfferElements.add(currencySelection); currencyComboBox.setConverter(new StringConverter<>() { @Override @@ -1094,6 +1104,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten GridPane.setColumnSpan(extraInfoTitledGroupBg, 3); extraInfoTextArea = new InputTextArea(); + extraInfoTextArea.setText(""); extraInfoTextArea.setPromptText(Res.get("payment.shared.extraInfo.prompt.offer")); extraInfoTextArea.getStyleClass().add("text-area"); extraInfoTextArea.setWrapText(true); @@ -1179,8 +1190,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten totalToPayTextField.setManaged(false); addressTextField.setVisible(false); addressTextField.setManaged(false); - qrCodeImageView.setVisible(false); - qrCodeImageView.setManaged(false); + qrCodePane.setVisible(false); + qrCodePane.setManaged(false); balanceTextField.setVisible(false); balanceTextField.setManaged(false); cancelButton2.setVisible(false); @@ -1196,8 +1207,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten totalToPayTextField.setManaged(true); addressTextField.setVisible(true); addressTextField.setManaged(true); - qrCodeImageView.setVisible(true); - qrCodeImageView.setManaged(true); + qrCodePane.setVisible(true); + qrCodePane.setManaged(true); balanceTextField.setVisible(true); balanceTextField.setManaged(true); cancelButton2.setVisible(true); @@ -1239,21 +1250,23 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten totalToPayTextField.setVisible(false); GridPane.setMargin(totalToPayTextField, new Insets(60 + heightAdjustment, 10, 0, 0)); - qrCodeImageView = new ImageView(); - qrCodeImageView.setVisible(false); - qrCodeImageView.setFitHeight(150); - qrCodeImageView.setFitWidth(150); - qrCodeImageView.getStyleClass().add("qr-code"); - Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow"))); - qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter( + Tuple2<StackPane, ImageView> qrCodeTuple = GUIUtil.getSmallXmrQrCodePane(); + qrCodePane = qrCodeTuple.first; + qrCodeImageView = qrCodeTuple.second; + + Tooltip.install(qrCodePane, new Tooltip(Res.get("shared.openLargeQRWindow"))); + qrCodePane.setOnMouseClicked(e -> UserThread.runAfter( () -> new QRCodeWindow(getMoneroURI()).show(), 200, TimeUnit.MILLISECONDS)); - GridPane.setRowIndex(qrCodeImageView, gridRow); - GridPane.setColumnIndex(qrCodeImageView, 1); - GridPane.setRowSpan(qrCodeImageView, 3); - GridPane.setValignment(qrCodeImageView, VPos.BOTTOM); - GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10)); - gridPane.getChildren().add(qrCodeImageView); + GridPane.setRowIndex(qrCodePane, gridRow); + GridPane.setColumnIndex(qrCodePane, 1); + GridPane.setRowSpan(qrCodePane, 3); + GridPane.setValignment(qrCodePane, VPos.BOTTOM); + GridPane.setMargin(qrCodePane, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10)); + gridPane.getChildren().add(qrCodePane); + + qrCodePane.setVisible(false); + qrCodePane.setManaged(false); addressTextField = addAddressTextField(gridPane, ++gridRow, Res.get("shared.tradeWalletAddress")); diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java index 821f9e8a5c..c29781c3bf 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java @@ -504,7 +504,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext extraInfoStringListener = (ov, oldValue, newValue) -> { if (newValue != null) { - extraInfo.set(newValue); + extraInfo.set(newValue.trim()); UserThread.execute(() -> onExtraInfoTextAreaChanged()); } }; @@ -727,7 +727,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext new Popup().warning(Res.get("shared.notEnoughFunds", HavenoUtils.formatXmr(dataModel.totalToPayAsProperty().get(), true), HavenoUtils.formatXmr(dataModel.getTotalBalance(), true))) - .actionButtonTextWithGoTo("navigation.funds.depositFunds") + .actionButtonTextWithGoTo("funds.tab.deposit") .onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, DepositView.class)) .show(); } @@ -1056,7 +1056,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext FormattingUtils.formatToPercentWithSymbol(preferences.getMaxPriceDistanceInPercent()))) .actionButtonText(Res.get("createOffer.changePrice")) .onAction(popup::hide) - .closeButtonTextWithGoTo("navigation.settings.preferences") + .closeButtonTextWithGoTo("settings.tab.preferences") .onClose(() -> navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class)) .show(); } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java index 6f6aba7cbc..dd037593fa 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java @@ -220,14 +220,14 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> { labelTab.setClosable(false); Label offerLabel = new Label(getOfferLabel()); // use overlay for label for custom formatting offerLabel.getStyleClass().add("titled-group-bg-label"); - offerLabel.setStyle("-fx-font-size: 1.4em;"); + offerLabel.setStyle("-fx-font-size: 1.3em;"); labelTab.setGraphic(offerLabel); - fiatOfferBookTab = new Tab(Res.get("shared.fiat").toUpperCase()); + fiatOfferBookTab = new Tab(Res.get("shared.fiat")); fiatOfferBookTab.setClosable(false); - cryptoOfferBookTab = new Tab(Res.get("shared.crypto").toUpperCase()); + cryptoOfferBookTab = new Tab(Res.get("shared.crypto")); cryptoOfferBookTab.setClosable(false); - otherOfferBookTab = new Tab(Res.get("shared.other").toUpperCase()); + otherOfferBookTab = new Tab(Res.get("shared.other")); otherOfferBookTab.setClosable(false); tabPane.getTabs().addAll(labelTab, fiatOfferBookTab, cryptoOfferBookTab, otherOfferBookTab); } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java index 0248b9c522..e3ed68ccf1 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java @@ -91,7 +91,6 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; import javafx.util.Callback; @@ -179,29 +178,29 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM currencyComboBoxContainer = currencyBoxTuple.first; currencyComboBox = currencyBoxTuple.third; currencyComboBox.setPrefWidth(250); + currencyComboBox.getStyleClass().add("input-with-border"); Tuple3<VBox, Label, AutocompleteComboBox<PaymentMethod>> paymentBoxTuple = FormBuilder.addTopLabelAutocompleteComboBox( Res.get("offerbook.filterByPaymentMethod")); paymentMethodComboBox = paymentBoxTuple.third; paymentMethodComboBox.setCellFactory(GUIUtil.getPaymentMethodCellFactory()); paymentMethodComboBox.setPrefWidth(250); - - matchingOffersToggleButton = AwesomeDude.createIconToggleButton(AwesomeIcon.USER, null, "1.5em", null); - matchingOffersToggleButton.getStyleClass().add("toggle-button-no-slider"); - matchingOffersToggleButton.setPrefHeight(27); - Tooltip matchingOffersTooltip = new Tooltip(Res.get("offerbook.matchingOffers")); - Tooltip.install(matchingOffersToggleButton, matchingOffersTooltip); + paymentMethodComboBox.getStyleClass().add("input-with-border"); noDepositOffersToggleButton = new ToggleButton(Res.get("offerbook.filterNoDeposit")); noDepositOffersToggleButton.getStyleClass().add("toggle-button-no-slider"); - noDepositOffersToggleButton.setPrefHeight(27); Tooltip noDepositOffersTooltip = new Tooltip(Res.get("offerbook.noDepositOffers")); Tooltip.install(noDepositOffersToggleButton, noDepositOffersTooltip); + matchingOffersToggleButton = AwesomeDude.createIconToggleButton(AwesomeIcon.USER, null, "1.5em", null); + matchingOffersToggleButton.getStyleClass().add("toggle-button-no-slider"); + Tooltip matchingOffersTooltip = new Tooltip(Res.get("offerbook.matchingOffers")); + Tooltip.install(matchingOffersToggleButton, matchingOffersTooltip); + createOfferButton = new AutoTooltipButton(""); createOfferButton.setMinHeight(40); createOfferButton.setGraphicTextGap(10); - createOfferButton.setStyle("-fx-padding: 0 15 0 15;"); + createOfferButton.setStyle("-fx-padding: 7 25 7 25;"); disabledCreateOfferButtonTooltip = new Label(""); disabledCreateOfferButtonTooltip.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); disabledCreateOfferButtonTooltip.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); @@ -211,15 +210,17 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM disabledCreateOfferButtonTooltip.setManaged(false); disabledCreateOfferButtonTooltip.setVisible(false); - var createOfferButtonStack = new StackPane(createOfferButton, disabledCreateOfferButtonTooltip); + var createOfferVBox = new VBox(createOfferButton, disabledCreateOfferButtonTooltip); + createOfferVBox.setAlignment(Pos.BOTTOM_RIGHT); Tuple3<VBox, Label, AutoTooltipTextField> autoToolTipTextField = addTopLabelAutoToolTipTextField(""); VBox filterBox = autoToolTipTextField.first; filterInputField = autoToolTipTextField.third; - filterInputField.setPromptText(Res.get("market.offerBook.filterPrompt")); + filterInputField.setPromptText(Res.get("shared.filter")); + filterInputField.getStyleClass().add("input-with-border"); offerToolsBox.getChildren().addAll(currencyBoxTuple.first, paymentBoxTuple.first, - filterBox, matchingOffersToggleButton, noDepositOffersToggleButton, getSpacer(), createOfferButtonStack); + filterBox, noDepositOffersToggleButton, matchingOffersToggleButton, getSpacer(), createOfferVBox); GridPane.setHgrow(offerToolsBox, Priority.ALWAYS); GridPane.setRowIndex(offerToolsBox, gridRow); @@ -228,6 +229,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM root.getChildren().add(offerToolsBox); tableView = new TableView<>(); + GUIUtil.applyTableStyle(tableView); GridPane.setRowIndex(tableView, ++gridRow); GridPane.setColumnIndex(tableView, 0); @@ -405,14 +407,12 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM if (showAll) { volumeColumn.setTitleWithHelpText(Res.get("shared.amountMinMax"), Res.get("shared.amountHelp")); priceColumn.setTitle(Res.get("shared.price")); - priceColumn.getStyleClass().remove("first-column"); if (!tableView.getColumns().contains(marketColumn)) tableView.getColumns().add(0, marketColumn); } else { volumeColumn.setTitleWithHelpText(Res.get("offerbook.volume", code), Res.get("shared.amountHelp")); priceColumn.setTitle(CurrencyUtil.getPriceWithCurrencyCode(code)); - priceColumn.getStyleClass().add("first-column"); tableView.getColumns().remove(marketColumn); } @@ -585,9 +585,8 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM public void setDirection(OfferDirection direction) { model.initWithDirection(direction); - ImageView iconView = new ImageView(); - createOfferButton.setGraphic(iconView); - iconView.setId(direction == OfferDirection.SELL ? "image-sell-white" : "image-buy-white"); + createOfferButton.setGraphic(GUIUtil.getCurrencyIconWithBorder(Res.getBaseCurrencyCode())); + createOfferButton.setContentDisplay(ContentDisplay.RIGHT); createOfferButton.setId(direction == OfferDirection.SELL ? "sell-button-big" : "buy-button-big"); avatarColumn.setTitle(direction == OfferDirection.SELL ? Res.get("shared.buyerUpperCase") : Res.get("shared.sellerUpperCase")); if (direction == OfferDirection.SELL) { @@ -750,8 +749,8 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM () -> { log.debug(Res.get("offerbook.removeOffer.success")); if (DontShowAgainLookup.showAgain(key)) - new Popup().instruction(Res.get("offerbook.withdrawFundsHint", Res.get("navigation.funds.availableForWithdrawal"))) - .actionButtonTextWithGoTo("navigation.funds.availableForWithdrawal") + new Popup().instruction(Res.get("offerbook.withdrawFundsHint", Res.get("funds.tab.withdrawal"))) + .actionButtonTextWithGoTo("funds.tab.withdrawal") .onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class)) .dontShowAgainId(key) .show(); @@ -769,7 +768,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM new Popup().headLine(headline) .instruction(Res.get("offerbook.warning.noMatchingAccount.msg")) - .actionButtonTextWithGoTo("navigation.account") + .actionButtonTextWithGoTo("mainView.menu.account") .onAction(() -> { navigation.setReturnPath(navigation.getCurrentPath()); navigation.navigateTo(MainView.class, AccountView.class, accountViewClass); @@ -812,7 +811,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM setMinWidth(40); } }; - column.getStyleClass().addAll("number-column", "first-column"); + column.getStyleClass().addAll("number-column"); column.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); column.setCellFactory( new Callback<>() { @@ -1065,7 +1064,6 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM setSortable(false); } }; - column.getStyleClass().addAll("last-column", "avatar-column"); column.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); column.setCellFactory( new Callback<>() { @@ -1116,7 +1114,12 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM // https://github.com/bisq-network/bisq/issues/4986 if (tableRow != null) { canTakeOfferResult = model.offerFilterService.canTakeOffer(offer, false); - tableRow.setOpacity(canTakeOfferResult.isValid() || myOffer ? 1 : 0.4); + if (canTakeOfferResult.isValid() || myOffer) { + tableRow.getStyleClass().remove("row-faded"); + } else { + if (!tableRow.getStyleClass().contains("row-faded")) tableRow.getStyleClass().add("row-faded"); + hbox.getStyleClass().add("cell-faded"); + } if (myOffer) { button.setDefaultButton(false); @@ -1149,12 +1152,16 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM } else { boolean isSellOffer = OfferViewUtil.isShownAsSellOffer(offer); boolean isPrivateOffer = offer.isPrivateOffer(); - iconView.setId(isPrivateOffer ? "image-lock2x" : isSellOffer ? "image-buy-white" : "image-sell-white"); - iconView.setFitHeight(16); - iconView.setFitWidth(16); + if (isPrivateOffer) { + button.setGraphic(GUIUtil.getLockLabel()); + } else { + iconView.setId(isSellOffer ? "image-buy-white" : "image-sell-white"); + iconView.setFitHeight(16); + iconView.setFitWidth(16); + } button.setId(isSellOffer ? "buy-button" : "sell-button"); button.setStyle("-fx-text-fill: white"); - title = Res.get("offerbook.takeOffer"); + title = Res.get(isSellOffer ? "mainView.menu.buyXmr" : "mainView.menu.sellXmr"); button.setTooltip(new Tooltip(Res.get("offerbook.takeOfferButton.tooltip", model.getDirectionLabelTooltip(offer)))); button.setOnAction(e -> onTakeOffer(offer)); button2.setManaged(false); @@ -1179,8 +1186,8 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM button.setOnAction(null); button2.setOnAction(null); if (tableRow != null) { - tableRow.setOpacity(1); tableRow.setOnMousePressed(null); + tableRow.getStyleClass().remove("row-faded"); } } } @@ -1236,7 +1243,7 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM setSortable(true); } }; - column.getStyleClass().addAll("last-column", "avatar-column"); + column.getStyleClass().addAll("avatar-column"); column.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); column.setCellFactory( new Callback<>() { @@ -1280,8 +1287,8 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM private void updateCreateOfferButton() { createOfferButton.setText(Res.get("offerbook.createNewOffer", - model.getDirection() == OfferDirection.BUY ? Res.get("shared.buy") : Res.get("shared.sell"), - getTradeCurrencyCode()).toUpperCase()); + model.getDirection() == OfferDirection.BUY ? Res.get("shared.buy").toUpperCase() : Res.get("shared.sell").toUpperCase(), + getTradeCurrencyCode())); } abstract String getTradeCurrencyCode(); diff --git a/desktop/src/main/java/haveno/desktop/main/offer/signedoffer/SignedOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/signedoffer/SignedOfferView.java index f241797fc7..bfc4118d25 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/signedoffer/SignedOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/signedoffer/SignedOfferView.java @@ -40,7 +40,6 @@ import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.collections.transformation.FilteredList; import javafx.collections.transformation.SortedList; import javafx.fxml.FXML; -import javafx.geometry.Insets; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; @@ -98,10 +97,6 @@ public class SignedOfferView extends ActivatableViewAndModel<VBox, SignedOffersV @Override public void initialize() { - Label label = new AutoTooltipLabel(Res.get("support.filter")); - HBox.setMargin(label, new Insets(5, 0, 0, 0)); - HBox.setHgrow(label, Priority.NEVER); - filterTextField = new InputTextField(); Tooltip tooltip = new Tooltip(); tooltip.setShowDelay(Duration.millis(100)); @@ -120,6 +115,7 @@ public class SignedOfferView extends ActivatableViewAndModel<VBox, SignedOffersV sortedList.comparatorProperty().bind(tableView.comparatorProperty()); tableView.setItems(sortedList); filterBox.initialize(filteredList, tableView); + filterBox.setPromptText(Res.get("shared.filter")); filterBox.activate(); contextMenu = new ContextMenu(); diff --git a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java index 58f6bf4158..cb1611841b 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferView.java @@ -108,6 +108,7 @@ import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import net.glxn.qrgen.QRCode; @@ -127,13 +128,13 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer private GridPane gridPane; private TitledGroupBg noFundingRequiredTitledGroupBg; private Label noFundingRequiredLabel; - private int lastGridRowNoFundingRequired; + private int gridRowNoFundingRequired; private TitledGroupBg payFundsTitledGroupBg; private TitledGroupBg advancedOptionsGroup; private VBox priceAsPercentageInputBox, amountRangeBox; private HBox fundingHBox, amountValueCurrencyBox, priceValueCurrencyBox, volumeValueCurrencyBox, priceAsPercentageValueCurrencyBox, minAmountValueCurrencyBox, advancedOptionsBox, - takeOfferBox, buttonBox, firstRowHBox; + takeOfferBox, nextButtonBox, firstRowHBox; private ComboBox<PaymentAccount> paymentAccountsComboBox; private TextArea extraInfoTextArea; private Label amountDescriptionLabel, @@ -142,7 +143,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer volumeCurrencyLabel, priceDescriptionLabel, volumeDescriptionLabel, waitingForFundsLabel, offerAvailabilityLabel, priceAsPercentageDescription, tradeFeeDescriptionLabel, resultLabel, tradeFeeInXmrLabel, xLabel, - fakeXLabel; + fakeXLabel, extraInfoLabel; private InputTextField amountTextField; private TextField paymentMethodTextField, currencyTextField, priceTextField, priceAsPercentageTextField, volumeTextField, amountRangeTextField; @@ -153,6 +154,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer private Button nextButton, cancelButton1, cancelButton2; private AutoTooltipButton takeOfferButton, fundFromSavingsWalletButton; private ImageView qrCodeImageView; + private StackPane qrCodePane; private BusyAnimation waitingForFundsBusyAnimation, offerAvailabilityBusyAnimation; private Notification walletFundedNotification; private OfferView.CloseHandler closeHandler; @@ -200,7 +202,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer createListeners(); - addButtons(); + addNextButtons(); addOfferAvailabilityLabel(); addFundingGroup(); @@ -339,8 +341,10 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer if (model.isRange()) { amountRangeTextField.setText(model.getAmountRange()); amountRangeBox.setVisible(true); + amountRangeBox.setManaged(true); } else { amountTextField.setDisable(true); + amountTextField.setManaged(true); } priceTextField.setText(model.getPrice()); @@ -361,20 +365,26 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer if (offer.hasBuyerAsTakerWithoutDeposit() && offer.getCombinedExtraInfo() != null && !offer.getCombinedExtraInfo().isEmpty()) { // attach extra info text area - extraInfoTextArea = addCompactTopLabelTextArea(gridPane, ++lastGridRowNoFundingRequired, Res.get("payment.shared.extraInfo.noDeposit"), "").second; - extraInfoTextArea.setText(offer.getCombinedExtraInfo()); - extraInfoTextArea.getStyleClass().add("text-area"); + //updateOfferElementsStyle(); + Tuple2<Label, TextArea> extraInfoTuple = addCompactTopLabelTextArea(gridPane, ++gridRowNoFundingRequired, Res.get("payment.shared.extraInfo.noDeposit"), ""); + extraInfoLabel = extraInfoTuple.first; + extraInfoLabel.setVisible(false); + extraInfoLabel.setManaged(false); + extraInfoTextArea = extraInfoTuple.second; + extraInfoTextArea.setVisible(false); + extraInfoTextArea.setManaged(false); + extraInfoTextArea.setText(offer.getCombinedExtraInfo().trim()); + extraInfoTextArea.getStyleClass().addAll("text-area", "flat-text-area-with-border"); extraInfoTextArea.setWrapText(true); - extraInfoTextArea.setPrefHeight(75); - extraInfoTextArea.setMinHeight(75); - extraInfoTextArea.setMaxHeight(150); + extraInfoTextArea.setMaxHeight(300); extraInfoTextArea.setEditable(false); - GridPane.setRowIndex(extraInfoTextArea, lastGridRowNoFundingRequired); + GUIUtil.adjustHeightAutomatically(extraInfoTextArea); + GridPane.setRowIndex(extraInfoTextArea, gridRowNoFundingRequired); GridPane.setColumnSpan(extraInfoTextArea, GridPane.REMAINING); GridPane.setColumnIndex(extraInfoTextArea, 0); // move up take offer buttons - GridPane.setRowIndex(takeOfferBox, lastGridRowNoFundingRequired + 1); + GridPane.setRowIndex(takeOfferBox, gridRowNoFundingRequired + 1); GridPane.setMargin(takeOfferBox, new Insets(15, 0, 0, 0)); } } @@ -490,19 +500,33 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer } cancelButton2.setVisible(true); + cancelButton2.setManaged(true); // temporarily disabled due to high CPU usage (per issue #4649) //waitingForFundsBusyAnimation.play(); if (model.getOffer().hasBuyerAsTakerWithoutDeposit()) { noFundingRequiredTitledGroupBg.setVisible(true); + noFundingRequiredTitledGroupBg.setManaged(true); noFundingRequiredLabel.setVisible(true); + noFundingRequiredLabel.setManaged(true); + if (model.getOffer().getCombinedExtraInfo() != null && !model.getOffer().getCombinedExtraInfo().isEmpty()) { + extraInfoLabel.setVisible(true); + extraInfoLabel.setManaged(true); + extraInfoTextArea.setVisible(true); + extraInfoTextArea.setManaged(true); + } } else { payFundsTitledGroupBg.setVisible(true); + payFundsTitledGroupBg.setManaged(true); totalToPayTextField.setVisible(true); + totalToPayTextField.setManaged(true); addressTextField.setVisible(true); - qrCodeImageView.setVisible(true); + addressTextField.setManaged(true); + qrCodePane.setVisible(true); + qrCodePane.setManaged(true); balanceTextField.setVisible(true); + balanceTextField.setManaged(true); } totalToPayTextField.setFundsStructure(Res.get("takeOffer.fundsBox.fundsStructure", @@ -536,8 +560,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer private void updateOfferElementsStyle() { GridPane.setColumnSpan(firstRowHBox, 1); - final String activeInputStyle = "input-with-border"; - final String readOnlyInputStyle = "input-with-border-readonly"; + final String activeInputStyle = "offer-input"; + final String readOnlyInputStyle = "offer-input-readonly"; amountValueCurrencyBox.getStyleClass().remove(activeInputStyle); amountValueCurrencyBox.getStyleClass().add(readOnlyInputStyle); priceAsPercentageValueCurrencyBox.getStyleClass().remove(activeInputStyle); @@ -631,7 +655,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer UserThread.runAfter(() -> new Popup().warning(newValue + "\n\n" + Res.get("takeOffer.alreadyPaidInFunds")) - .actionButtonTextWithGoTo("navigation.funds.availableForWithdrawal") + .actionButtonTextWithGoTo("funds.tab.withdrawal") .onAction(() -> { errorPopupDisplayed.set(true); model.resetOfferWarning(); @@ -663,6 +687,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer if (isOfferAvailable) { offerAvailabilityBusyAnimation.stop(); offerAvailabilityBusyAnimation.setVisible(false); + offerAvailabilityBusyAnimation.setManaged(false); if (!model.isRange() && !model.showPayFundsScreenDisplayed.get()) showNextStepAfterAmountIsSet(); } @@ -693,7 +718,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer if (DontShowAgainLookup.showAgain(key)) { UserThread.runAfter(() -> new Popup().headLine(Res.get("takeOffer.success.headline")) .feedback(Res.get("takeOffer.success.info")) - .actionButtonTextWithGoTo("navigation.portfolio.pending") + .actionButtonTextWithGoTo("portfolio.tab.pendingTrades") .dontShowAgainId(key) .onAction(() -> { UserThread.runAfter( @@ -755,7 +780,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer private void addGridPane() { gridPane = new GridPane(); gridPane.getStyleClass().add("content-pane"); - gridPane.setPadding(new Insets(15, 15, -1, 15)); + gridPane.setPadding(new Insets(25, 25, 25, 25)); gridPane.setHgap(5); gridPane.setVgap(5); GUIUtil.setDefaultTwoColumnConstraintsForGridPane(gridPane); @@ -837,11 +862,11 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer advancedOptionsBox.getChildren().addAll(tradeFeeFieldsBox); } - private void addButtons() { + private void addNextButtons() { Tuple3<Button, Button, HBox> tuple = add2ButtonsWithBox(gridPane, ++gridRow, Res.get("shared.nextStep"), Res.get("shared.cancel"), 15, true); - buttonBox = tuple.third; + nextButtonBox = tuple.third; nextButton = tuple.first; nextButton.setMaxWidth(200); @@ -870,7 +895,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer offerAvailabilityBusyAnimation = new BusyAnimation(false); offerAvailabilityLabel = new AutoTooltipLabel(Res.get("takeOffer.fundsBox.isOfferAvailable")); HBox.setMargin(offerAvailabilityLabel, new Insets(6, 0, 0, 0)); - buttonBox.getChildren().addAll(offerAvailabilityBusyAnimation, offerAvailabilityLabel); + nextButtonBox.getChildren().addAll(offerAvailabilityBusyAnimation, offerAvailabilityLabel); } private void addFundingGroup() { @@ -881,16 +906,18 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer noFundingRequiredTitledGroupBg.getStyleClass().add("last"); GridPane.setColumnSpan(noFundingRequiredTitledGroupBg, 2); noFundingRequiredTitledGroupBg.setVisible(false); + noFundingRequiredTitledGroupBg.setManaged(false); // no funding required description noFundingRequiredLabel = new AutoTooltipLabel(Res.get("takeOffer.fundsBox.noFundingRequiredDescription")); noFundingRequiredLabel.setVisible(false); + noFundingRequiredLabel.setManaged(false); //GridPane.setRowSpan(noFundingRequiredLabel, 1); GridPane.setRowIndex(noFundingRequiredLabel, gridRow); noFundingRequiredLabel.setPadding(new Insets(Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE, 0, 0, 0)); GridPane.setHalignment(noFundingRequiredLabel, HPos.LEFT); gridPane.getChildren().add(noFundingRequiredLabel); - lastGridRowNoFundingRequired = gridRow; + gridRowNoFundingRequired = gridRow; // funding title payFundsTitledGroupBg = addTitledGroupBg(gridPane, gridRow, 3, @@ -898,32 +925,38 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer payFundsTitledGroupBg.getStyleClass().add("last"); GridPane.setColumnSpan(payFundsTitledGroupBg, 2); payFundsTitledGroupBg.setVisible(false); + payFundsTitledGroupBg.setManaged(false); totalToPayTextField = addFundsTextfield(gridPane, gridRow, Res.get("shared.totalsNeeded"), Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE); totalToPayTextField.setVisible(false); + totalToPayTextField.setManaged(false); - qrCodeImageView = new ImageView(); - qrCodeImageView.setVisible(false); - qrCodeImageView.setFitHeight(150); - qrCodeImageView.setFitWidth(150); - qrCodeImageView.getStyleClass().add("qr-code"); - Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow"))); - qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter( + Tuple2<StackPane, ImageView> qrCodeTuple = GUIUtil.getSmallXmrQrCodePane(); + qrCodePane = qrCodeTuple.first; + qrCodeImageView = qrCodeTuple.second; + + Tooltip.install(qrCodePane, new Tooltip(Res.get("shared.openLargeQRWindow"))); + qrCodePane.setOnMouseClicked(e -> UserThread.runAfter( () -> new QRCodeWindow(getMoneroURI()).show(), 200, TimeUnit.MILLISECONDS)); - GridPane.setRowIndex(qrCodeImageView, gridRow); - GridPane.setColumnIndex(qrCodeImageView, 1); - GridPane.setRowSpan(qrCodeImageView, 3); - GridPane.setValignment(qrCodeImageView, VPos.BOTTOM); - GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10)); - gridPane.getChildren().add(qrCodeImageView); + GridPane.setRowIndex(qrCodePane, gridRow); + GridPane.setColumnIndex(qrCodePane, 1); + GridPane.setRowSpan(qrCodePane, 3); + GridPane.setValignment(qrCodePane, VPos.BOTTOM); + GridPane.setMargin(qrCodePane, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 10)); + gridPane.getChildren().add(qrCodePane); + + qrCodePane.setVisible(false); + qrCodePane.setManaged(false); addressTextField = addAddressTextField(gridPane, ++gridRow, Res.get("shared.tradeWalletAddress")); addressTextField.setVisible(false); + addressTextField.setManaged(false); balanceTextField = addBalanceTextField(gridPane, ++gridRow, Res.get("shared.tradeWalletBalance")); balanceTextField.setVisible(false); + balanceTextField.setManaged(false); fundingHBox = new HBox(); fundingHBox.setVisible(false); @@ -1000,6 +1033,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer }); cancelButton2.setDefaultButton(false); cancelButton2.setVisible(false); + cancelButton2.setManaged(false); } private void openWallet() { @@ -1100,6 +1134,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer fakeXLabel = new Label(); fakeXIcon = getIconForLabel(MaterialDesignIcon.CLOSE, "2em", fakeXLabel); fakeXLabel.setVisible(false); // we just use it to get the same layout as the upper row + fakeXIcon.setManaged(false); fakeXLabel.getStyleClass().add("opaque-icon-character"); HBox hBox = new HBox(); @@ -1147,7 +1182,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer UserThread.runAfter(() -> { new GenericMessageWindow() .preamble(Res.get("payment.tradingRestrictions")) - .instruction(offer.getCombinedExtraInfo()) + .instruction(offer.getCombinedExtraInfo().trim()) .actionButtonText(Res.get("shared.iConfirm")) .closeButtonText(Res.get("shared.close")) .width(Layout.INITIAL_WINDOW_WIDTH) diff --git a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java index c73ce988c6..1ad0f9547a 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/takeoffer/TakeOfferViewModel.java @@ -256,7 +256,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im new Popup().warning(Res.get("shared.notEnoughFunds", HavenoUtils.formatXmr(dataModel.getTotalToPay().get(), true), HavenoUtils.formatXmr(dataModel.getTotalAvailableBalance(), true))) - .actionButtonTextWithGoTo("navigation.funds.depositFunds") + .actionButtonTextWithGoTo("funds.tab.deposit") .onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, DepositView.class)) .show(); return false; diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/Overlay.java b/desktop/src/main/java/haveno/desktop/main/overlays/Overlay.java index e216b14ed9..ea29d4b7ec 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/Overlay.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/Overlay.java @@ -19,6 +19,8 @@ package haveno.desktop.main.overlays; import com.google.common.reflect.TypeToken; import de.jensd.fx.fontawesome.AwesomeIcon; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView; import haveno.common.Timer; import haveno.common.UserThread; import haveno.common.config.Config; @@ -42,8 +44,6 @@ import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; -import javafx.application.Platform; -import javafx.beans.binding.Bindings; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; @@ -52,6 +52,7 @@ import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.NodeOrientation; import javafx.geometry.Pos; +import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.PerspectiveCamera; import javafx.scene.Scene; @@ -122,7 +123,7 @@ public abstract class Overlay<T extends Overlay<T>> { Notification(AnimationType.SlideFromRightTop, ChangeBackgroundType.BlurLight), BackgroundInfo(AnimationType.SlideDownFromCenterTop, ChangeBackgroundType.BlurUltraLight), - Feedback(AnimationType.SlideDownFromCenterTop, ChangeBackgroundType.Darken), + Feedback(AnimationType.SlideDownFromCenterTop, ChangeBackgroundType.BlurLight), Information(AnimationType.FadeInAtCenter, ChangeBackgroundType.BlurLight), Instruction(AnimationType.ScaleFromCenter, ChangeBackgroundType.BlurLight), @@ -141,6 +142,9 @@ public abstract class Overlay<T extends Overlay<T>> { } } + private static int numCenterOverlays = 0; + private static int numBlurEffects = 0; + protected final static double DEFAULT_WIDTH = 668; protected Stage stage; protected GridPane gridPane; @@ -168,7 +172,7 @@ public abstract class Overlay<T extends Overlay<T>> { protected boolean showScrollPane = false; protected TextArea messageTextArea; - protected Label headlineIcon, copyIcon, headLineLabel; + protected Label headlineIcon, copyLabel, headLineLabel; protected String headLine, message, closeButtonText, actionButtonText, secondaryActionButtonText, dontShowAgainId, dontShowAgainText, truncatedMessage; @@ -249,6 +253,7 @@ public abstract class Overlay<T extends Overlay<T>> { protected void animateHide() { animateHide(() -> { + if (isCentered()) numCenterOverlays--; removeEffectFromBackground(); if (stage != null) @@ -541,6 +546,14 @@ public abstract class Overlay<T extends Overlay<T>> { layout(); + // add dropshadow if light mode or multiple centered overlays + if (isCentered()) { + numCenterOverlays++; + } + if (!CssTheme.isDarkTheme() || numCenterOverlays > 1) { + getRootContainer().getStyleClass().add("popup-dropshadow"); + } + addEffectToBackground(); // On Linux the owner stage does not move the child stage as it does on Mac @@ -739,6 +752,8 @@ public abstract class Overlay<T extends Overlay<T>> { } protected void addEffectToBackground() { + numBlurEffects++; + if (numBlurEffects > 1) return; if (type.changeBackgroundType == ChangeBackgroundType.BlurUltraLight) MainView.blurUltraLight(); else if (type.changeBackgroundType == ChangeBackgroundType.BlurLight) @@ -758,12 +773,14 @@ public abstract class Overlay<T extends Overlay<T>> { if (headLineLabel != null) { - if (copyIcon != null) { - copyIcon.getStyleClass().add("popup-icon-information"); - copyIcon.setManaged(true); - copyIcon.setVisible(true); - FormBuilder.getIconForLabel(AwesomeIcon.COPY, copyIcon, "1.1em"); - copyIcon.addEventHandler(MOUSE_CLICKED, mouseEvent -> { + if (copyLabel != null) { + copyLabel.getStyleClass().add("popup-icon-information"); + copyLabel.setManaged(true); + copyLabel.setVisible(true); + MaterialDesignIconView copyIcon = new MaterialDesignIconView(MaterialDesignIcon.CONTENT_COPY, "1.2em"); + copyLabel.setGraphic(copyIcon); + copyLabel.setCursor(Cursor.HAND); + copyLabel.addEventHandler(MOUSE_CLICKED, mouseEvent -> { if (message != null) { Utilities.copyToClipboard(getClipboardText()); Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); @@ -808,6 +825,8 @@ public abstract class Overlay<T extends Overlay<T>> { } protected void removeEffectFromBackground() { + numBlurEffects--; + if (numBlurEffects > 0) return; MainView.removeEffect(); } @@ -828,15 +847,15 @@ public abstract class Overlay<T extends Overlay<T>> { headLineLabel.setStyle(headlineStyle); if (message != null) { - copyIcon = new Label(); - copyIcon.setManaged(false); - copyIcon.setVisible(false); - copyIcon.setPadding(new Insets(3)); - copyIcon.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); + copyLabel = new Label(); + copyLabel.setManaged(false); + copyLabel.setVisible(false); + copyLabel.setPadding(new Insets(3)); + copyLabel.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); final Pane spacer = new Pane(); HBox.setHgrow(spacer, Priority.ALWAYS); spacer.setMinSize(Layout.PADDING, 1); - hBox.getChildren().addAll(headlineIcon, headLineLabel, spacer, copyIcon); + hBox.getChildren().addAll(headlineIcon, headLineLabel, spacer, copyLabel); } else { hBox.getChildren().addAll(headlineIcon, headLineLabel); } @@ -852,23 +871,8 @@ public abstract class Overlay<T extends Overlay<T>> { if (message != null) { messageTextArea = new TextArea(truncatedMessage); messageTextArea.setEditable(false); - messageTextArea.getStyleClass().add("text-area-no-border"); - messageTextArea.sceneProperty().addListener((o, oldScene, newScene) -> { - if (newScene != null) { - // avoid javafx css warning - CssTheme.loadSceneStyles(newScene, CssTheme.CSS_THEME_LIGHT, false); - messageTextArea.applyCss(); - var text = messageTextArea.lookup(".text"); - - messageTextArea.prefHeightProperty().bind(Bindings.createDoubleBinding(() -> { - return messageTextArea.getFont().getSize() + text.getBoundsInLocal().getHeight(); - }, text.boundsInLocalProperty())); - - text.boundsInLocalProperty().addListener((observableBoundsAfter, boundsBefore, boundsAfter) -> { - Platform.runLater(() -> messageTextArea.requestLayout()); - }); - } - }); + messageTextArea.getStyleClass().add("text-area-popup"); + GUIUtil.adjustHeightAutomatically(messageTextArea); messageTextArea.setWrapText(true); Region messageRegion; @@ -1093,4 +1097,10 @@ public abstract class Overlay<T extends Overlay<T>> { ", message='" + message + '\'' + '}'; } + + private boolean isCentered() { + if (type.animationType == AnimationType.SlideDownFromCenterTop) return false; + if (type.animationType == AnimationType.SlideFromRightTop) return false; + return true; + } } diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java b/desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java index bd169b3a4b..0af066c9aa 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/editor/PasswordPopup.java @@ -71,7 +71,7 @@ public class PasswordPopup extends Overlay<PasswordPopup> { @Override public void show() { - actionButtonText("CONFIRM"); + actionButtonText("Confirm"); createGridPane(); addHeadLine(); addContent(); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/notifications/Notification.java b/desktop/src/main/java/haveno/desktop/main/overlays/notifications/Notification.java index 57a9550e3a..ebfcb4dcd4 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/notifications/Notification.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/notifications/Notification.java @@ -42,6 +42,7 @@ public class Notification extends Overlay<Notification> { private boolean hasBeenDisplayed; private boolean autoClose; private Timer autoCloseTimer; + private static final int BORDER_PADDING = 10; public Notification() { width = 413; // 320 visible bg because of insets @@ -205,8 +206,8 @@ public class Notification extends Overlay<Notification> { Window window = owner.getScene().getWindow(); double titleBarHeight = window.getHeight() - owner.getScene().getHeight(); double shadowInset = 44; - stage.setX(Math.round(window.getX() + window.getWidth() + shadowInset - stage.getWidth())); - stage.setY(Math.round(window.getY() + titleBarHeight - shadowInset)); + stage.setX(Math.round(window.getX() + window.getWidth() + shadowInset - stage.getWidth() - BORDER_PADDING)); + stage.setY(Math.round(window.getY() + titleBarHeight - shadowInset + BORDER_PADDING)); } @Override diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/notifications/NotificationCenter.java b/desktop/src/main/java/haveno/desktop/main/overlays/notifications/NotificationCenter.java index 829a0640d5..f9211cd17f 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/notifications/NotificationCenter.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/notifications/NotificationCenter.java @@ -216,7 +216,7 @@ public class NotificationCenter { if (DontShowAgainLookup.showAgain(key)) { Notification notification = new Notification().tradeHeadLine(trade.getShortId()).message(message); if (navigation.getCurrentPath() != null && !navigation.getCurrentPath().contains(PendingTradesView.class)) { - notification.actionButtonTextWithGoTo("navigation.portfolio.pending") + notification.actionButtonTextWithGoTo("portfolio.tab.pendingTrades") .onAction(() -> { DontShowAgainLookup.dontShowAgain(key, true); navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class); @@ -318,7 +318,7 @@ public class NotificationCenter { private void goToSupport(Trade trade, String message, Class<? extends DisputeView> viewClass) { Notification notification = new Notification().disputeHeadLine(trade.getShortId()).message(message); if (navigation.getCurrentPath() != null && !navigation.getCurrentPath().contains(viewClass)) { - notification.actionButtonTextWithGoTo("navigation.support") + notification.actionButtonTextWithGoTo("mainView.menu.support") .onAction(() -> navigation.navigateTo(MainView.class, SupportView.class, viewClass)) .show(); } else { diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java index 6a238e56ee..7f72fb2e10 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/ContractWindow.java @@ -46,6 +46,7 @@ import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextField; import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextFieldWithCopyIcon; import static haveno.desktop.util.FormBuilder.addLabelExplorerAddressTextField; import static haveno.desktop.util.FormBuilder.addLabelTxIdTextField; +import static haveno.desktop.util.FormBuilder.addSeparator; import static haveno.desktop.util.FormBuilder.addTitledGroupBg; import haveno.desktop.util.Layout; import haveno.network.p2p.NodeAddress; @@ -137,15 +138,20 @@ public class ContractWindow extends Overlay<ContractWindow> { addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("contractWindow.title")); addConfirmationLabelTextField(gridPane, rowIndex, Res.get("shared.offerId"), offer.getId(), Layout.TWICE_FIRST_ROW_DISTANCE); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("contractWindow.dates"), DisplayUtils.formatDateTime(offer.getDate()) + " / " + DisplayUtils.formatDateTime(dispute.getTradeDate())); String currencyCode = offer.getCurrencyCode(); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.offerType"), DisplayUtils.getDirectionBothSides(offer.getDirection(), offer.isPrivateOffer())); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.tradePrice"), FormattingUtils.formatPrice(contract.getPrice())); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.tradeAmount"), HavenoUtils.formatXmr(contract.getTradeAmount(), true)); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, VolumeUtil.formatVolumeLabel(currencyCode, ":"), @@ -157,16 +163,20 @@ public class ContractWindow extends Overlay<ContractWindow> { Res.getWithColAndCap("shared.seller") + " " + HavenoUtils.formatXmr(offer.getOfferPayload().getSellerSecurityDepositForTradeAmount(contract.getTradeAmount()), true); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), securityDeposit); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("contractWindow.xmrAddresses"), contract.getBuyerPayoutAddressString() + " / " + contract.getSellerPayoutAddressString()); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("contractWindow.onions"), contract.getBuyerNodeAddress().getFullAddress() + " / " + contract.getSellerNodeAddress().getFullAddress()); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("contractWindow.accountAge"), @@ -176,16 +186,19 @@ public class ContractWindow extends Overlay<ContractWindow> { DisputeManager<? extends DisputeList<Dispute>> disputeManager = getDisputeManager(dispute); String nrOfDisputesAsBuyer = disputeManager != null ? disputeManager.getNrOfDisputes(true, contract) : ""; String nrOfDisputesAsSeller = disputeManager != null ? disputeManager.getNrOfDisputes(false, contract) : ""; + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("contractWindow.numDisputes"), nrOfDisputesAsBuyer + " / " + nrOfDisputesAsSeller); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentDetails", Res.get("shared.buyer")), dispute.getBuyerPaymentAccountPayload() != null ? dispute.getBuyerPaymentAccountPayload().getPaymentDetails() : "NA"); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentDetails", Res.get("shared.seller")), @@ -219,6 +232,7 @@ public class ContractWindow extends Overlay<ContractWindow> { NodeAddress agentNodeAddress = disputeManager.getAgentNodeAddress(dispute); if (agentNodeAddress != null) { String value = agentMatrixUserName + " (" + agentNodeAddress.getFullAddress() + ")"; + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, title, value); } } @@ -232,40 +246,53 @@ public class ContractWindow extends Overlay<ContractWindow> { countries = CountryUtil.getCodesString(acceptedCountryCodes); tooltip = new Tooltip(CountryUtil.getNamesByCodesString(acceptedCountryCodes)); } + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.acceptedTakerCountries"), countries) .second.setTooltip(tooltip); } if (showAcceptedBanks) { if (offer.getPaymentMethod().equals(PaymentMethod.SAME_BANK)) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.bankName"), acceptedBanks.get(0)); } else if (offer.getPaymentMethod().equals(PaymentMethod.SPECIFIC_BANKS)) { String value = Joiner.on(", ").join(acceptedBanks); Tooltip tooltip = new Tooltip(Res.get("shared.acceptedBanks") + value); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.acceptedBanks"), value) .second.setTooltip(tooltip); } } + addSeparator(gridPane, ++rowIndex); addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.makerDepositTransactionId"), contract.getMakerDepositTxHash()); - if (contract.getTakerDepositTxHash() != null) + if (contract.getTakerDepositTxHash() != null) { addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.takerDepositTransactionId"), contract.getTakerDepositTxHash()); + } - if (dispute.getDelayedPayoutTxId() != null) + if (dispute.getDelayedPayoutTxId() != null) { + addSeparator(gridPane, ++rowIndex); addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.delayedPayoutTxId"), dispute.getDelayedPayoutTxId()); + } if (dispute.getDonationAddressOfDelayedPayoutTx() != null) { + addSeparator(gridPane, ++rowIndex); addLabelExplorerAddressTextField(gridPane, ++rowIndex, Res.get("shared.delayedPayoutTxReceiverAddress"), dispute.getDonationAddressOfDelayedPayoutTx()); } - if (dispute.getPayoutTxSerialized() != null) + if (dispute.getPayoutTxSerialized() != null) { + addSeparator(gridPane, ++rowIndex); addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.payoutTxId"), dispute.getPayoutTxId()); + } - if (dispute.getContractHash() != null) + if (dispute.getContractHash() != null) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, Res.get("contractWindow.contractHash"), Utils.HEX.encode(dispute.getContractHash())).second.setMouseTransparent(false); + } + addSeparator(gridPane, ++rowIndex); Button viewContractButton = addConfirmationLabelButton(gridPane, ++rowIndex, Res.get("shared.contractAsJson"), Res.get("shared.viewContractAsJson"), 0).second; viewContractButton.setDefaultButton(false); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java index 5004d240d8..49931e202b 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java @@ -188,7 +188,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> { protected void createGridPane() { super.createGridPane(); gridPane.setPadding(new Insets(35, 40, 30, 40)); - gridPane.getStyleClass().add("grid-pane"); + gridPane.getStyleClass().addAll("grid-pane", "popup-with-input"); gridPane.getColumnConstraints().get(0).setHalignment(HPos.LEFT); gridPane.setPrefWidth(width); } @@ -413,11 +413,13 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> { private void addPayoutAmountTextFields() { buyerPayoutAmountInputTextField = new InputTextField(); buyerPayoutAmountInputTextField.setLabelFloat(true); + buyerPayoutAmountInputTextField.getStyleClass().add("label-float"); buyerPayoutAmountInputTextField.setEditable(false); buyerPayoutAmountInputTextField.setPromptText(Res.get("disputeSummaryWindow.payoutAmount.buyer")); sellerPayoutAmountInputTextField = new InputTextField(); sellerPayoutAmountInputTextField.setLabelFloat(true); + sellerPayoutAmountInputTextField.getStyleClass().add("label-float"); sellerPayoutAmountInputTextField.setPromptText(Res.get("disputeSummaryWindow.payoutAmount.seller")); sellerPayoutAmountInputTextField.setEditable(false); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/GenericMessageWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/GenericMessageWindow.java index 01d119a479..9ee42ae27c 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/GenericMessageWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/GenericMessageWindow.java @@ -18,6 +18,7 @@ package haveno.desktop.main.overlays.windows; import haveno.desktop.main.overlays.Overlay; +import haveno.desktop.util.GUIUtil; import haveno.desktop.util.Layout; import javafx.scene.control.Label; import javafx.scene.control.TextArea; @@ -28,6 +29,7 @@ import static haveno.desktop.util.FormBuilder.addTextArea; public class GenericMessageWindow extends Overlay<GenericMessageWindow> { private String preamble; + private static final double MAX_TEXT_AREA_HEIGHT = 250; public GenericMessageWindow() { super(); @@ -54,20 +56,11 @@ public class GenericMessageWindow extends Overlay<GenericMessageWindow> { } checkNotNull(message, "message must not be null"); TextArea textArea = addTextArea(gridPane, ++rowIndex, "", 10); + textArea.getStyleClass().add("flat-text-area-with-border"); textArea.setText(message); textArea.setEditable(false); textArea.setWrapText(true); - // sizes the textArea to fit within its parent container - double verticalSizePercentage = ensureRange(countLines(message) / 20.0, 0.2, 0.7); - textArea.setPrefSize(Layout.INITIAL_WINDOW_WIDTH, Layout.INITIAL_WINDOW_HEIGHT * verticalSizePercentage); - } - - private static int countLines(String str) { - String[] lines = str.split("\r\n|\r|\n"); - return lines.length; - } - - private static double ensureRange(double value, double min, double max) { - return Math.min(Math.max(value, min), max); + textArea.setPrefWidth(Layout.INITIAL_WINDOW_WIDTH); + GUIUtil.adjustHeightAutomatically(textArea, MAX_TEXT_AREA_HEIGHT); } } diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java index dd645246f7..b0c00ca60f 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/OfferDetailsWindow.java @@ -20,10 +20,16 @@ package haveno.desktop.main.overlays.windows; import com.google.common.base.Joiner; import com.google.inject.Inject; import com.google.inject.name.Named; +import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXTextField; + +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView; import haveno.common.UserThread; import haveno.common.crypto.KeyRing; import haveno.common.util.Tuple2; import haveno.common.util.Tuple4; +import haveno.common.util.Utilities; import haveno.core.locale.CountryUtil; import haveno.core.locale.Res; import haveno.core.monetary.Price; @@ -45,30 +51,37 @@ import haveno.desktop.components.BusyAnimation; import haveno.desktop.main.overlays.Overlay; import haveno.desktop.main.overlays.editor.PasswordPopup; import haveno.desktop.main.overlays.popups.Popup; -import haveno.desktop.util.CssTheme; import haveno.desktop.util.DisplayUtils; import static haveno.desktop.util.FormBuilder.addButtonAfterGroup; import static haveno.desktop.util.FormBuilder.addButtonBusyAnimationLabelAfterGroup; import static haveno.desktop.util.FormBuilder.addConfirmationLabelLabel; import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextArea; import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextFieldWithCopyIcon; +import static haveno.desktop.util.FormBuilder.addLabel; +import static haveno.desktop.util.FormBuilder.addSeparator; import static haveno.desktop.util.FormBuilder.addTitledGroupBg; import haveno.desktop.util.GUIUtil; import haveno.desktop.util.Layout; import java.math.BigInteger; import java.util.List; import java.util.Optional; -import javafx.application.Platform; -import javafx.beans.binding.Bindings; import javafx.geometry.HPos; import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.geometry.VPos; +import javafx.scene.Node; import javafx.scene.control.Button; +import javafx.scene.control.ContentDisplay; import javafx.scene.control.Label; import javafx.scene.control.TextArea; import javafx.scene.control.Tooltip; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; + import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; import org.slf4j.Logger; @@ -115,7 +128,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { this.tradePrice = tradePrice; rowIndex = -1; - width = 1118; + width = Layout.DETAILS_WINDOW_WIDTH; createGridPane(); addContent(); display(); @@ -124,7 +137,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { public void show(Offer offer) { this.offer = offer; rowIndex = -1; - width = 1118; + width = Layout.DETAILS_WINDOW_WIDTH; createGridPane(); addContent(); display(); @@ -194,7 +207,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { rows++; } - addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get(offer.isPrivateOffer() ? "shared.Offer" : "shared.Offer")); + addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.Offer")); String counterCurrencyDirectionInfo = ""; String xmrDirectionInfo = ""; @@ -218,17 +231,22 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { addConfirmationLabelLabel(gridPane, rowIndex, offerTypeLabel, DisplayUtils.getDirectionBothSides(direction, offer.isPrivateOffer()), firstRowDistance); } + String amount = Res.get("shared.xmrAmount"); + addSeparator(gridPane, ++rowIndex); if (takeOfferHandlerOptional.isPresent()) { addConfirmationLabelLabel(gridPane, ++rowIndex, amount + xmrDirectionInfo, HavenoUtils.formatXmr(tradeAmount, true)); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, VolumeUtil.formatVolumeLabel(currencyCode) + counterCurrencyDirectionInfo, VolumeUtil.formatVolumeWithCode(offer.getVolumeByAmount(tradeAmount))); } else { addConfirmationLabelLabel(gridPane, ++rowIndex, amount + xmrDirectionInfo, HavenoUtils.formatXmr(offer.getAmount(), true)); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.minXmrAmount"), HavenoUtils.formatXmr(offer.getMinAmount(), true)); + addSeparator(gridPane, ++rowIndex); String volume = VolumeUtil.formatVolumeWithCode(offer.getVolume()); String minVolume = ""; if (offer.getVolume() != null && offer.getMinVolume() != null && @@ -239,6 +257,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { } String priceLabel = Res.get("shared.price"); + addSeparator(gridPane, ++rowIndex); if (takeOfferHandlerOptional.isPresent()) { addConfirmationLabelLabel(gridPane, ++rowIndex, priceLabel, FormattingUtils.formatPrice(tradePrice)); } else { @@ -264,6 +283,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { final PaymentAccount myPaymentAccount = user.getPaymentAccount(makerPaymentAccountId); String countryCode = offer.getCountryCode(); boolean isMyOffer = offer.isMyOffer(keyRing); + addSeparator(gridPane, ++rowIndex); if (isMyOffer && makerPaymentAccountId != null && myPaymentAccount != null) { addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.myTradingAccount"), myPaymentAccount.getAccountName()); } else { @@ -272,6 +292,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { } if (showXmrAutoConf) { + addSeparator(gridPane, ++rowIndex); String isAutoConf = offer.isXmrAutoConf() ? Res.get("shared.yes") : Res.get("shared.no"); @@ -280,8 +301,10 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { if (showAcceptedBanks) { if (paymentMethod.equals(PaymentMethod.SAME_BANK)) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.bankId"), acceptedBanks.get(0)); } else if (isSpecificBanks) { + addSeparator(gridPane, ++rowIndex); String value = Joiner.on(", ").join(acceptedBanks); String acceptedBanksLabel = Res.get("shared.acceptedBanks"); Tooltip tooltip = new Tooltip(acceptedBanksLabel + " " + value); @@ -291,6 +314,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { } } if (showAcceptedCountryCodes) { + addSeparator(gridPane, ++rowIndex); String countries; Tooltip tooltip = null; if (CountryUtil.containsAllSepaEuroCountries(acceptedCountryCodes)) { @@ -313,29 +337,16 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { } if (isF2F) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("payment.f2f.city"), offer.getF2FCity()); } if (showOfferExtraInfo) { + addSeparator(gridPane, ++rowIndex); TextArea textArea = addConfirmationLabelTextArea(gridPane, ++rowIndex, Res.get("payment.shared.extraInfo"), "", 0).second; - textArea.setText(offer.getCombinedExtraInfo()); - textArea.setMaxHeight(200); - textArea.sceneProperty().addListener((o, oldScene, newScene) -> { - if (newScene != null) { - // avoid javafx css warning - CssTheme.loadSceneStyles(newScene, CssTheme.CSS_THEME_LIGHT, false); - textArea.applyCss(); - var text = textArea.lookup(".text"); - - textArea.prefHeightProperty().bind(Bindings.createDoubleBinding(() -> { - return textArea.getFont().getSize() + text.getBoundsInLocal().getHeight(); - }, text.boundsInLocalProperty())); - - text.boundsInLocalProperty().addListener((observableBoundsAfter, boundsBefore, boundsAfter) -> { - Platform.runLater(() -> textArea.requestLayout()); - }); - } - }); + textArea.setText(offer.getCombinedExtraInfo().trim()); + textArea.setMaxHeight(Layout.DETAILS_WINDOW_EXTRA_INFO_MAX_HEIGHT); textArea.setEditable(false); + GUIUtil.adjustHeightAutomatically(textArea, Layout.DETAILS_WINDOW_EXTRA_INFO_MAX_HEIGHT); } // get amount reserved for the offer @@ -355,13 +366,16 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { if (offerChallenge != null) rows++; - addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.GROUP_DISTANCE); + addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.COMPACT_GROUP_DISTANCE); addConfirmationLabelTextFieldWithCopyIcon(gridPane, rowIndex, Res.get("shared.offerId"), offer.getId(), - Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE); + Layout.TWICE_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, Res.get("offerDetailsWindow.makersOnion"), offer.getMakerNodeAddress().getFullAddress()); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.creationDate"), DisplayUtils.formatDateTime(offer.getDate())); + addSeparator(gridPane, ++rowIndex); String value = Res.getWithColAndCap("shared.buyer") + " " + HavenoUtils.formatXmr(takeOfferHandlerOptional.isPresent() ? offer.getOfferPayload().getBuyerSecurityDepositForTradeAmount(tradeAmount) : offer.getOfferPayload().getMaxBuyerSecurityDeposit(), true) + @@ -372,27 +386,75 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), value); if (reservedAmount != null) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.reservedAmount"), HavenoUtils.formatXmr(reservedAmount, true)); } - if (countryCode != null && !isF2F) + if (countryCode != null && !isF2F) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.countryBank"), CountryUtil.getNameAndCode(countryCode)); + } - if (offerChallenge != null) - addConfirmationLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, Res.get("offerDetailsWindow.challenge"), offerChallenge); + if (offerChallenge != null) { + addSeparator(gridPane, ++rowIndex); + + // add label + Label label = addLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.challenge"), 0); + label.getStyleClass().addAll("confirmation-label", "regular-text-color"); + GridPane.setHalignment(label, HPos.LEFT); + GridPane.setValignment(label, VPos.TOP); + + // add vbox with passphrase and copy button + VBox vbox = new VBox(13); + vbox.setAlignment(Pos.TOP_CENTER); + VBox.setVgrow(vbox, Priority.ALWAYS); + vbox.getStyleClass().addAll("passphrase-copy-box"); + + // add passphrase + JFXTextField centerLabel = new JFXTextField(offerChallenge); + centerLabel.getStyleClass().add("confirmation-value"); + centerLabel.setAlignment(Pos.CENTER); + centerLabel.setFocusTraversable(false); + + // add copy button + Label copyLabel = new Label(); + copyLabel.getStyleClass().addAll("icon"); + copyLabel.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); + MaterialDesignIconView copyIcon = new MaterialDesignIconView(MaterialDesignIcon.CONTENT_COPY, "1.2em"); + copyIcon.setFill(Color.WHITE); + copyLabel.setGraphic(copyIcon); + JFXButton copyButton = new JFXButton(Res.get("offerDetailsWindow.challenge.copy"), copyLabel); + copyButton.setContentDisplay(ContentDisplay.LEFT); + copyButton.setGraphicTextGap(8); + copyButton.setOnMouseClicked(e -> { + Utilities.copyToClipboard(offerChallenge); + Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); + Node node = (Node) e.getSource(); + UserThread.runAfter(() -> tp.hide(), 1); + tp.show(node, e.getScreenX() + Layout.PADDING, e.getScreenY() + Layout.PADDING); + }); + copyButton.setId("buy-button"); + copyButton.setFocusTraversable(false); + vbox.getChildren().addAll(centerLabel, copyButton); + + // add vbox to grid pane in next column + GridPane.setRowIndex(vbox, rowIndex); + GridPane.setColumnIndex(vbox, 1); + gridPane.getChildren().add(vbox); + } if (placeOfferHandlerOptional.isPresent()) { - addTitledGroupBg(gridPane, ++rowIndex, 1, Res.get("offerDetailsWindow.commitment"), Layout.GROUP_DISTANCE); + addTitledGroupBg(gridPane, ++rowIndex, 1, Res.get("offerDetailsWindow.commitment"), Layout.COMPACT_GROUP_DISTANCE); final Tuple2<Label, Label> labelLabelTuple2 = addConfirmationLabelLabel(gridPane, rowIndex, Res.get("offerDetailsWindow.agree"), Res.get("createOffer.tac"), - Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE); + Layout.TWICE_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE); labelLabelTuple2.second.setWrapText(true); addConfirmAndCancelButtons(true); } else if (takeOfferHandlerOptional.isPresent()) { - addTitledGroupBg(gridPane, ++rowIndex, 1, Res.get("shared.contract"), Layout.GROUP_DISTANCE); + addTitledGroupBg(gridPane, ++rowIndex, 1, Res.get("shared.contract"), Layout.COMPACT_GROUP_DISTANCE); final Tuple2<Label, Label> labelLabelTuple2 = addConfirmationLabelLabel(gridPane, rowIndex, Res.get("offerDetailsWindow.tac"), Res.get("takeOffer.tac"), - Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE); + Layout.TWICE_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE); labelLabelTuple2.second.setWrapText(true); addConfirmAndCancelButtons(false); @@ -418,9 +480,6 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { Res.get("offerDetailsWindow.confirm.taker", Res.get("shared.buy")) : Res.get("offerDetailsWindow.confirm.taker", Res.get("shared.sell")); - ImageView iconView = new ImageView(); - iconView.setId(isBuyerRole ? "image-buy-white" : "image-sell-white"); - Tuple4<Button, BusyAnimation, Label, HBox> placeOfferTuple = addButtonBusyAnimationLabelAfterGroup(gridPane, ++rowIndex, 1, isPlaceOffer ? placeOfferButtonText : takeOfferButtonText); @@ -428,11 +487,18 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> { AutoTooltipButton confirmButton = (AutoTooltipButton) placeOfferTuple.first; confirmButton.setMinHeight(40); confirmButton.setPadding(new Insets(0, 20, 0, 20)); - confirmButton.setGraphic(iconView); confirmButton.setGraphicTextGap(10); confirmButton.setId(isBuyerRole ? "buy-button-big" : "sell-button-big"); confirmButton.updateText(isPlaceOffer ? placeOfferButtonText : takeOfferButtonText); + if (offer.hasBuyerAsTakerWithoutDeposit()) { + confirmButton.setGraphic(GUIUtil.getLockLabel()); + } else { + ImageView iconView = new ImageView(); + iconView.setId(isBuyerRole ? "image-buy-white" : "image-sell-white"); + confirmButton.setGraphic(iconView); + } + busyAnimation = placeOfferTuple.second; Label spinnerInfoLabel = placeOfferTuple.third; diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/QRCodeWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/QRCodeWindow.java index 8bca62d143..21809a7501 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/QRCodeWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/QRCodeWindow.java @@ -17,9 +17,12 @@ package haveno.desktop.main.overlays.windows; +import haveno.common.util.Tuple2; +import haveno.common.util.Utilities; import haveno.core.locale.Res; import haveno.desktop.components.AutoTooltipLabel; import haveno.desktop.main.overlays.Overlay; +import haveno.desktop.util.GUIUtil; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.scene.control.Label; @@ -27,31 +30,35 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; import net.glxn.qrgen.QRCode; import net.glxn.qrgen.image.ImageType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; +import java.net.URI; public class QRCodeWindow extends Overlay<QRCodeWindow> { private static final Logger log = LoggerFactory.getLogger(QRCodeWindow.class); - private final ImageView qrCodeImageView; + private final StackPane qrCodePane; private final String moneroUri; - public QRCodeWindow(String bitcoinURI) { - this.moneroUri = bitcoinURI; + public QRCodeWindow(String moneroUri) { + this.moneroUri = moneroUri; + + Tuple2<StackPane, ImageView> qrCodeTuple = GUIUtil.getBigXmrQrCodePane(); + qrCodePane = qrCodeTuple.first; + ImageView qrCodeImageView = qrCodeTuple.second; + final byte[] imageBytes = QRCode - .from(bitcoinURI) + .from(moneroUri) .withSize(300, 300) .to(ImageType.PNG) .stream() .toByteArray(); Image qrImage = new Image(new ByteArrayInputStream(imageBytes)); - qrCodeImageView = new ImageView(qrImage); - qrCodeImageView.setFitHeight(250); - qrCodeImageView.setFitWidth(250); - qrCodeImageView.getStyleClass().add("qr-code"); + qrCodeImageView.setImage(qrImage); type = Type.Information; width = 468; @@ -65,10 +72,11 @@ public class QRCodeWindow extends Overlay<QRCodeWindow> { addHeadLine(); addMessage(); - GridPane.setRowIndex(qrCodeImageView, ++rowIndex); - GridPane.setColumnSpan(qrCodeImageView, 2); - GridPane.setHalignment(qrCodeImageView, HPos.CENTER); - gridPane.getChildren().add(qrCodeImageView); + qrCodePane.setOnMouseClicked(event -> openWallet()); + GridPane.setRowIndex(qrCodePane, ++rowIndex); + GridPane.setColumnSpan(qrCodePane, 2); + GridPane.setHalignment(qrCodePane, HPos.CENTER); + gridPane.getChildren().add(qrCodePane); String request = moneroUri.replace("%20", " ").replace("?", "\n?").replace("&", "\n&"); Label infoLabel = new AutoTooltipLabel(Res.get("qRCodeWindow.request", request)); @@ -91,4 +99,12 @@ public class QRCodeWindow extends Overlay<QRCodeWindow> { public String getClipboardText() { return moneroUri; } + + private void openWallet() { + try { + Utilities.openURI(URI.create(moneroUri)); + } catch (Exception e) { + log.warn(e.getMessage()); + } + } } diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignPaymentAccountsWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignPaymentAccountsWindow.java index fe8b8c9009..3bf7be6b14 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignPaymentAccountsWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignPaymentAccountsWindow.java @@ -113,7 +113,7 @@ public class SignPaymentAccountsWindow extends Overlay<SignPaymentAccountsWindow // We want to have more space to read list entries... initial screen does not look so nice now, but // dynamically updating height of window is a bit tricky.... @christoph feel free to improve if you like... gridPane.setPrefHeight(600); - + gridPane.getStyleClass().add("popup-with-input"); gridPane.getColumnConstraints().get(1).setHgrow(Priority.NEVER); headLine(Res.get("popup.accountSigning.selectAccounts.headline")); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignSpecificWitnessWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignSpecificWitnessWindow.java index f008dd7b9e..cd9ebba4ac 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignSpecificWitnessWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignSpecificWitnessWindow.java @@ -67,6 +67,7 @@ public class SignSpecificWitnessWindow extends Overlay<SignSpecificWitnessWindow gridPane.setPrefHeight(600); gridPane.getColumnConstraints().get(1).setHgrow(Priority.NEVER); + gridPane.getStyleClass().add("popup-with-input"); headLine(Res.get("popup.accountSigning.singleAccountSelect.headline")); type = Type.Attention; diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java index 6b618adf68..cfed34d8c9 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/SignUnsignedPubKeysWindow.java @@ -92,6 +92,7 @@ public class SignUnsignedPubKeysWindow extends Overlay<SignUnsignedPubKeysWindow gridPane.setPrefHeight(600); gridPane.getColumnConstraints().get(1).setHgrow(Priority.NEVER); + gridPane.getStyleClass().add("popup-with-input"); headLine(Res.get("popup.accountSigning.singleAccountSelect.headline")); type = Type.Attention; diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeDetailsWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeDetailsWindow.java index 004cc0a3f5..451b440ab9 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeDetailsWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeDetailsWindow.java @@ -38,22 +38,21 @@ import haveno.core.xmr.wallet.BtcWalletService; import haveno.desktop.components.HavenoTextArea; import haveno.desktop.main.MainView; import haveno.desktop.main.overlays.Overlay; -import haveno.desktop.util.CssTheme; import haveno.desktop.util.DisplayUtils; +import haveno.desktop.util.GUIUtil; + import static haveno.desktop.util.DisplayUtils.getAccountWitnessDescription; import static haveno.desktop.util.FormBuilder.add2ButtonsWithBox; import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextArea; import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextField; import static haveno.desktop.util.FormBuilder.addLabelTxIdTextField; +import static haveno.desktop.util.FormBuilder.addSeparator; import static haveno.desktop.util.FormBuilder.addTitledGroupBg; import haveno.desktop.util.Layout; import haveno.network.p2p.NodeAddress; -import javafx.application.Platform; -import javafx.beans.binding.Bindings; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.value.ChangeListener; -import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.TextArea; @@ -107,7 +106,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> { this.trade = trade; rowIndex = -1; - width = 918; + width = Layout.DETAILS_WINDOW_WIDTH; createGridPane(); addContent(); display(); @@ -127,7 +126,6 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> { @Override protected void createGridPane() { super.createGridPane(); - gridPane.setPadding(new Insets(35, 40, 30, 40)); gridPane.getStyleClass().add("grid-pane"); } @@ -135,7 +133,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> { Offer offer = trade.getOffer(); Contract contract = trade.getContract(); - int rows = 5; + int rows = 9; addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("tradeDetailsWindow.headline")); boolean myOffer = tradeManager.isMyOffer(offer); @@ -156,18 +154,22 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> { xmrDirectionInfo = toSpend; } + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.xmrAmount") + xmrDirectionInfo, HavenoUtils.formatXmr(trade.getAmount(), true)); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, VolumeUtil.formatVolumeLabel(offer.getCurrencyCode()) + counterCurrencyDirectionInfo, VolumeUtil.formatVolumeWithCode(trade.getVolume())); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.tradePrice"), FormattingUtils.formatPrice(trade.getPrice())); String paymentMethodText = Res.get(offer.getPaymentMethod().getId()); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentMethod"), paymentMethodText); // second group - rows = 7; + rows = 5; if (offer.getCombinedExtraInfo() != null && !offer.getCombinedExtraInfo().isEmpty()) rows++; @@ -200,9 +202,10 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> { if (trade.getTradePeerNodeAddress() != null) rows++; - addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.GROUP_DISTANCE); + addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.COMPACT_GROUP_DISTANCE); addConfirmationLabelTextField(gridPane, rowIndex, Res.get("shared.tradeId"), - trade.getId(), Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE); + trade.getId(), Layout.TWICE_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.tradeDate"), DisplayUtils.formatDateTime(trade.getDate())); String securityDeposit = Res.getWithColAndCap("shared.buyer") + @@ -212,40 +215,30 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> { Res.getWithColAndCap("shared.seller") + " " + HavenoUtils.formatXmr(trade.getSellerSecurityDepositBeforeMiningFee(), true); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), securityDeposit); NodeAddress arbitratorNodeAddress = trade.getArbitratorNodeAddress(); if (arbitratorNodeAddress != null) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.agentAddresses"), arbitratorNodeAddress.getFullAddress()); } - if (trade.getTradePeerNodeAddress() != null) + if (trade.getTradePeerNodeAddress() != null) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.tradePeersOnion"), trade.getTradePeerNodeAddress().getFullAddress()); + } if (offer.getCombinedExtraInfo() != null && !offer.getCombinedExtraInfo().isEmpty()) { + addSeparator(gridPane, ++rowIndex); TextArea textArea = addConfirmationLabelTextArea(gridPane, ++rowIndex, Res.get("payment.shared.extraInfo.offer"), "", 0).second; - textArea.setText(offer.getCombinedExtraInfo()); - textArea.setMaxHeight(200); - textArea.sceneProperty().addListener((o, oldScene, newScene) -> { - if (newScene != null) { - // avoid javafx css warning - CssTheme.loadSceneStyles(newScene, CssTheme.CSS_THEME_LIGHT, false); - textArea.applyCss(); - var text = textArea.lookup(".text"); - - textArea.prefHeightProperty().bind(Bindings.createDoubleBinding(() -> { - return textArea.getFont().getSize() + text.getBoundsInLocal().getHeight(); - }, text.boundsInLocalProperty())); - - text.boundsInLocalProperty().addListener((observableBoundsAfter, boundsBefore, boundsAfter) -> { - Platform.runLater(() -> textArea.requestLayout()); - }); - } - }); + textArea.setText(offer.getCombinedExtraInfo().trim()); + textArea.setMaxHeight(Layout.DETAILS_WINDOW_EXTRA_INFO_MAX_HEIGHT); textArea.setEditable(false); + GUIUtil.adjustHeightAutomatically(textArea, Layout.DETAILS_WINDOW_EXTRA_INFO_MAX_HEIGHT); } if (contract != null) { @@ -254,6 +247,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> { if (buyerPaymentAccountPayload != null) { String paymentDetails = buyerPaymentAccountPayload.getPaymentDetails(); String postFix = " / " + buyersAccountAge; + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentDetails", Res.get("shared.buyer")), paymentDetails + postFix).second.setTooltip(new Tooltip(paymentDetails + postFix)); @@ -261,21 +255,27 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> { if (sellerPaymentAccountPayload != null) { String paymentDetails = sellerPaymentAccountPayload.getPaymentDetails(); String postFix = " / " + sellersAccountAge; + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentDetails", Res.get("shared.seller")), paymentDetails + postFix).second.setTooltip(new Tooltip(paymentDetails + postFix)); } - if (buyerPaymentAccountPayload == null && sellerPaymentAccountPayload == null) + if (buyerPaymentAccountPayload == null && sellerPaymentAccountPayload == null) { + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("shared.paymentMethod"), Res.get(contract.getPaymentMethodId())); + } } - if (trade.getMaker().getDepositTxHash() != null) + if (trade.getMaker().getDepositTxHash() != null) { + addSeparator(gridPane, ++rowIndex); addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.makerDepositTransactionId"), trade.getMaker().getDepositTxHash()); - if (trade.getTaker().getDepositTxHash() != null) + } + if (trade.getTaker().getDepositTxHash() != null) { addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.takerDepositTransactionId"), trade.getTaker().getDepositTxHash()); + } if (showDisputedTx) { @@ -287,6 +287,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> { } if (trade.hasFailed()) { + addSeparator(gridPane, ++rowIndex); textArea = addConfirmationLabelTextArea(gridPane, ++rowIndex, Res.get("shared.errorMessage"), "", 0).second; textArea.setText(trade.getErrorMessage()); textArea.setEditable(false); @@ -302,6 +303,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> { textArea.scrollTopProperty().addListener(changeListener); textArea.setScrollTop(30); + addSeparator(gridPane, ++rowIndex); addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.tradePhase"), trade.getPhase().name()); } diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeFeedbackWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeFeedbackWindow.java index 76d414b380..a9202f9b8a 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeFeedbackWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TradeFeedbackWindow.java @@ -40,7 +40,7 @@ public class TradeFeedbackWindow extends Overlay<TradeFeedbackWindow> { @Override public void show() { headLine(Res.get("tradeFeedbackWindow.title")); - message(Res.get("tradeFeedbackWindow.msg.part1")); + //message(Res.get("tradeFeedbackWindow.msg.part1")); // TODO: this message part has padding which remaining message does not have hideCloseButton(); actionButtonText(Res.get("shared.close")); @@ -51,6 +51,17 @@ public class TradeFeedbackWindow extends Overlay<TradeFeedbackWindow> { protected void addMessage() { super.addMessage(); + AutoTooltipLabel messageLabel1 = new AutoTooltipLabel(Res.get("tradeFeedbackWindow.msg.part1")); + messageLabel1.setMouseTransparent(true); + messageLabel1.setWrapText(true); + GridPane.setHalignment(messageLabel1, HPos.LEFT); + GridPane.setHgrow(messageLabel1, Priority.ALWAYS); + GridPane.setRowIndex(messageLabel1, ++rowIndex); + GridPane.setColumnIndex(messageLabel1, 0); + GridPane.setColumnSpan(messageLabel1, 2); + gridPane.getChildren().add(messageLabel1); + GridPane.setMargin(messageLabel1, new Insets(10, 0, 10, 0)); + AutoTooltipLabel messageLabel2 = new AutoTooltipLabel(Res.get("tradeFeedbackWindow.msg.part2")); messageLabel2.setMouseTransparent(true); messageLabel2.setWrapText(true); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TxDetailsWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TxDetailsWindow.java index 1af5d97fbf..417e5c0043 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/TxDetailsWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/TxDetailsWindow.java @@ -30,7 +30,6 @@ import static haveno.desktop.util.FormBuilder.addConfirmationLabelLabel; import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextFieldWithCopyIcon; import static haveno.desktop.util.FormBuilder.addLabelTxIdTextField; import static haveno.desktop.util.FormBuilder.addMultilineLabel; -import static haveno.desktop.util.FormBuilder.addTitledGroupBg; import java.math.BigInteger; @@ -50,21 +49,20 @@ public class TxDetailsWindow extends Overlay<TxDetailsWindow> { this.item = item; rowIndex = -1; width = 918; + if (headLine == null) + headLine = Res.get("txDetailsWindow.headline"); createGridPane(); gridPane.setHgap(15); addHeadLine(); addContent(); addButtons(); - addDontShowAgainCheckBox(); applyStyles(); display(); } protected void addContent() { - int rows = 10; MoneroTxWallet tx = item.getTx(); String memo = tx.getNote(); - if (memo != null && !"".equals(memo)) rows++; String txKey = null; boolean isOutgoing = tx.getOutgoingTransfer() != null; if (isOutgoing) { @@ -74,18 +72,11 @@ public class TxDetailsWindow extends Overlay<TxDetailsWindow> { // TODO (monero-java): wallet.getTxKey() should return null if key does not exist instead of throwing exception } } - if (txKey != null && !"".equals(txKey)) rows++; - - // add title - addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("txDetailsWindow.headline")); - Region spacer = new Region(); - spacer.setMinHeight(15); - gridPane.add(spacer, 0, ++rowIndex); // add sent or received note String resKey = isOutgoing ? "txDetailsWindow.xmr.noteSent" : "txDetailsWindow.xmr.noteReceived"; GridPane.setColumnSpan(addMultilineLabel(gridPane, ++rowIndex, Res.get(resKey), 0), 2); - spacer = new Region(); + Region spacer = new Region(); spacer.setMinHeight(15); gridPane.add(spacer, 0, ++rowIndex); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/VerifyDisputeResultSignatureWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/VerifyDisputeResultSignatureWindow.java index fd60d07193..a8bd993219 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/VerifyDisputeResultSignatureWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/VerifyDisputeResultSignatureWindow.java @@ -77,6 +77,7 @@ public class VerifyDisputeResultSignatureWindow extends Overlay<VerifyDisputeRes gridPane.setVgap(5); gridPane.setPadding(new Insets(64, 64, 64, 64)); gridPane.setPrefWidth(width); + gridPane.getStyleClass().add("popup-with-input"); ColumnConstraints columnConstraints1 = new ColumnConstraints(); columnConstraints1.setHalignment(HPos.RIGHT); diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/PortfolioView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/PortfolioView.java index c3a1e76738..b06274ccc7 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/PortfolioView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/PortfolioView.java @@ -80,9 +80,9 @@ public class PortfolioView extends ActivatableView<TabPane, Void> { root.setTabClosingPolicy(TabPane.TabClosingPolicy.ALL_TABS); failedTradesTab.setClosable(false); - openOffersTab.setText(Res.get("portfolio.tab.openOffers").toUpperCase()); - pendingTradesTab.setText(Res.get("portfolio.tab.pendingTrades").toUpperCase()); - closedTradesTab.setText(Res.get("portfolio.tab.history").toUpperCase()); + openOffersTab.setText(Res.get("portfolio.tab.openOffers")); + pendingTradesTab.setText(Res.get("portfolio.tab.pendingTrades")); + closedTradesTab.setText(Res.get("portfolio.tab.history")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(PortfolioView.class) == 1) diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesView.java index ab92f845db..aa7c6bb1c5 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/closedtrades/ClosedTradesView.java @@ -156,6 +156,8 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades @Override public void initialize() { + GUIUtil.applyTableStyle(tableView); + widthListener = (observable, oldValue, newValue) -> onWidthChange((double) newValue); tradeFeeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TRADE_FEE.toString().replace(" BTC", ""))); buyerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.BUYER_SEC.toString())); @@ -252,6 +254,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades tableView.setItems(sortedList); filterBox.initialize(filteredList, tableView); // here because filteredList is instantiated here + filterBox.setPromptText(Res.get("shared.filter")); filterBox.activate(); numItems.setText(Res.get("shared.numItemsLabel", sortedList.size())); @@ -326,7 +329,6 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades } private void setTradeIdColumnCellFactory() { - tradeIdColumn.getStyleClass().add("first-column"); tradeIdColumn.setCellValueFactory((offerListItem) -> new ReadOnlyObjectWrapper<>(offerListItem.getValue())); tradeIdColumn.setCellFactory( new Callback<>() { @@ -463,7 +465,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades @SuppressWarnings("UnusedReturnValue") private TableColumn<ClosedTradesListItem, ClosedTradesListItem> setAvatarColumnCellFactory() { - avatarColumn.getStyleClass().addAll("last-column", "avatar-column"); + avatarColumn.getStyleClass().add("avatar-column"); avatarColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); avatarColumn.setCellFactory( new Callback<>() { @@ -696,7 +698,7 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades if (!empty && newItem != null && !trade.isPayoutConfirmed()) { Label icon = FormBuilder.getIcon(AwesomeIcon.UNDO); JFXButton iconButton = new JFXButton("", icon); - iconButton.setStyle("-fx-cursor: hand;"); + iconButton.setStyle("-fx-cursor: hand; -fx-padding: 0 10 0 10;"); iconButton.getStyleClass().add("hidden-icon-button"); iconButton.setTooltip(new Tooltip(Res.get("portfolio.failed.revertToPending"))); iconButton.setOnAction(e -> onRevertTrade(trade)); diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java index 35fe31dc22..f92dc4c0b8 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/failedtrades/FailedTradesView.java @@ -115,6 +115,8 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades @Override public void initialize() { + GUIUtil.applyTableStyle(tableView); + priceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.price"))); amountColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode()))); volumeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amount"))); @@ -385,7 +387,6 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades } private void setTradeIdColumnCellFactory() { - tradeIdColumn.getStyleClass().add("first-column"); tradeIdColumn.setCellValueFactory((offerListItem) -> new ReadOnlyObjectWrapper<>(offerListItem.getValue())); tradeIdColumn.setCellFactory( new Callback<>() { @@ -455,7 +456,6 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades } private void setStateColumnCellFactory() { - stateColumn.getStyleClass().add("last-column"); stateColumn.setCellValueFactory((trade) -> new ReadOnlyObjectWrapper<>(trade.getValue())); stateColumn.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.fxml b/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.fxml index 035ec5fbdc..44b26f1bd3 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.fxml +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.fxml @@ -35,7 +35,6 @@ <Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/> </padding> <HBox fx:id="searchBox"> - <AutoTooltipLabel fx:id="filterLabel"/> <InputTextField fx:id="filterTextField" minWidth="500"/> <Pane fx:id="searchBoxSpacer"/> <AutoTooltipSlideToggleButton fx:id="selectToggleButton"/> diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.java index 3282ab078a..ec19daccdd 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/openoffer/OpenOffersView.java @@ -116,8 +116,6 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView @FXML HBox searchBox; @FXML - AutoTooltipLabel filterLabel; - @FXML InputTextField filterTextField; @FXML Pane searchBoxSpacer; @@ -156,6 +154,8 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView @Override public void initialize() { + GUIUtil.applyTableStyle(tableView); + widthListener = (observable, oldValue, newValue) -> onWidthChange((double) newValue); groupIdColumn.setGraphic(new AutoTooltipLabel(ColumnNames.GROUP_ID.toString())); paymentMethodColumn.setGraphic(new AutoTooltipLabel(ColumnNames.PAYMENT_METHOD.toString())); @@ -231,8 +231,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView return row; }); - filterLabel.setText(Res.get("shared.filter")); - HBox.setMargin(filterLabel, new Insets(5, 0, 0, 10)); + filterTextField.setPromptText(Res.get("shared.filter")); filterTextFieldListener = (observable, oldValue, newValue) -> applyFilteredListPredicate(filterTextField.getText()); searchBox.setSpacing(5); HBox.setHgrow(searchBoxSpacer, Priority.ALWAYS); @@ -470,8 +469,8 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView String key = "WithdrawFundsAfterRemoveOfferInfo"; if (DontShowAgainLookup.showAgain(key)) { - new Popup().instruction(Res.get("offerbook.withdrawFundsHint", Res.get("navigation.funds.availableForWithdrawal"))) - .actionButtonTextWithGoTo("navigation.funds.availableForWithdrawal") + new Popup().instruction(Res.get("offerbook.withdrawFundsHint", Res.get("funds.tab.withdrawal"))) + .actionButtonTextWithGoTo("funds.tab.withdrawal") .onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class)) .dontShowAgainId(key) .show(); @@ -527,7 +526,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView private void setOfferIdColumnCellFactory() { offerIdColumn.setCellValueFactory((openOfferListItem) -> new ReadOnlyObjectWrapper<>(openOfferListItem.getValue())); - offerIdColumn.getStyleClass().addAll("number-column", "first-column"); + offerIdColumn.getStyleClass().addAll("number-column"); offerIdColumn.setCellFactory( new Callback<>() { @@ -903,7 +902,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView } private void setRemoveColumnCellFactory() { - removeItemColumn.getStyleClass().addAll("last-column", "avatar-column"); + removeItemColumn.getStyleClass().addAll("avatar-column"); removeItemColumn.setCellValueFactory((offerListItem) -> new ReadOnlyObjectWrapper<>(offerListItem.getValue())); removeItemColumn.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java index 645fbb5014..70b588bb4e 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java @@ -55,6 +55,7 @@ import haveno.desktop.main.shared.ChatView; import haveno.desktop.util.CssTheme; import haveno.desktop.util.DisplayUtils; import haveno.desktop.util.FormBuilder; +import haveno.desktop.util.GUIUtil; import haveno.network.p2p.NodeAddress; import java.util.Comparator; import java.util.HashMap; @@ -171,6 +172,8 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad @Override public void initialize() { + GUIUtil.applyTableStyle(tableView); + priceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.price"))); amountColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode()))); volumeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amount"))); @@ -281,6 +284,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad tableView.setMaxHeight(200); filterBox.initialize(filteredList, tableView); // here because filteredList is instantiated here + filterBox.setPromptText(Res.get("shared.filter")); filterBox.activate(); updateMoveTradeToFailedColumnState(); @@ -607,7 +611,6 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad /////////////////////////////////////////////////////////////////////////////////////////// private void setTradeIdColumnCellFactory() { - tradeIdColumn.getStyleClass().add("first-column"); tradeIdColumn.setCellValueFactory((pendingTradesListItem) -> new ReadOnlyObjectWrapper<>(pendingTradesListItem.getValue())); tradeIdColumn.setCellFactory( new Callback<>() { @@ -821,7 +824,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad @SuppressWarnings("UnusedReturnValue") private TableColumn<PendingTradesListItem, PendingTradesListItem> setAvatarColumnCellFactory() { avatarColumn.setCellValueFactory((trade) -> new ReadOnlyObjectWrapper<>(trade.getValue())); - avatarColumn.getStyleClass().addAll("last-column", "avatar-column"); + avatarColumn.getStyleClass().add("avatar-column"); avatarColumn.setCellFactory( new Callback<>() { @@ -860,7 +863,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad @SuppressWarnings("UnusedReturnValue") private TableColumn<PendingTradesListItem, PendingTradesListItem> setChatColumnCellFactory() { chatColumn.setCellValueFactory((trade) -> new ReadOnlyObjectWrapper<>(trade.getValue())); - chatColumn.getStyleClass().addAll("last-column", "avatar-column"); + chatColumn.getStyleClass().addAll("avatar-column"); chatColumn.setSortable(false); chatColumn.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java index 3af019e6d0..b7ac1b1886 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/TradeStepView.java @@ -125,6 +125,7 @@ public abstract class TradeStepView extends AnchorPane { gridPane.setHgap(Layout.GRID_GAP); gridPane.setVgap(Layout.GRID_GAP); + gridPane.setPadding(new Insets(0, 0, 25, 0)); ColumnConstraints columnConstraints1 = new ColumnConstraints(); columnConstraints1.setHgrow(Priority.ALWAYS); diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java index e589443301..9a328695df 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/buyer/BuyerStep4View.java @@ -142,7 +142,7 @@ public class BuyerStep4View extends TradeStepView { if (!DevEnv.isDevMode()) { UserThread.runAfter(() -> new Popup().headLine(Res.get("portfolio.pending.step5_buyer.tradeCompleted.headline")) .feedback(Res.get("portfolio.pending.step5_buyer.tradeCompleted.msg")) - .actionButtonTextWithGoTo("navigation.portfolio.closedTrades") + .actionButtonTextWithGoTo("portfolio.tab.history") .onAction(() -> model.dataModel.navigation.navigateTo(MainView.class, PortfolioView.class, ClosedTradesView.class)) .dontShowAgainId("tradeCompleteWithdrawCompletedInfo") .show(), 500, TimeUnit.MILLISECONDS); diff --git a/desktop/src/main/java/haveno/desktop/main/settings/SettingsView.java b/desktop/src/main/java/haveno/desktop/main/settings/SettingsView.java index ee147ad376..e2a67428d9 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/SettingsView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/SettingsView.java @@ -56,9 +56,9 @@ public class SettingsView extends ActivatableView<TabPane, Void> { @Override public void initialize() { - preferencesTab.setText(Res.get("settings.tab.preferences").toUpperCase()); - networkTab.setText(Res.get("settings.tab.network").toUpperCase()); - aboutTab.setText(Res.get("settings.tab.about").toUpperCase()); + preferencesTab.setText(Res.get("settings.tab.preferences")); + networkTab.setText(Res.get("settings.tab.network")); + aboutTab.setText(Res.get("settings.tab.about")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(SettingsView.class) == 1) diff --git a/desktop/src/main/java/haveno/desktop/main/settings/about/AboutView.java b/desktop/src/main/java/haveno/desktop/main/settings/about/AboutView.java index e543837f5d..73e51a47da 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/about/AboutView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/about/AboutView.java @@ -19,6 +19,7 @@ package haveno.desktop.main.settings.about; import com.google.inject.Inject; import haveno.common.app.Version; +import haveno.core.filter.FilterManager; import haveno.core.locale.Res; import haveno.desktop.common.view.ActivatableView; import haveno.desktop.common.view.FxmlView; @@ -35,16 +36,18 @@ import javafx.scene.layout.GridPane; @FxmlView public class AboutView extends ActivatableView<GridPane, Void> { +private final FilterManager filterManager; private int gridRow = 0; @Inject - public AboutView() { + public AboutView(FilterManager filterManager) { super(); + this.filterManager = filterManager; } @Override public void initialize() { - addTitledGroupBg(root, gridRow, 4, Res.get("setting.about.aboutHaveno")); + addTitledGroupBg(root, gridRow, 5, Res.get("setting.about.aboutHaveno")); Label label = addLabel(root, gridRow, Res.get("setting.about.about"), Layout.TWICE_FIRST_ROW_DISTANCE); label.setWrapText(true); @@ -77,8 +80,11 @@ public class AboutView extends ActivatableView<GridPane, Void> { if (isXmr) addCompactTopLabelTextField(root, ++gridRow, Res.get("setting.about.feeEstimation.label"), "Monero node"); - addTitledGroupBg(root, ++gridRow, 2, Res.get("setting.about.versionDetails"), Layout.GROUP_DISTANCE); + String minVersion = filterManager.getDisableTradeBelowVersion() == null ? Res.get("shared.none") : filterManager.getDisableTradeBelowVersion(); + + addTitledGroupBg(root, ++gridRow, 3, Res.get("setting.about.versionDetails"), Layout.GROUP_DISTANCE); addCompactTopLabelTextField(root, gridRow, Res.get("setting.about.version"), Version.VERSION, Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE); + addCompactTopLabelTextField(root, ++gridRow, Res.get("filterWindow.disableTradeBelowVersion"), minVersion); addCompactTopLabelTextField(root, ++gridRow, Res.get("setting.about.subsystems.label"), Res.get("setting.about.subsystems.val", diff --git a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml index bbfb4c6e05..d4dcf14085 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml +++ b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.fxml @@ -53,7 +53,7 @@ <PropertyValueFactory property="address"/> </cellValueFactory> </TableColumn> - <TableColumn fx:id="moneroConnectionConnectedColumn" minWidth="80" maxWidth="90"> + <TableColumn fx:id="moneroConnectionConnectedColumn" minWidth="80" maxWidth="110"> <cellValueFactory> <PropertyValueFactory property="connected"/> </cellValueFactory> @@ -108,6 +108,9 @@ </HavenoTextField> <VBox GridPane.rowIndex="6" GridPane.hgrow="ALWAYS" GridPane.vgrow="SOMETIMES"> + <GridPane.margin> + <Insets top="0" right="0" bottom="10" left="0"/> + </GridPane.margin> <AutoTooltipLabel fx:id="p2PPeersLabel" styleClass="small-text"/> <TableView fx:id="p2pPeersTableView"> <columns> @@ -159,10 +162,7 @@ <HavenoTextField fx:id="chainHeightTextField" GridPane.rowIndex="9" editable="false" focusTraversable="false" labelFloat="true"/> - <HavenoTextField fx:id="minVersionForTrading" GridPane.rowIndex="10" editable="false" - focusTraversable="false" labelFloat="true"/> - - <AutoTooltipButton fx:id="openTorSettingsButton" GridPane.rowIndex="11" GridPane.columnIndex="0"/> + <AutoTooltipButton fx:id="openTorSettingsButton" GridPane.rowIndex="10" GridPane.columnIndex="0"/> <columnConstraints> <ColumnConstraints hgrow="ALWAYS" minWidth="500"/> diff --git a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java index a767d032bc..4d1a6cde01 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/network/NetworkSettingsView.java @@ -75,7 +75,7 @@ public class NetworkSettingsView extends ActivatableView<GridPane, Void> { @FXML InputTextField xmrNodesInputTextField; @FXML - TextField onionAddress, sentDataTextField, receivedDataTextField, chainHeightTextField, minVersionForTrading; + TextField onionAddress, sentDataTextField, receivedDataTextField, chainHeightTextField; @FXML Label p2PPeersLabel, moneroConnectionsLabel; @FXML @@ -149,6 +149,14 @@ public class NetworkSettingsView extends ActivatableView<GridPane, Void> { @Override public void initialize() { + GUIUtil.applyTableStyle(p2pPeersTableView); + GUIUtil.applyTableStyle(moneroConnectionsTableView); + + onionAddress.getStyleClass().add("label-float"); + sentDataTextField.getStyleClass().add("label-float"); + receivedDataTextField.getStyleClass().add("label-float"); + chainHeightTextField.getStyleClass().add("label-float"); + btcHeader.setText(Res.get("settings.net.xmrHeader")); p2pHeader.setText(Res.get("settings.net.p2pHeader")); onionAddress.setPromptText(Res.get("settings.net.onionAddressLabel")); @@ -160,7 +168,6 @@ public class NetworkSettingsView extends ActivatableView<GridPane, Void> { useTorForXmrOnRadio.setText(Res.get("settings.net.useTorForXmrOnRadio")); moneroNodesLabel.setText(Res.get("settings.net.moneroNodesLabel")); moneroConnectionAddressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); - moneroConnectionAddressColumn.getStyleClass().add("first-column"); moneroConnectionConnectedColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.connection"))); localhostXmrNodeInfoLabel.setText(Res.get("settings.net.localhostXmrNodeInfo")); useProvidedNodesRadio.setText(Res.get("settings.net.useProvidedNodesRadio")); @@ -170,18 +177,15 @@ public class NetworkSettingsView extends ActivatableView<GridPane, Void> { rescanOutputsButton.updateText(Res.get("settings.net.rescanOutputsButton")); p2PPeersLabel.setText(Res.get("settings.net.p2PPeersLabel")); onionAddressColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.onionAddressColumn"))); - onionAddressColumn.getStyleClass().add("first-column"); creationDateColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.creationDateColumn"))); connectionTypeColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.connectionTypeColumn"))); sentDataTextField.setPromptText(Res.get("settings.net.sentDataLabel")); receivedDataTextField.setPromptText(Res.get("settings.net.receivedDataLabel")); chainHeightTextField.setPromptText(Res.get("settings.net.chainHeightLabel")); - minVersionForTrading.setPromptText(Res.get("filterWindow.disableTradeBelowVersion")); roundTripTimeColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.roundTripTimeColumn"))); sentBytesColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.sentBytesColumn"))); receivedBytesColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.receivedBytesColumn"))); peerTypeColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.peerTypeColumn"))); - peerTypeColumn.getStyleClass().add("last-column"); openTorSettingsButton.updateText(Res.get("settings.net.openTorSettingsButton")); // TODO: hiding button to rescan outputs until supported @@ -504,10 +508,6 @@ public class NetworkSettingsView extends ActivatableView<GridPane, Void> { selectMoneroPeersToggle(); onMoneroPeersToggleSelected(false); } - - // set min version for trading - String minVersion = filterManager.getDisableTradeBelowVersion(); - minVersionForTrading.textProperty().setValue(minVersion == null ? Res.get("shared.none") : minVersion); } private boolean isPublicNodesDisabled() { diff --git a/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java b/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java index da71490b9a..e12e4d4bc0 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java @@ -102,7 +102,7 @@ import org.apache.commons.lang3.StringUtils; @FxmlView public class PreferencesView extends ActivatableViewAndModel<GridPane, PreferencesViewModel> { private final User user; - private TextField btcExplorerTextField; + private TextField xmrExplorerTextField; private ComboBox<String> userLanguageComboBox; private ComboBox<Country> userCountryComboBox; private ComboBox<TradeCurrency> preferredTradeCurrencyComboBox; @@ -220,9 +220,9 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc userCountryComboBox.setButtonCell(GUIUtil.getComboBoxButtonCell(Res.get("shared.country"), userCountryComboBox, false)); - Tuple2<TextField, Button> btcExp = addTextFieldWithEditButton(root, ++gridRow, Res.get("setting.preferences.explorer")); - btcExplorerTextField = btcExp.first; - editCustomBtcExplorer = btcExp.second; + Tuple2<TextField, Button> xmrExp = addTextFieldWithEditButton(root, ++gridRow, Res.get("setting.preferences.explorer")); + xmrExplorerTextField = xmrExp.first; + editCustomBtcExplorer = xmrExp.second; // deviation deviationInputTextField = addInputTextField(root, ++gridRow, @@ -688,7 +688,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc } }); - btcExplorerTextField.setText(preferences.getBlockChainExplorer().name); + xmrExplorerTextField.setText(preferences.getBlockChainExplorer().name); deviationInputTextField.setText(FormattingUtils.formatToPercentWithSymbol(preferences.getMaxPriceDistanceInPercent())); deviationInputTextField.textProperty().addListener(deviationListener); @@ -779,7 +779,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc .actionButtonText(Res.get("shared.save")) .onAction(() -> { preferences.setBlockChainExplorer(urlWindow.getEditedBlockChainExplorer()); - btcExplorerTextField.setText(preferences.getBlockChainExplorer().name); + xmrExplorerTextField.setText(preferences.getBlockChainExplorer().name); }) .closeButtonText(Res.get("shared.cancel")) .onClose(urlWindow::hide) diff --git a/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java b/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java index 236f8471e9..e2ea0bb1c9 100644 --- a/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java +++ b/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java @@ -26,7 +26,7 @@ import haveno.desktop.main.overlays.notifications.Notification; import haveno.desktop.main.overlays.popups.Popup; import haveno.desktop.util.DisplayUtils; import haveno.desktop.util.GUIUtil; - +import haveno.desktop.util.Layout; import haveno.core.locale.Res; import haveno.core.support.SupportManager; import haveno.core.support.SupportSession; @@ -43,7 +43,8 @@ import com.google.common.io.ByteStreams; import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; - +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView; import javafx.stage.FileChooser; import javafx.scene.Node; @@ -204,6 +205,7 @@ public class ChatView extends AnchorPane { inputTextArea = new HavenoTextArea(); inputTextArea.setPrefHeight(70); inputTextArea.setWrapText(true); + inputTextArea.getStyleClass().add("input-with-border"); if (!supportSession.isDisputeAgent()) { inputTextArea.setPromptText(Res.get("support.input.prompt")); @@ -271,7 +273,7 @@ public class ChatView extends AnchorPane { ImageView arrow = new ImageView(); Label headerLabel = new AutoTooltipLabel(); Label messageLabel = new AutoTooltipLabel(); - Label copyIcon = new Label(); + Label copyLabel = new Label(); HBox attachmentsBox = new HBox(); AnchorPane messageAnchorPane = new AnchorPane(); Label statusIcon = new Label(); @@ -292,10 +294,10 @@ public class ChatView extends AnchorPane { statusIcon.getStyleClass().add("small-text"); statusInfoLabel.getStyleClass().add("small-text"); statusInfoLabel.setPadding(new Insets(3, 0, 0, 0)); - copyIcon.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); + copyLabel.setTooltip(new Tooltip(Res.get("shared.copyToClipboard"))); statusHBox.setSpacing(5); statusHBox.getChildren().addAll(statusIcon, statusInfoLabel); - messageAnchorPane.getChildren().addAll(bg, arrow, headerLabel, messageLabel, copyIcon, attachmentsBox, statusHBox); + messageAnchorPane.getChildren().addAll(bg, arrow, headerLabel, messageLabel, copyLabel, attachmentsBox, statusHBox); } @Override @@ -303,7 +305,13 @@ public class ChatView extends AnchorPane { UserThread.execute(() -> { super.updateItem(message, empty); if (message != null && !empty) { - copyIcon.setOnMouseClicked(e -> Utilities.copyToClipboard(messageLabel.getText())); + copyLabel.setOnMouseClicked(e -> { + Utilities.copyToClipboard(messageLabel.getText()); + Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard")); + Node node = (Node) e.getSource(); + UserThread.runAfter(() -> tp.hide(), 1); + tp.show(node, e.getScreenX() + Layout.PADDING, e.getScreenY() + Layout.PADDING); + }); messageLabel.setOnMouseClicked(event -> { if (2 > event.getClickCount()) { return; @@ -319,7 +327,7 @@ public class ChatView extends AnchorPane { AnchorPane.clearConstraints(headerLabel); AnchorPane.clearConstraints(arrow); AnchorPane.clearConstraints(messageLabel); - AnchorPane.clearConstraints(copyIcon); + AnchorPane.clearConstraints(copyLabel); AnchorPane.clearConstraints(statusHBox); AnchorPane.clearConstraints(attachmentsBox); @@ -328,7 +336,7 @@ public class ChatView extends AnchorPane { AnchorPane.setTopAnchor(headerLabel, 0d); AnchorPane.setBottomAnchor(arrow, bottomBorder + 5d); AnchorPane.setTopAnchor(messageLabel, 25d); - AnchorPane.setTopAnchor(copyIcon, 25d); + AnchorPane.setTopAnchor(copyLabel, 25d); AnchorPane.setBottomAnchor(attachmentsBox, bottomBorder + 10); boolean senderIsTrader = message.isSenderIsTrader(); @@ -341,20 +349,20 @@ public class ChatView extends AnchorPane { headerLabel.getStyleClass().removeAll("message-header", "my-message-header", "success-text", "highlight-static"); messageLabel.getStyleClass().removeAll("my-message", "message"); - copyIcon.getStyleClass().removeAll("my-message", "message"); + copyLabel.getStyleClass().removeAll("my-message", "message"); if (message.isSystemMessage()) { headerLabel.getStyleClass().addAll("message-header", "success-text"); bg.setId("message-bubble-green"); messageLabel.getStyleClass().add("my-message"); - copyIcon.getStyleClass().add("my-message"); + copyLabel.getStyleClass().add("my-message"); message.addWeakMessageStateListener(() -> UserThread.execute(() -> updateMsgState(message))); updateMsgState(message); } else if (isMyMsg) { headerLabel.getStyleClass().add("my-message-header"); bg.setId("message-bubble-blue"); messageLabel.getStyleClass().add("my-message"); - copyIcon.getStyleClass().add("my-message"); + copyLabel.getStyleClass().add("my-message"); if (supportSession.isClient()) arrow.setId("bubble_arrow_blue_left"); else @@ -375,7 +383,7 @@ public class ChatView extends AnchorPane { headerLabel.getStyleClass().add("message-header"); bg.setId("message-bubble-grey"); messageLabel.getStyleClass().add("message"); - copyIcon.getStyleClass().add("message"); + copyLabel.getStyleClass().add("message"); if (supportSession.isClient()) arrow.setId("bubble_arrow_grey_right"); else @@ -389,7 +397,7 @@ public class ChatView extends AnchorPane { AnchorPane.setRightAnchor(bg, border); AnchorPane.setLeftAnchor(messageLabel, padding); AnchorPane.setRightAnchor(messageLabel, msgLabelPaddingRight); - AnchorPane.setRightAnchor(copyIcon, padding); + AnchorPane.setRightAnchor(copyLabel, padding); AnchorPane.setLeftAnchor(attachmentsBox, padding); AnchorPane.setRightAnchor(attachmentsBox, padding); AnchorPane.setLeftAnchor(statusHBox, padding); @@ -400,7 +408,7 @@ public class ChatView extends AnchorPane { AnchorPane.setLeftAnchor(arrow, border); AnchorPane.setLeftAnchor(messageLabel, padding + arrowWidth); AnchorPane.setRightAnchor(messageLabel, msgLabelPaddingRight); - AnchorPane.setRightAnchor(copyIcon, padding); + AnchorPane.setRightAnchor(copyLabel, padding); AnchorPane.setLeftAnchor(attachmentsBox, padding + arrowWidth); AnchorPane.setRightAnchor(attachmentsBox, padding); AnchorPane.setRightAnchor(statusHBox, padding); @@ -411,7 +419,7 @@ public class ChatView extends AnchorPane { AnchorPane.setRightAnchor(arrow, border); AnchorPane.setLeftAnchor(messageLabel, padding); AnchorPane.setRightAnchor(messageLabel, msgLabelPaddingRight + arrowWidth); - AnchorPane.setRightAnchor(copyIcon, padding + arrowWidth); + AnchorPane.setRightAnchor(copyLabel, padding + arrowWidth); AnchorPane.setLeftAnchor(attachmentsBox, padding); AnchorPane.setRightAnchor(attachmentsBox, padding + arrowWidth); AnchorPane.setLeftAnchor(statusHBox, padding); @@ -454,8 +462,9 @@ public class ChatView extends AnchorPane { } // Need to set it here otherwise style is not correct - AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY, "16.0"); - copyIcon.getStyleClass().addAll("icon", "copy-icon-disputes"); + copyLabel.getStyleClass().addAll("icon", "copy-icon-disputes"); + MaterialDesignIconView copyIcon = new MaterialDesignIconView(MaterialDesignIcon.CONTENT_COPY, "16.0"); + copyLabel.setGraphic(copyIcon); // TODO There are still some cell rendering issues on updates setGraphic(messageAnchorPane); @@ -465,7 +474,7 @@ public class ChatView extends AnchorPane { messageAnchorPane.prefWidthProperty().unbind(); - copyIcon.setOnMouseClicked(null); + copyLabel.setOnMouseClicked(null); messageLabel.setOnMouseClicked(null); setGraphic(null); } diff --git a/desktop/src/main/java/haveno/desktop/main/support/SupportView.java b/desktop/src/main/java/haveno/desktop/main/support/SupportView.java index 42503f315f..12a000c00b 100644 --- a/desktop/src/main/java/haveno/desktop/main/support/SupportView.java +++ b/desktop/src/main/java/haveno/desktop/main/support/SupportView.java @@ -139,9 +139,9 @@ public class SupportView extends ActivatableView<TabPane, Void> { // Has to be called before loadView updateAgentTabs(); - tradersMediationDisputesTab.setText(Res.get("support.tab.mediation.support").toUpperCase()); - tradersRefundDisputesTab.setText(Res.get("support.tab.refund.support").toUpperCase()); - tradersArbitrationDisputesTab.setText(Res.get("support.tab.arbitration.support").toUpperCase()); + tradersMediationDisputesTab.setText(Res.get("support.tab.mediation.support")); + tradersRefundDisputesTab.setText(Res.get("support.tab.refund.support")); + tradersArbitrationDisputesTab.setText(Res.get("support.tab.arbitration.support")); navigationListener = (viewPath, data) -> { if (viewPath.size() == 3 && viewPath.indexOf(SupportView.class) == 1) @@ -221,16 +221,16 @@ public class SupportView extends ActivatableView<TabPane, Void> { // We might get that method called before we have the map is filled in the arbitratorManager if (arbitratorTab != null) { - arbitratorTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.arbitrator")).toUpperCase()); + arbitratorTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.arbitrator"))); } if (signedOfferTab != null) { - signedOfferTab.setText(Res.get("support.tab.SignedOffers").toUpperCase()); + signedOfferTab.setText(Res.get("support.tab.SignedOffers")); } if (mediatorTab != null) { - mediatorTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.mediator")).toUpperCase()); + mediatorTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.mediator"))); } if (refundAgentTab != null) { - refundAgentTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.refundAgentForSupportStaff")).toUpperCase()); + refundAgentTab.setText(Res.get("support.tab.ArbitratorsSupportTickets", Res.get("shared.refundAgentForSupportStaff"))); } } diff --git a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java index 5af027c16c..87c05c997c 100644 --- a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java +++ b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java @@ -223,12 +223,8 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements @Override public void initialize() { - Label label = new AutoTooltipLabel(Res.get("support.filter")); - HBox.setMargin(label, new Insets(5, 0, 0, 0)); - HBox.setHgrow(label, Priority.NEVER); - filterTextField = new InputTextField(); - filterTextField.setPromptText(Res.get("support.filter.prompt")); + filterTextField.setPromptText(Res.get("shared.filter")); Tooltip tooltip = new Tooltip(); tooltip.setShowDelay(Duration.millis(100)); tooltip.setShowDuration(Duration.seconds(10)); @@ -298,8 +294,7 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements HBox filterBox = new HBox(); filterBox.setSpacing(5); - filterBox.getChildren().addAll(label, - filterTextField, + filterBox.getChildren().addAll(filterTextField, alertIconLabel, spacer, reOpenButton, @@ -311,6 +306,7 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements VBox.setVgrow(filterBox, Priority.NEVER); tableView = new TableView<>(); + GUIUtil.applyTableStyle(tableView); VBox.setVgrow(tableView, Priority.SOMETIMES); tableView.setMinHeight(150); @@ -957,7 +953,6 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements { setMaxWidth(80); setMinWidth(65); - getStyleClass().addAll("first-column", "avatar-column"); setSortable(false); } }; @@ -1354,7 +1349,6 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> implements setMinWidth(50); } }; - column.getStyleClass().add("last-column"); column.setCellValueFactory((dispute) -> new ReadOnlyObjectWrapper<>(dispute.getValue())); column.setCellFactory( new Callback<>() { diff --git a/desktop/src/main/java/haveno/desktop/main/support/dispute/agent/DisputeAgentView.java b/desktop/src/main/java/haveno/desktop/main/support/dispute/agent/DisputeAgentView.java index 10c657dce7..5901d48769 100644 --- a/desktop/src/main/java/haveno/desktop/main/support/dispute/agent/DisputeAgentView.java +++ b/desktop/src/main/java/haveno/desktop/main/support/dispute/agent/DisputeAgentView.java @@ -208,7 +208,6 @@ public abstract class DisputeAgentView extends DisputeView implements MultipleHo protected void setupTable() { super.setupTable(); - stateColumn.getStyleClass().remove("last-column"); tableView.getColumns().add(getAlertColumn()); } @@ -243,7 +242,6 @@ public abstract class DisputeAgentView extends DisputeView implements MultipleHo setMinWidth(50); } }; - column.getStyleClass().add("last-column"); column.setCellValueFactory((dispute) -> new ReadOnlyObjectWrapper<>(dispute.getValue())); column.setCellFactory( c -> new TableCell<>() { diff --git a/desktop/src/main/java/haveno/desktop/theme-dark.css b/desktop/src/main/java/haveno/desktop/theme-dark.css index d045e3b5b0..1e56814261 100644 --- a/desktop/src/main/java/haveno/desktop/theme-dark.css +++ b/desktop/src/main/java/haveno/desktop/theme-dark.css @@ -20,10 +20,10 @@ /* haveno main colors */ -bs-color-primary: #0b65da; -bs-color-primary-dark: #0c59bd; - -bs-text-color: #dadada; - -bs-background-color: #29292a; - -bs-background-gray: #2B2B2B; - -bs-content-background-gray: #1F1F1F; + -bs-text-color: white; + -bs-background-color: black; + -bs-background-gray: transparent; + -bs-content-background-gray: black; /* fifty shades of gray */ -bs-color-gray-13: #bbb; @@ -43,7 +43,19 @@ -bs-color-gray-bbb: #5a5a5a; -bs-color-gray-aaa: #29292a; -bs-color-gray-fafa: #0a0a0a; - -bs-color-gray-background: #1F1F1F; + -bs-color-gray-background: black; + -bs-color-background-popup: rgb(38, 38, 38); + -bs-color-background-popup-blur: rgb(9, 9, 9); + -bs-color-background-popup-input: rgb(9, 9, 9); + -bs-color-background-form-field: rgb(26, 26, 26); + -bs-color-background-form-field-readonly: rgb(18, 18, 18); + -bs-color-border-form-field: rgb(65, 65, 65); + -bs-color-background-pane: rgb(15, 15, 15); + -bs-color-background-row-even: rgb(19, 19, 19); + -bs-color-background-row-odd: rgb(9, 9, 9); + -bs-color-table-cell-dim: -bs-color-gray-ccc; + -bs-text-color-dim1: rgb(87, 87, 87); + -bs-text-color-dim2: rgb(130, 130, 130); /* lesser used colors */ -bs-color-blue-5: #0a4576; @@ -70,11 +82,15 @@ -bs-rd-nav-border: #535353; -bs-rd-nav-primary-border: rgba(0, 0, 0, 0); -bs-rd-nav-border-color: rgba(255, 255, 255, 0.1); - -bs-rd-nav-background: #141414; - -bs-rd-nav-primary-background: rgba(255, 255, 255, 0.015); - -bs-rd-nav-selected: #fff; - -bs-rd-nav-deselected: rgba(255, 255, 255, 0.45); - -bs-rd-nav-button-hover: rgba(255, 255, 255, 0.03); + -bs-rd-nav-background: rgb(15, 15, 15); + -bs-rd-nav-primary-background: rgb(15, 15, 15); + -bs-rd-nav-selected: black; + -bs-rd-nav-deselected: rgba(255, 255, 255, 1); + -bs-rd-nav-secondary-selected: -fx-accent; + -bs-rd-nav-secondary-deselected: -bs-rd-font-light; + -bs-rd-nav-button-hover: derive(-bs-rd-nav-background, 10%); + -bs-rd-nav-primary-button-hover: derive(-bs-rd-nav-primary-background, 10%); + -bs-rd-nav-hover-text: black; -bs-content-pane-bg-top: #212121; -bs-rd-tab-border: rgba(255, 255, 255, 0.00); @@ -90,7 +106,7 @@ -bs-footer-pane-text: #cfcecf; -bs-footer-pane-line: #29292a; - -bs-rd-font-balance: #bbbbbb; + -bs-rd-font-balance: white; -bs-rd-font-dark-gray: #d4d4d4; -bs-rd-font-dark: #cccccc; -bs-rd-font-light: #b4b4b4; @@ -99,28 +115,28 @@ -bs-rd-font-confirmation-label: #504f52; -bs-rd-font-balance-label: #999999; - -bs-text-color-transparent-dark: rgba(29, 29, 33, 0.54); + -bs-text-color-dropshadow: rgba(45, 45, 49, .75); + -bs-text-color-dropshadow-light-mode: transparent; -bs-text-color-transparent: rgba(29, 29, 33, 0.2); -bs-color-gray-line: #504f52; -bs-rd-separator: #1F1F1F; - -bs-rd-separator-dark: #1F1F1F; + -bs-rd-separator-dark: rgb(255, 255, 255, 0.1); -bs-rd-error-red: #d83431; -bs-rd-error-field: #521C1C; -bs-rd-message-bubble: #0086c6; -bs-rd-tooltip-truncated: #afaeb0; - -bs-toggle-selected: #25b135; + /*-bs-toggle-selected: rgb(12, 89, 189);*/ + -bs-toggle-selected: rgb(12, 89, 190); -bs-warning: #db6300; - -bs-buy: #006600; - -bs-buy-focus: black; - -bs-buy-hover: #237b2d; - -bs-buy-transparent: rgba(46, 163, 60, 0.3); - -bs-sell: #660000; - -bs-sell-focus: #090202; - -bs-sell-hover: #b42522; - -bs-sell-transparent: rgba(216, 52, 49, 0.3); - -bs-volume-transparent: rgba(37, 177, 54, 0.5); + -bs-buy: rgb(80, 180, 90); + -bs-buy-focus: derive(-bs-buy, -50%); + -bs-buy-hover: derive(-bs-buy, -10%); + -bs-sell: rgb(213, 63, 46); + -bs-sell-focus: derive(-bs-sell, -50%); + -bs-sell-hover: derive(-bs-sell, -10%); + -bs-volume-transparent: -bs-buy; -bs-candle-stick-average-line: rgba(21, 188, 29, 0.8); -bs-candle-stick-loss: #ee6563; -bs-candle-stick-won: #15bc1d; @@ -154,7 +170,7 @@ /* Monero orange color code */ -xmr-orange: #f26822; - -bs-support-chat-background: #cccccc; + -bs-support-chat-background: rgb(125, 125, 125); } /* table view */ @@ -164,7 +180,7 @@ } .table-view .column-header { - -fx-background-color: derive(-bs-background-color,-50%); + -fx-background-color: -bs-color-background-pane; -fx-border-width: 0; } @@ -173,21 +189,31 @@ -fx-border-width: 0; } +/** These must be set to override default styles */ .table-view .table-row-cell:even .table-cell { - -fx-background-color: derive(-bs-background-color, -5%); - -fx-border-color: derive(-bs-background-color, -5%); + -fx-background-color: -bs-color-background-row-even; + -fx-border-color: -bs-color-background-row-even; } - .table-view .table-row-cell:odd .table-cell { - -fx-background-color: derive(-bs-background-color,-30%); - -fx-border-color: derive(-bs-background-color,-30%); + -fx-background-color: -bs-color-background-row-odd; + -fx-border-color: -bs-color-background-row-odd; } - .table-view .table-row-cell:selected .table-cell { -fx-background: -fx-accent; -fx-background-color: -fx-selection-bar; -fx-border-color: -fx-selection-bar; } +.table-view .table-row-cell:selected .table-cell, +.table-view .table-row-cell:selected .table-cell .label, +.table-view .table-row-cell:selected .table-cell .text { + -fx-text-fill: -fx-dark-text-color; +} +.table-view .table-row-cell:selected .table-cell .hyperlink, +.table-view .table-row-cell:selected .table-cell .hyperlink .text, +.table-view .table-row-cell:selected .table-cell .hyperlink-with-icon, +.table-view .table-row-cell:selected .table-cell .hyperlink-with-icon .text { + -fx-fill: -fx-dark-text-color; +} .table-row-cell { -fx-border-color: -bs-background-color; @@ -208,35 +234,49 @@ -fx-background-color: -bs-tab-content-area; } -.jfx-tab-pane .viewport { - -fx-background-color: -bs-viewport-background; + +.jfx-tab-pane .headers-region .tab:selected .tab-container .tab-label { + -fx-text-fill: white; } -.jfx-tab-pane .tab-header-background { - -fx-background-color: derive(-bs-color-gray-background, -20%); +.nav-secondary-button:selected .text { + -fx-fill: white; +} + +.jfx-tab-pane .headers-region > .tab > .jfx-rippler { + -jfx-rippler-fill: none; +} + +.jfx-tab-pane .viewport { + -fx-background-color: -bs-viewport-background; } /* text field */ .jfx-text-field, .jfx-text-area, .jfx-combo-box, .jfx-combo-box > .list-cell { - -fx-background-color: derive(-bs-background-color, 15%); -fx-prompt-text-fill: -bs-color-gray-6; -fx-text-fill: -bs-color-gray-12; } -.jfx-text-area:readonly, .jfx-text-field:readonly, +.jfx-text-area:readonly, +.jfx-text-field:readonly, .hyperlink-with-icon { -fx-background: -bs-background-color; - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-form-field-readonly; -fx-prompt-text-fill: -bs-color-gray-2; -fx-text-fill: -bs-color-gray-3; } + +.popover > .content .text-field { + -fx-background-color: -bs-color-background-form-field !important; +} + .jfx-combo-box > .text, .jfx-text-field-top-label, .jfx-text-area-top-label { -fx-text-fill: -bs-color-gray-11; } -.input-with-border { +.offer-input { -fx-border-color: -bs-color-gray-2; -fx-border-width: 0 0 10 0; } @@ -254,11 +294,6 @@ -fx-text-fill: -fx-dark-text-color; } -.chart-pane, .chart-plot-background, -#charts .chart-plot-background, -#charts-dao .chart-plot-background { - -fx-background-color: transparent; -} .axis:top, .axis:right, .axis:bottom, .axis:left { -fx-border-color: transparent transparent transparent transparent; } @@ -332,7 +367,7 @@ } .combo-box-popup > .list-view{ - -fx-background-color: -bs-background-color; + -fx-background-color: -bs-color-background-pane; } .jfx-combo-box > .arrow-button > .arrow { @@ -352,7 +387,6 @@ } .list-view .list-cell:odd, .list-view .list-cell:even { - -fx-background-color: -bs-background-color; -fx-border-width: 0; } @@ -371,18 +405,6 @@ -fx-border-width: 0; } -.jfx-text-field { - -fx-background-radius: 4; -} - -.jfx-text-field > .input-line { - -fx-translate-x: 0; -} - -.jfx-text-field > .input-focused-line { - -fx-translate-x: 0; -} - .jfx-text-field-top-label { -fx-text-fill: -bs-color-gray-dim; } @@ -394,14 +416,13 @@ -fx-background-color: derive(-bs-background-color, 15%); } .jfx-combo-box:error, -.jfx-text-field:error{ +.jfx-text-field:error { -fx-text-fill: -bs-rd-error-red; -fx-background-color: -bs-rd-error-field; } .jfx-combo-box:error:focused, .jfx-text-field:error:focused{ - -fx-text-fill: -bs-rd-error-red; -fx-background-color: derive(-bs-rd-error-field, -5%); } @@ -417,11 +438,7 @@ -jfx-disable-animation: true; } -.jfx-password-field { - -fx-background-color: derive(-bs-background-color, -15%); -} - -.input-with-border { +.offer-input { -fx-border-width: 0; -fx-border-color: -bs-background-color; } @@ -448,11 +465,6 @@ -jfx-disable-animation: true; } -.top-navigation { - -fx-border-width: 0 0 0 0; - -fx-padding: 0 7 0 0; -} - .nav-price-balance { -fx-effect: null; } @@ -462,37 +474,17 @@ } .nav-button:selected { - -fx-background-color: derive(-bs-color-primary-dark, -10%); + -fx-background-color: white; -fx-effect: null; } -.nav-button:hover { - -fx-background-color: -bs-rd-nav-button-hover; -} - .nav-primary .nav-button:selected { - -fx-background-color: derive(-bs-color-primary-dark, -5%); + -fx-background-color: derive(white, -5%); } .table-view { -fx-border-color: transparent; } -.table-view .table-cell { - -fx-padding: 6 0 4 0; - -fx-text-fill: -bs-text-color; -} -.table-view .table-cell.last-column { - -fx-padding: 6 10 4 0; -} - -.table-view .table-cell.last-column.avatar-column { - -fx-padding: 6 0 4 0; -} - -.table-view .table-cell.first-column { - -fx-padding: 6 0 4 10; -} - .jfx-tab-pane .headers-region .tab .tab-container .tab-label { -fx-cursor: hand; -jfx-disable-animation: true; @@ -559,12 +551,95 @@ } .toggle-button-no-slider { - -fx-focus-color: transparent; - -fx-faint-focus-color: transparent; - -fx-background-radius: 3; - -fx-background-insets: 0, 1; + -fx-background-color: -bs-color-background-form-field; } .toggle-button-no-slider:selected { + -fx-text-fill: white; + -fx-background-color: -bs-color-gray-ccc; + -fx-border-color: -bs-color-gray-ccc; + -fx-border-width: 1px; +} + +.toggle-button-no-slider:hover { + -fx-cursor: hand; + -fx-background-color: -bs-color-gray-ddd; + -fx-border-color: -bs-color-gray-ddd; +} + +.toggle-button-no-slider:selected:hover { + -fx-cursor: hand; + -fx-background-color: -bs-color-gray-3; + -fx-border-color: -bs-color-gray-3; +} + +.toggle-button-no-slider:pressed, .toggle-button-no-slider:selected:hover:pressed { -fx-background-color: -bs-color-gray-bbb; } + +#image-logo-splash { + -fx-image: url("../../images/logo_splash_dark.png"); +} + +#image-logo-splash-testnet { + -fx-image: url("../../images/logo_splash_testnet_dark.png"); +} + +#image-logo-landscape { + -fx-image: url("../../images/logo_landscape_dark.png"); +} + +.table-view .placeholder { + -fx-background-color: -bs-color-background-pane; +} + +#charts .default-color0.chart-series-area-fill { + -fx-fill: linear-gradient(to bottom, + rgba(80, 181, 90, 0.45) 0%, + rgba(80, 181, 90, 0.0) 100% + ); +} + +#charts .default-color1.chart-series-area-fill { + -fx-fill: linear-gradient(to bottom, + rgba(213, 63, 46, 0.45) 0%, + rgba(213, 63, 46, 0.0) 100% + ); +} + +.table-view .table-row-cell .label { + -fx-text-fill: -bs-text-color; +} + +.table-view.non-interactive-table .table-cell, +.table-view.non-interactive-table .table-cell .label, +.table-view.non-interactive-table .label, +.table-view.non-interactive-table .text, +.table-view.non-interactive-table .hyperlink, +.table-view.non-interactive-table .hyperlink-with-icon, +.table-view.non-interactive-table .table-row-cell .hyperlink .text { + -fx-text-fill: -bs-color-gray-dim; +} + +.table-view.non-interactive-table .hyperlink, +.table-view.non-interactive-table .hyperlink-with-icon, +.table-view.non-interactive-table .table-row-cell .hyperlink .text { + -fx-fill: -bs-color-gray-dim; +} + +.table-view.non-interactive-table .table-cell.highlight-text, +.table-view.non-interactive-table .table-cell.highlight-text .label, +.table-view.non-interactive-table .table-cell.highlight-text .text, +.table-view.non-interactive-table .table-cell.highlight-text .hyperlink, +.table-view.non-interactive-table .table-cell.highlight-text .hyperlink .text { + -fx-text-fill: -fx-dark-text-color; +} + +/* Match specificity to override. */ +.table-view.non-interactive-table .table-cell.highlight-text .zero-decimals { + -fx-text-fill: -bs-color-gray-3; +} + +.regular-text-color { + -fx-text-fill: -bs-text-color; +} diff --git a/desktop/src/main/java/haveno/desktop/theme-light.css b/desktop/src/main/java/haveno/desktop/theme-light.css index 7605eb1819..160a21e3c3 100644 --- a/desktop/src/main/java/haveno/desktop/theme-light.css +++ b/desktop/src/main/java/haveno/desktop/theme-light.css @@ -41,12 +41,17 @@ -bs-rd-green: #0b65da; -bs-rd-green-dark: #3EA34A; -bs-rd-nav-selected: #0b65da; - -bs-rd-nav-deselected: rgba(255, 255, 255, 0.75); - -bs-rd-nav-background: #0c59bd; + -bs-rd-nav-deselected: rgba(255, 255, 255, 1); + -bs-rd-nav-secondary-selected: -fx-accent; + -bs-rd-nav-secondary-deselected: -bs-rd-font-light; + -bs-rd-nav-background: #0b65da; -bs-rd-nav-primary-background: #0b65da; + -bs-rd-nav-button-hover: derive(-bs-rd-nav-background, 10%); + -bs-rd-nav-primary-button-hover: derive(-bs-rd-nav-primary-background, 10%); -bs-rd-nav-primary-border: #0B65DA; -bs-rd-nav-border: #535353; -bs-rd-nav-border-color: rgba(255, 255, 255, 0.31); + -bs-rd-nav-hover-text: white; -bs-rd-tab-border: #e2e0e0; -bs-tab-content-area: #ffffff; -bs-color-gray-background: #f2f2f2; @@ -58,32 +63,31 @@ -bs-footer-pane-background: #dddddd; -bs-footer-pane-text: #4b4b4b; -bs-footer-pane-line: #bbb; - -bs-rd-font-balance: #4f4f4f; + -bs-rd-font-balance: white; -bs-rd-font-dark-gray: #3c3c3c; -bs-rd-font-dark: #4b4b4b; -bs-rd-font-light: #8d8d8d; -bs-rd-font-lighter: #a7a7a7; -bs-rd-font-confirmation-label: #504f52; - -bs-rd-font-balance-label: #8e8e8e; - -bs-text-color-transparent-dark: rgba(0, 0, 0, 0.54); + -bs-rd-font-balance-label: #bbbbbb; + -bs-text-color-dropshadow: rgba(0, 0, 0, 0.54); + -bs-text-color-dropshadow-light-mode: rgba(0, 0, 0, 0.54); -bs-text-color-transparent: rgba(0, 0, 0, 0.2); -bs-color-gray-line: #979797; -bs-rd-separator: #dbdbdb; - -bs-rd-separator-dark: #d5e0d6; + -bs-rd-separator-dark: rgb(255, 255, 255, 0.1); -bs-rd-error-red: #dd0000; -bs-rd-message-bubble: #0086c6; -bs-toggle-selected: #7b7b7b; -bs-rd-tooltip-truncated: #0a0a0a; -bs-warning: #ff8a2b; - -bs-buy: #3ea34a; + -bs-buy: rgb(80, 180, 90); -bs-buy-focus: derive(-bs-buy, -50%); -bs-buy-hover: derive(-bs-buy, -10%); - -bs-buy-transparent: rgba(62, 163, 74, 0.3); - -bs-sell: #d73030; + -bs-sell: rgb(213, 63, 46); -bs-sell-focus: derive(-bs-sell, -50%); -bs-sell-hover: derive(-bs-sell, -10%); - -bs-sell-transparent: rgba(215, 48, 48, 0.3); - -bs-volume-transparent: rgba(37, 177, 53, 0.3); + -bs-volume-transparent: -bs-buy; -bs-candle-stick-average-line: -bs-rd-green; -bs-candle-stick-loss: #fe3001; -bs-candle-stick-won: #20b221; @@ -104,6 +108,19 @@ -bs-prompt-text: -fx-control-inner-background; -bs-soft-red: #aa4c3b; -bs-turquoise-light: #11eeee; + -bs-color-border-form-field: -bs-background-gray; + -bs-color-background-form-field-readonly: -bs-color-gray-1; + -bs-color-background-pane: -bs-background-color; + -bs-color-background-row-even: -bs-color-background-pane; + -bs-color-background-row-odd: derive(-bs-color-background-pane, -6%); + -bs-color-table-cell-dim: -bs-color-gray-ccc; + -bs-color-background-popup: white; + -bs-color-background-popup-blur: white; + -bs-color-background-popup-input: -bs-color-gray-background; + -bs-color-background-form-field: white; + -bs-text-color-dim1: black; + -bs-text-color-dim2: black; + /* Monero orange color code */ -xmr-orange: #f26822; @@ -126,7 +143,33 @@ -fx-background-color: -bs-color-gray-3; } -.toggle-button-no-slider { - -fx-focus-color: transparent; - -fx-faint-focus-color: transparent; +#image-logo-splash { + -fx-image: url("../../images/logo_splash_light.png"); +} + +#image-logo-splash-testnet { + -fx-image: url("../../images/logo_splash_testnet_light.png"); +} + +#image-logo-landscape { + -fx-image: url("../../images/logo_landscape_light.png"); +} + +#charts .default-color0.chart-series-area-fill { + -fx-fill: linear-gradient(to bottom, + rgba(62, 163, 74, 0.45) 0%, + rgba(62, 163, 74, 0.0) 100% + ); +} + +#charts .default-color1.chart-series-area-fill { + -fx-fill: linear-gradient(to bottom, + rgba(215, 48, 48, 0.45) 0%, + rgba(215, 48, 48, 0.0) 100% + ); +} + +/* All inputs have border in light mode. */ +.jfx-combo-box, .jfx-text-field, .jfx-text-area, .jfx-password-field { + -fx-border-color: -bs-color-border-form-field; } diff --git a/desktop/src/main/java/haveno/desktop/util/CssTheme.java b/desktop/src/main/java/haveno/desktop/util/CssTheme.java index 1e1c547607..4648d07eeb 100644 --- a/desktop/src/main/java/haveno/desktop/util/CssTheme.java +++ b/desktop/src/main/java/haveno/desktop/util/CssTheme.java @@ -58,6 +58,10 @@ public class CssTheme { scene.getStylesheets().add(cssThemeFolder + "theme-dev.css"); } + public static int getCurrentTheme() { + return currentCSSTheme; + } + public static boolean isDarkTheme() { return currentCSSTheme == CSS_THEME_DARK; } diff --git a/desktop/src/main/java/haveno/desktop/util/CurrencyList.java b/desktop/src/main/java/haveno/desktop/util/CurrencyList.java index 3e68ccf876..88bef5ab1c 100644 --- a/desktop/src/main/java/haveno/desktop/util/CurrencyList.java +++ b/desktop/src/main/java/haveno/desktop/util/CurrencyList.java @@ -18,6 +18,8 @@ package haveno.desktop.util; import com.google.common.collect.Lists; + +import haveno.core.locale.CurrencyUtil; import haveno.core.locale.TradeCurrency; import haveno.core.user.Preferences; import javafx.collections.FXCollections; @@ -92,14 +94,13 @@ public class CurrencyList { } private Comparator<CurrencyListItem> getComparator() { - Comparator<CurrencyListItem> result; if (preferences.isSortMarketCurrenciesNumerically()) { - Comparator<CurrencyListItem> byCount = Comparator.comparingInt(left -> left.numTrades); - result = byCount.reversed(); + return Comparator + .comparingInt((CurrencyListItem item) -> item.numTrades).reversed() + .thenComparing(item -> CurrencyUtil.isCryptoCurrency(item.tradeCurrency.getCode()) ? item.tradeCurrency.getName() : item.tradeCurrency.getCode()); } else { - result = Comparator.comparing(item -> item.tradeCurrency); + return Comparator.comparing(item -> CurrencyUtil.isCryptoCurrency(item.tradeCurrency.getCode()) ? item.tradeCurrency.getName() : item.tradeCurrency.getCode()); } - return result; } private Map<TradeCurrency, Integer> countTrades(List<TradeCurrency> currencies) { diff --git a/desktop/src/main/java/haveno/desktop/util/FormBuilder.java b/desktop/src/main/java/haveno/desktop/util/FormBuilder.java index ae3e7ed266..dbee100f06 100644 --- a/desktop/src/main/java/haveno/desktop/util/FormBuilder.java +++ b/desktop/src/main/java/haveno/desktop/util/FormBuilder.java @@ -135,6 +135,24 @@ public class FormBuilder { return titledGroupBg; } + /////////////////////////////////////////////////////////////////////////////////////////// + // Divider + /////////////////////////////////////////////////////////////////////////////////////////// + + public static Region addSeparator(GridPane gridPane, int rowIndex) { + Region separator = new Region(); + separator.getStyleClass().add("grid-pane-separator"); + separator.setPrefHeight(1); + separator.setMinHeight(1); + separator.setMaxHeight(1); + GridPane.setRowIndex(separator, rowIndex); + GridPane.setColumnIndex(separator, 0); + GridPane.setColumnSpan(separator, 2); + gridPane.getChildren().add(separator); + separator.setPrefHeight(1); + GridPane.setMargin(separator, new Insets(0, 0, 3, 0)); + return separator; + } /////////////////////////////////////////////////////////////////////////////////////////// // Label @@ -361,7 +379,7 @@ public class FormBuilder { textField.setPrefWidth(Layout.INITIAL_WINDOW_WIDTH); Button button = new AutoTooltipButton("..."); - button.setStyle("-fx-min-width: 26; -fx-pref-height: 26; -fx-padding: 0 0 10 0; -fx-background-color: -fx-background;"); + button.setStyle("-fx-min-width: 32; -fx-padding: 0 0 10 0; -fx-background-color: -fx-background;"); button.managedProperty().bind(button.visibleProperty()); HBox hbox = new HBox(textField, button); @@ -369,6 +387,7 @@ public class FormBuilder { hbox.setSpacing(8); VBox vbox = getTopLabelVBox(0); + vbox.setSpacing(2); vbox.getChildren().addAll(getTopLabel(title), hbox); gridPane.getChildren().add(vbox); @@ -490,6 +509,7 @@ public class FormBuilder { GridPane.setColumnIndex(textArea, 1); GridPane.setMargin(label, new Insets(top, 0, 0, 0)); GridPane.setHalignment(label, HPos.LEFT); + GridPane.setValignment(label, VPos.TOP); GridPane.setMargin(textArea, new Insets(top, 0, 0, 0)); return new Tuple2<>(label, textArea); @@ -617,6 +637,7 @@ public class FormBuilder { JFXTextArea textArea = new HavenoTextArea(); textArea.setPromptText(prompt); textArea.setLabelFloat(true); + textArea.getStyleClass().add("label-float"); textArea.setWrapText(true); GridPane.setRowIndex(textArea, rowIndex); @@ -805,9 +826,9 @@ public class FormBuilder { } public static InputTextField addInputTextField(GridPane gridPane, int rowIndex, String title, double top) { - InputTextField inputTextField = new InputTextField(); inputTextField.setLabelFloat(true); + inputTextField.getStyleClass().add("label-float"); inputTextField.setPromptText(title); GridPane.setRowIndex(inputTextField, rowIndex); GridPane.setColumnIndex(inputTextField, 0); @@ -884,6 +905,8 @@ public class FormBuilder { public static PasswordTextField addPasswordTextField(GridPane gridPane, int rowIndex, String title, double top) { PasswordTextField passwordField = new PasswordTextField(); + passwordField.getStyleClass().addAll("label-float"); + GUIUtil.applyFilledStyle(passwordField); passwordField.setPromptText(title); GridPane.setRowIndex(passwordField, rowIndex); GridPane.setColumnIndex(passwordField, 0); @@ -1006,8 +1029,10 @@ public class FormBuilder { InputTextField inputTextField1 = new InputTextField(); inputTextField1.setPromptText(title1); inputTextField1.setLabelFloat(true); + inputTextField1.getStyleClass().add("label-float"); InputTextField inputTextField2 = new InputTextField(); inputTextField2.setLabelFloat(true); + inputTextField2.getStyleClass().add("label-float"); inputTextField2.setPromptText(title2); HBox hBox = new HBox(); @@ -1228,6 +1253,7 @@ public class FormBuilder { public static <T> ComboBox<T> addComboBox(GridPane gridPane, int rowIndex, int top) { final JFXComboBox<T> comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); GridPane.setRowIndex(comboBox, rowIndex); GridPane.setMargin(comboBox, new Insets(top, 0, 0, 0)); @@ -1264,7 +1290,9 @@ public class FormBuilder { VBox vBox = getTopLabelVBox(top); final JFXComboBox<T> comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); comboBox.setPromptText(prompt); + comboBox.setPadding(new Insets(top, 0, 0, 12)); vBox.getChildren().addAll(label, comboBox); @@ -1389,7 +1417,9 @@ public class FormBuilder { public static <T> ComboBox<T> addComboBox(GridPane gridPane, int rowIndex, String title, double top) { JFXComboBox<T> comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); comboBox.setLabelFloat(true); + comboBox.getStyleClass().add("label-float"); comboBox.setPromptText(title); comboBox.setMaxWidth(Double.MAX_VALUE); @@ -1399,6 +1429,7 @@ public class FormBuilder { GridPane.setRowIndex(comboBox, rowIndex); GridPane.setColumnIndex(comboBox, 0); + comboBox.setPadding(new Insets(0, 0, 0, 12)); GridPane.setMargin(comboBox, new Insets(top + Layout.FLOATING_LABEL_DISTANCE, 0, 0, 0)); gridPane.getChildren().add(comboBox); @@ -1407,7 +1438,9 @@ public class FormBuilder { public static <T> AutocompleteComboBox<T> addAutocompleteComboBox(GridPane gridPane, int rowIndex, String title, double top) { var comboBox = new AutocompleteComboBox<T>(); + GUIUtil.applyFilledStyle(comboBox); comboBox.setLabelFloat(true); + comboBox.getStyleClass().add("label-float"); comboBox.setPromptText(title); comboBox.setMaxWidth(Double.MAX_VALUE); @@ -1469,6 +1502,7 @@ public class FormBuilder { AutocompleteComboBox<T> comboBox = new AutocompleteComboBox<>(); comboBox.setPromptText(titleCombobox); comboBox.setLabelFloat(true); + comboBox.getStyleClass().add("label-float"); topLabelVBox2.getChildren().addAll(topLabel2, comboBox); hBox.getChildren().addAll(topLabelVBox1, topLabelVBox2); @@ -1498,7 +1532,9 @@ public class FormBuilder { hBox.setSpacing(10); ComboBox<T> comboBox1 = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox1); ComboBox<R> comboBox2 = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox2); hBox.getChildren().addAll(comboBox1, comboBox2); final Tuple2<Label, VBox> topLabelWithVBox = addTopLabelWithVBox(gridPane, rowIndex, title, hBox, top); @@ -1526,8 +1562,10 @@ public class FormBuilder { hBox.setSpacing(10); JFXComboBox<T> comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); comboBox.setPromptText(titleCombobox); comboBox.setLabelFloat(true); + comboBox.getStyleClass().add("label-float"); TextField textField = new HavenoTextField(); @@ -1570,6 +1608,7 @@ public class FormBuilder { button.setDefaultButton(true); ComboBox<T> comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); hBox.getChildren().addAll(comboBox, button); @@ -1604,6 +1643,7 @@ public class FormBuilder { hBox.setSpacing(10); ComboBox<T> comboBox = new JFXComboBox<>(); + GUIUtil.applyFilledStyle(comboBox); TextField textField = new TextField(textFieldText); textField.setEditable(false); textField.setMouseTransparent(true); @@ -1797,6 +1837,7 @@ public class FormBuilder { return new Tuple2<>(label, textFieldWithCopyIcon); } + /////////////////////////////////////////////////////////////////////////////////////////// // Label + AddressTextField /////////////////////////////////////////////////////////////////////////////////////////// @@ -2181,11 +2222,13 @@ public class FormBuilder { Label label = new AutoTooltipLabel(Res.getBaseCurrencyCode()); label.getStyleClass().add("input-label"); + HBox.setMargin(label, new Insets(0, 8, 0, 0)); HBox box = new HBox(); HBox.setHgrow(input, Priority.ALWAYS); input.setMaxWidth(Double.MAX_VALUE); - box.getStyleClass().add("input-with-border"); + box.setAlignment(Pos.CENTER_LEFT); + box.getStyleClass().add("offer-input"); box.getChildren().addAll(input, label); return new Tuple3<>(box, input, label); } @@ -2197,11 +2240,13 @@ public class FormBuilder { Label label = new AutoTooltipLabel(Res.getBaseCurrencyCode()); label.getStyleClass().add("input-label"); + HBox.setMargin(label, new Insets(0, 8, 0, 0)); HBox box = new HBox(); HBox.setHgrow(infoInputTextField, Priority.ALWAYS); infoInputTextField.setMaxWidth(Double.MAX_VALUE); - box.getStyleClass().add("input-with-border"); + box.setAlignment(Pos.CENTER_LEFT); + box.getStyleClass().add("offer-input"); box.getChildren().addAll(infoInputTextField, label); return new Tuple3<>(box, infoInputTextField, label); } @@ -2444,6 +2489,7 @@ public class FormBuilder { if (groupStyle != null) titledGroupBg.getStyleClass().add(groupStyle); TableView<T> tableView = new TableView<>(); + GUIUtil.applyTableStyle(tableView); GridPane.setRowIndex(tableView, rowIndex); GridPane.setMargin(tableView, new Insets(top + 30, -10, 5, -10)); gridPane.getChildren().add(tableView); diff --git a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java index e825e29e6e..3097780c1a 100644 --- a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java @@ -25,7 +25,10 @@ import com.googlecode.jcsv.CSVStrategy; import com.googlecode.jcsv.writer.CSVEntryConverter; import com.googlecode.jcsv.writer.CSVWriter; import com.googlecode.jcsv.writer.internal.CSVWriterBuilder; + +import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; +import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView; import haveno.common.UserThread; import haveno.common.config.Config; import haveno.common.file.CorruptedStorageFileHandler; @@ -64,9 +67,17 @@ import haveno.desktop.main.account.AccountView; import haveno.desktop.main.account.content.traditionalaccounts.TraditionalAccountsView; import haveno.desktop.main.overlays.popups.Popup; import haveno.network.p2p.P2PService; +import javafx.application.Platform; +import javafx.beans.binding.Bindings; +import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; import javafx.geometry.HPos; +import javafx.geometry.Insets; import javafx.geometry.Orientation; +import javafx.geometry.Pos; +import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.ComboBox; @@ -76,14 +87,21 @@ import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.ScrollBar; import javafx.scene.control.ScrollPane; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; +import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.shape.Rectangle; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import javafx.stage.Modality; @@ -131,7 +149,9 @@ public class GUIUtil { public final static int NUM_DECIMALS_PRECISE = 7; public final static int AMOUNT_DECIMALS_WITH_ZEROS = 3; public final static int AMOUNT_DECIMALS = 4; + public static final double NUM_OFFERS_TRANSLATE_X = -13.0; + public static final boolean disablePaymentUriLabel = true; // universally disable payment uri labels, allowing bigger xmr logo overlays private static Preferences preferences; public static void setPreferences(Preferences preferences) { @@ -304,30 +324,42 @@ public class GUIUtil { HBox box = new HBox(); box.setSpacing(20); - Label currencyType = new AutoTooltipLabel( - CurrencyUtil.isTraditionalCurrency(code) ? Res.get("shared.traditional") : Res.get("shared.crypto")); + box.setAlignment(Pos.CENTER_LEFT); + Label label1 = new AutoTooltipLabel(getCurrencyType(code)); + label1.getStyleClass().add("currency-label-small"); + Label label2 = new AutoTooltipLabel(CurrencyUtil.isCryptoCurrency(code) ? item.tradeCurrency.getNameAndCode() : code); + label2.getStyleClass().add("currency-label"); + Label label3 = new AutoTooltipLabel(CurrencyUtil.isCryptoCurrency(code) ? "" : item.tradeCurrency.getName()); + if (!CurrencyUtil.isCryptoCurrency(code)) label3.getStyleClass().add("currency-label"); + Label label4 = new AutoTooltipLabel(); - currencyType.getStyleClass().add("currency-label-small"); - Label currency = new AutoTooltipLabel(code); - currency.getStyleClass().add("currency-label"); - Label offers = new AutoTooltipLabel(item.tradeCurrency.getName()); - offers.getStyleClass().add("currency-label"); - - box.getChildren().addAll(currencyType, currency, offers); + box.getChildren().addAll(label1, label2, label3); + if (!CurrencyUtil.isCryptoCurrency(code)) box.getChildren().add(label4); switch (code) { case GUIUtil.SHOW_ALL_FLAG: - currencyType.setText(Res.get("shared.all")); - currency.setText(Res.get("list.currency.showAll")); + label1.setText(Res.get("shared.all")); + label2.setText(Res.get("list.currency.showAll")); break; case GUIUtil.EDIT_FLAG: - currencyType.setText(Res.get("shared.edit")); - currency.setText(Res.get("list.currency.editList")); + label1.setText(Res.get("shared.edit")); + label2.setText(Res.get("list.currency.editList")); break; default: - if (preferences.isSortMarketCurrenciesNumerically()) { - offers.setText(offers.getText() + " (" + item.numTrades + " " + - (item.numTrades == 1 ? postFixSingle : postFixMulti) + ")"); + + // use icons for crypto + if (CurrencyUtil.isCryptoCurrency(code)) { + label1.setText(""); + StackPane iconWrapper = new StackPane(getCurrencyIcon(code)); // TODO: icon must be wrapped in StackPane for reliable rendering on linux + label1.setGraphic(iconWrapper); + } + + if (preferences.isSortMarketCurrenciesNumerically() && item.numTrades > 0) { + boolean isCrypto = CurrencyUtil.isCryptoCurrency(code); + Label offersTarget = isCrypto ? label3 : label4; + HBox.setMargin(offersTarget, new Insets(0, 0, 0, NUM_OFFERS_TRANSLATE_X)); + offersTarget.getStyleClass().add("offer-label"); + offersTarget.setText(item.numTrades + " " + (item.numTrades == 1 ? postFixSingle : postFixMulti)); } } @@ -430,33 +462,47 @@ public class GUIUtil { HBox box = new HBox(); box.setSpacing(20); - Label currencyType = new AutoTooltipLabel( - CurrencyUtil.isTraditionalCurrency(item.getCode()) ? Res.get("shared.traditional") : Res.get("shared.crypto")); + box.setAlignment(Pos.CENTER_LEFT); - currencyType.getStyleClass().add("currency-label-small"); - Label currency = new AutoTooltipLabel(item.getCode()); - currency.getStyleClass().add("currency-label"); - Label offers = new AutoTooltipLabel(item.getName()); - offers.getStyleClass().add("currency-label"); - - box.getChildren().addAll(currencyType, currency, offers); + Label label1 = new AutoTooltipLabel(getCurrencyType(item.getCode())); + label1.getStyleClass().add("currency-label-small"); + Label label2 = new AutoTooltipLabel(CurrencyUtil.isCryptoCurrency(code) ? item.getNameAndCode() : code); + label2.getStyleClass().add("currency-label"); + Label label3 = new AutoTooltipLabel(CurrencyUtil.isCryptoCurrency(code) ? "" : item.getName()); + if (!CurrencyUtil.isCryptoCurrency(code)) label3.getStyleClass().add("currency-label"); + Label label4 = new AutoTooltipLabel(); Optional<Integer> offerCountOptional = Optional.ofNullable(offerCounts.get(code)); switch (code) { case GUIUtil.SHOW_ALL_FLAG: - currencyType.setText(Res.get("shared.all")); - currency.setText(Res.get("list.currency.showAll")); + label1.setText(Res.get("shared.all")); + label2.setText(Res.get("list.currency.showAll")); break; case GUIUtil.EDIT_FLAG: - currencyType.setText(Res.get("shared.edit")); - currency.setText(Res.get("list.currency.editList")); + label1.setText(Res.get("shared.edit")); + label2.setText(Res.get("list.currency.editList")); break; default: - offerCountOptional.ifPresent(numOffer -> offers.setText(offers.getText() + " (" + numOffer + " " + - (numOffer == 1 ? postFixSingle : postFixMulti) + ")")); + + // use icons for crypto + if (CurrencyUtil.isCryptoCurrency(item.getCode())) { + label1.setText(""); + label1.setGraphic(getCurrencyIcon(item.getCode())); + } + + boolean isCrypto = CurrencyUtil.isCryptoCurrency(code); + Label offersTarget = isCrypto ? label3 : label4; + offerCountOptional.ifPresent(numOffers -> { + HBox.setMargin(offersTarget, new Insets(0, 0, 0, NUM_OFFERS_TRANSLATE_X)); + offersTarget.getStyleClass().add("offer-label"); + offersTarget.setText(numOffers + " " + (numOffers == 1 ? postFixSingle : postFixMulti)); + }); } + box.getChildren().addAll(label1, label2, label3); + if (!CurrencyUtil.isCryptoCurrency(code)) box.getChildren().add(label4); + setGraphic(box); } else { @@ -466,6 +512,55 @@ public class GUIUtil { }; } + public static Callback<ListView<TradeCurrency>, ListCell<TradeCurrency>> getTradeCurrencyCellFactoryNameAndCode() { + return p -> new ListCell<>() { + @Override + protected void updateItem(TradeCurrency item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + + HBox box = new HBox(); + box.setSpacing(10); + + Label label1 = new AutoTooltipLabel(getCurrencyType(item.getCode())); + label1.getStyleClass().add("currency-label-small"); + Label label2 = new AutoTooltipLabel(item.getNameAndCode()); + label2.getStyleClass().add("currency-label"); + + // use icons for crypto + if (CurrencyUtil.isCryptoCurrency(item.getCode())) { + label1.setText(""); + label1.setGraphic(getCurrencyIcon(item.getCode())); + } + + box.getChildren().addAll(label1, label2); + + setGraphic(box); + + } else { + setGraphic(null); + } + } + }; + } + + private static String getCurrencyType(String code) { + if (CurrencyUtil.isFiatCurrency(code)) { + return Res.get("shared.fiat"); + } else if (CurrencyUtil.isTraditionalCurrency(code)) { + return Res.get("shared.traditional"); + } else if (CurrencyUtil.isCryptoCurrency(code)) { + return Res.get("shared.crypto"); + } else { + return ""; + } + } + + private static String getCurrencyType(PaymentMethod method) { + return method.isTraditional() ? Res.get("shared.traditional") : Res.get("shared.crypto"); + } + public static ListCell<PaymentMethod> getPaymentMethodButtonCell() { return new ListCell<>() { @@ -501,9 +596,7 @@ public class GUIUtil { HBox box = new HBox(); box.setSpacing(20); - Label paymentType = new AutoTooltipLabel( - method.isTraditional() ? Res.get("shared.traditional") : Res.get("shared.crypto")); - + Label paymentType = new AutoTooltipLabel(getCurrencyType(method)); paymentType.getStyleClass().add("currency-label-small"); Label paymentMethod = new AutoTooltipLabel(Res.get(id)); paymentMethod.getStyleClass().add("currency-label"); @@ -673,7 +766,7 @@ public class GUIUtil { String currencyName = Config.baseCurrencyNetwork().getCurrencyName(); new Popup().information(Res.get("payment.fasterPayments.newRequirements.info", currencyName)) .width(900) - .actionButtonTextWithGoTo("navigation.account") + .actionButtonTextWithGoTo("mainView.menu.account") .onAction(() -> { navigation.setReturnPath(navigation.getCurrentPath()); navigation.navigateTo(MainView.class, AccountView.class, TraditionalAccountsView.class); @@ -683,10 +776,10 @@ public class GUIUtil { } public static String getMoneroURI(String address, BigInteger amount, String label) { - return MoneroUtils.getPaymentUri(new MoneroTxConfig() - .setAddress(address) - .setAmount(amount) - .setNote(label)); + MoneroTxConfig txConfig = new MoneroTxConfig().setAddress(address); + if (amount != null) txConfig.setAmount(amount); + if (label != null && !label.isEmpty() && !disablePaymentUriLabel) txConfig.setNote(label); + return MoneroUtils.getPaymentUri(txConfig); } public static boolean isBootstrappedOrShowPopup(P2PService p2PService) { @@ -742,7 +835,7 @@ public class GUIUtil { if (user.currentPaymentAccountProperty().get() == null) { new Popup().headLine(Res.get("popup.warning.noTradingAccountSetup.headline")) .instruction(Res.get("popup.warning.noTradingAccountSetup.msg")) - .actionButtonTextWithGoTo("navigation.account") + .actionButtonTextWithGoTo("mainView.menu.account") .onAction(() -> { navigation.setReturnPath(navigation.getCurrentPath()); navigation.navigateTo(MainView.class, AccountView.class, TraditionalAccountsView.class); @@ -1033,4 +1126,269 @@ public class GUIUtil { columnConstraints2.setHgrow(Priority.ALWAYS); gridPane.getColumnConstraints().addAll(columnConstraints1, columnConstraints2); } + + public static void applyFilledStyle(TextField textField) { + textField.textProperty().addListener((observable, oldValue, newValue) -> { + updateFilledStyle(textField); + }); + } + + private static void updateFilledStyle(TextField textField) { + if (textField.getText() != null && !textField.getText().isEmpty()) { + if (!textField.getStyleClass().contains("filled")) { + textField.getStyleClass().add("filled"); + } + } else { + textField.getStyleClass().remove("filled"); + } + } + + public static void applyFilledStyle(ComboBox<?> comboBox) { + comboBox.valueProperty().addListener((observable, oldValue, newValue) -> { + updateFilledStyle(comboBox); + }); + } + + private static void updateFilledStyle(ComboBox<?> comboBox) { + if (comboBox.getValue() != null) { + if (!comboBox.getStyleClass().contains("filled")) { + comboBox.getStyleClass().add("filled"); + } + } else { + comboBox.getStyleClass().remove("filled"); + } + } + + public static void applyTableStyle(TableView<?> tableView) { + applyTableStyle(tableView, true); + } + + public static void applyTableStyle(TableView<?> tableView, boolean applyRoundedArc) { + if (applyRoundedArc) applyRoundedArc(tableView); + addSpacerColumns(tableView); + applyEdgeColumnStyleClasses(tableView); + } + + private static void applyRoundedArc(TableView<?> tableView) { + Rectangle clip = new Rectangle(); + clip.setArcWidth(Layout.ROUNDED_ARC); + clip.setArcHeight(Layout.ROUNDED_ARC); + tableView.setClip(clip); + tableView.layoutBoundsProperty().addListener((obs, oldVal, newVal) -> { + clip.setWidth(newVal.getWidth()); + clip.setHeight(newVal.getHeight()); + }); + } + + private static <T> void addSpacerColumns(TableView<T> tableView) { + TableColumn<T, Void> leftSpacer = new TableColumn<>(); + TableColumn<T, Void> rightSpacer = new TableColumn<>(); + + configureSpacerColumn(leftSpacer); + configureSpacerColumn(rightSpacer); + + tableView.getColumns().add(0, leftSpacer); + tableView.getColumns().add(rightSpacer); + } + + private static void configureSpacerColumn(TableColumn<?, ?> column) { + column.setPrefWidth(15); + column.setMaxWidth(15); + column.setMinWidth(15); + column.setReorderable(false); + column.setResizable(false); + column.setSortable(false); + column.setCellFactory(col -> new TableCell<>()); // empty cell + } + + private static <T> void applyEdgeColumnStyleClasses(TableView<T> tableView) { + ListChangeListener<TableColumn<T, ?>> columnListener = change -> { + UserThread.execute(() -> { + updateEdgeColumnStyleClasses(tableView); + }); + }; + + tableView.getColumns().addListener(columnListener); + tableView.skinProperty().addListener((obs, oldSkin, newSkin) -> { + if (newSkin != null) { + UserThread.execute(() -> { + updateEdgeColumnStyleClasses(tableView); + }); + } + }); + + // react to size changes + ChangeListener<Number> sizeListener = (obs, oldVal, newVal) -> updateEdgeColumnStyleClasses(tableView); + tableView.heightProperty().addListener(sizeListener); + tableView.widthProperty().addListener(sizeListener); + + updateEdgeColumnStyleClasses(tableView); + } + + private static <T> void updateEdgeColumnStyleClasses(TableView<T> tableView) { + ObservableList<TableColumn<T, ?>> columns = tableView.getColumns(); + + // find columns with "first-column" and "last-column" classes + TableColumn<T, ?> firstCol = null; + TableColumn<T, ?> lastCol = null; + for (TableColumn<T, ?> col : columns) { + if (col.getStyleClass().contains("first-column")) { + firstCol = col; + } else if (col.getStyleClass().contains("last-column")) { + lastCol = col; + } + } + + // handle if columns do not exist + if (firstCol == null || lastCol == null) { + if (firstCol != null) throw new IllegalStateException("Missing column with 'last-column'"); + if (lastCol != null) throw new IllegalStateException("Missing column with 'first-column'"); + + // remove all classes + for (TableColumn<T, ?> col : columns) { + col.getStyleClass().removeAll("first-column", "last-column"); + } + + // apply first and last classes + if (!columns.isEmpty()) { + TableColumn<T, ?> first = columns.get(0); + TableColumn<T, ?> last = columns.get(columns.size() - 1); + + if (!first.getStyleClass().contains("first-column")) { + first.getStyleClass().add("first-column"); + } + + if (!last.getStyleClass().contains("last-column")) { + last.getStyleClass().add("last-column"); + } + } + } else { + + // done if correct order + if (columns.get(0) == firstCol && columns.get(columns.size() - 1) == lastCol) { + return; + } + + // set first and last columns + if (columns.get(0) != firstCol) { + columns.remove(firstCol); + columns.add(0, firstCol); + } + if (columns.get(columns.size() - 1) != lastCol) { + columns.remove(lastCol); + columns.add(firstCol == lastCol ? columns.size() - 1 : columns.size(), lastCol); + } + } + } + + public static ImageView getCurrencyIcon(String currencyCode) { + return getCurrencyIcon(currencyCode, 24); + } + + public static ImageView getCurrencyIcon(String currencyCode, double size) { + if (currencyCode == null) return null; + ImageView iconView = new ImageView(); + iconView.setFitWidth(size); + iconView.setPreserveRatio(true); + iconView.setSmooth(true); + iconView.setCache(true); + iconView.setId(getImageId(currencyCode)); + return iconView; + } + + public static StackPane getCurrencyIconWithBorder(String currencyCode) { + return getCurrencyIconWithBorder(currencyCode, 25, 1); + } + + public static StackPane getCurrencyIconWithBorder(String currencyCode, double size, double borderWidth) { + if (currencyCode == null) return null; + + ImageView icon = getCurrencyIcon(currencyCode, size); + icon.setFitWidth(size - 2 * borderWidth); + icon.setFitHeight(size - 2 * borderWidth); + icon.setPreserveRatio(true); + icon.setSmooth(true); + icon.setCache(true); + + StackPane circleWrapper = new StackPane(icon); + circleWrapper.setPrefSize(size, size); + circleWrapper.setMaxSize(size, size); + circleWrapper.setMinSize(size, size); + + circleWrapper.setStyle( + "-fx-background-color: white;" + + "-fx-background-radius: 50%;" + + "-fx-border-radius: 50%;" + + "-fx-border-color: white;" + + "-fx-border-width: " + borderWidth + "px;" + ); + + StackPane.setAlignment(icon, Pos.CENTER); + + return circleWrapper; + } + + private static String getImageId(String currencyCode) { + if (currencyCode == null) return null; + return "image-" + currencyCode.toLowerCase() + "-logo"; + } + + public static void adjustHeightAutomatically(TextArea textArea) { + adjustHeightAutomatically(textArea, null); + } + + public static void adjustHeightAutomatically(TextArea textArea, Double maxHeight) { + textArea.sceneProperty().addListener((o, oldScene, newScene) -> { + if (newScene != null) { + // avoid javafx css warning + CssTheme.loadSceneStyles(newScene, CssTheme.getCurrentTheme(), false); + textArea.applyCss(); + var text = textArea.lookup(".text"); + + textArea.prefHeightProperty().bind(Bindings.createDoubleBinding(() -> { + Insets padding = textArea.getInsets(); + double topBottomPadding = padding.getTop() + padding.getBottom(); + double prefHeight = textArea.getFont().getSize() + text.getBoundsInLocal().getHeight() + topBottomPadding; + return maxHeight == null ? prefHeight : Math.min(prefHeight, maxHeight); + }, text.boundsInLocalProperty())); + + text.boundsInLocalProperty().addListener((observableBoundsAfter, boundsBefore, boundsAfter) -> { + Platform.runLater(() -> textArea.requestLayout()); + }); + } + }); + } + + public static Label getLockLabel() { + Label lockLabel = FormBuilder.getIcon(AwesomeIcon.LOCK, "16px"); + lockLabel.setStyle(lockLabel.getStyle() + " -fx-text-fill: white;"); + return lockLabel; + } + + public static MaterialDesignIconView getCopyIcon() { + return new MaterialDesignIconView(MaterialDesignIcon.CONTENT_COPY, "1.35em"); + } + + + public static Tuple2<StackPane, ImageView> getSmallXmrQrCodePane() { + return getXmrQrCodePane(150, disablePaymentUriLabel ? 32 : 28, 2); + } + + public static Tuple2<StackPane, ImageView> getBigXmrQrCodePane() { + return getXmrQrCodePane(250, disablePaymentUriLabel ? 47 : 45, 3); + } + + private static Tuple2<StackPane, ImageView> getXmrQrCodePane(int qrCodeSize, int logoSize, int logoBorderWidth) { + ImageView qrCodeImageView = new ImageView(); + qrCodeImageView.setFitHeight(qrCodeSize); + qrCodeImageView.setFitWidth(qrCodeSize); + qrCodeImageView.getStyleClass().add("qr-code"); + + StackPane xmrLogo = GUIUtil.getCurrencyIconWithBorder(Res.getBaseCurrencyCode(), logoSize, logoBorderWidth); + StackPane qrCodePane = new StackPane(qrCodeImageView, xmrLogo); + qrCodePane.setCursor(Cursor.HAND); + qrCodePane.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE); + + return new Tuple2<>(qrCodePane, qrCodeImageView); + } } diff --git a/desktop/src/main/java/haveno/desktop/util/Layout.java b/desktop/src/main/java/haveno/desktop/util/Layout.java index 975bb40df6..f3c705f947 100644 --- a/desktop/src/main/java/haveno/desktop/util/Layout.java +++ b/desktop/src/main/java/haveno/desktop/util/Layout.java @@ -25,7 +25,7 @@ public class Layout { public static final double FIRST_ROW_DISTANCE = 20d; public static final double COMPACT_FIRST_ROW_DISTANCE = 10d; public static final double TWICE_FIRST_ROW_DISTANCE = 20d * 2; - public static final double FLOATING_LABEL_DISTANCE = 18d; + public static final double FLOATING_LABEL_DISTANCE = 23d; public static final double GROUP_DISTANCE = 40d; public static final double COMPACT_GROUP_DISTANCE = 30d; public static final double GROUP_DISTANCE_WITHOUT_SEPARATOR = 20d; @@ -33,6 +33,7 @@ public class Layout { public static final double COMPACT_FIRST_ROW_AND_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + FIRST_ROW_DISTANCE; public static final double COMPACT_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + COMPACT_FIRST_ROW_DISTANCE; public static final double COMPACT_FIRST_ROW_AND_GROUP_DISTANCE_WITHOUT_SEPARATOR = GROUP_DISTANCE_WITHOUT_SEPARATOR + COMPACT_FIRST_ROW_DISTANCE; + public static final double TWICE_FIRST_ROW_AND_COMPACT_GROUP_DISTANCE = COMPACT_GROUP_DISTANCE + TWICE_FIRST_ROW_DISTANCE; public static final double TWICE_FIRST_ROW_AND_GROUP_DISTANCE = GROUP_DISTANCE + TWICE_FIRST_ROW_DISTANCE; public static final double PADDING_WINDOW = 20d; public static double PADDING = 10d; @@ -40,4 +41,8 @@ public class Layout { public static final double SPACING_V_BOX = 5d; public static final double GRID_GAP = 5d; public static final double LIST_ROW_HEIGHT = 34; + public static final double ROUNDED_ARC = 20; + public static final double FLOATING_ICON_Y = 9; // adjust when .jfx-text-field padding is changed for right icons + public static final double DETAILS_WINDOW_WIDTH = 950; + public static final double DETAILS_WINDOW_EXTRA_INFO_MAX_HEIGHT = 150; } diff --git a/desktop/src/main/java/haveno/desktop/util/Transitions.java b/desktop/src/main/java/haveno/desktop/util/Transitions.java index 300524ccce..691d9d6243 100644 --- a/desktop/src/main/java/haveno/desktop/util/Transitions.java +++ b/desktop/src/main/java/haveno/desktop/util/Transitions.java @@ -37,7 +37,7 @@ import javafx.util.Duration; @Singleton public class Transitions { - public final static int DEFAULT_DURATION = 600; + public final static int DEFAULT_DURATION = 400; private final Preferences preferences; private Timeline removeEffectTimeLine; @@ -96,7 +96,7 @@ public class Transitions { // Blur public void blur(Node node) { - blur(node, DEFAULT_DURATION, -0.1, false, 15); + blur(node, DEFAULT_DURATION, -0.1, false, 45); } public void blur(Node node, int duration, double brightness, boolean removeNode, double blurRadius) { @@ -111,7 +111,7 @@ public class Transitions { ColorAdjust darken = new ColorAdjust(); darken.setBrightness(0.0); blur.setInput(darken); - KeyValue kv2 = new KeyValue(darken.brightnessProperty(), brightness); + KeyValue kv2 = new KeyValue(darken.brightnessProperty(), CssTheme.isDarkTheme() ? brightness * -0.13 : brightness); KeyFrame kf2 = new KeyFrame(Duration.millis(getDuration(duration)), kv2); timeline.getKeyFrames().addAll(kf1, kf2); node.setEffect(blur); diff --git a/desktop/src/main/resources/images/account.png b/desktop/src/main/resources/images/account.png new file mode 100644 index 0000000000..53b4058465 Binary files /dev/null and b/desktop/src/main/resources/images/account.png differ diff --git a/desktop/src/main/resources/images/alert_round.png b/desktop/src/main/resources/images/alert_round.png index c5c182ed94..4ba0578457 100644 Binary files a/desktop/src/main/resources/images/alert_round.png and b/desktop/src/main/resources/images/alert_round.png differ diff --git a/desktop/src/main/resources/images/bch_logo.png b/desktop/src/main/resources/images/bch_logo.png new file mode 100644 index 0000000000..3c30cfb9fb Binary files /dev/null and b/desktop/src/main/resources/images/bch_logo.png differ diff --git a/desktop/src/main/resources/images/blue_circle.png b/desktop/src/main/resources/images/blue_circle_solid.png similarity index 100% rename from desktop/src/main/resources/images/blue_circle.png rename to desktop/src/main/resources/images/blue_circle_solid.png diff --git a/desktop/src/main/resources/images/blue_circle@2x.png b/desktop/src/main/resources/images/blue_circle_solid@2x.png similarity index 100% rename from desktop/src/main/resources/images/blue_circle@2x.png rename to desktop/src/main/resources/images/blue_circle_solid@2x.png diff --git a/desktop/src/main/resources/images/btc_logo.png b/desktop/src/main/resources/images/btc_logo.png new file mode 100644 index 0000000000..d3c3e7a7c8 Binary files /dev/null and b/desktop/src/main/resources/images/btc_logo.png differ diff --git a/desktop/src/main/resources/images/connection/tor.png b/desktop/src/main/resources/images/connection/tor.png index a88b4310cb..6553e06dac 100644 Binary files a/desktop/src/main/resources/images/connection/tor.png and b/desktop/src/main/resources/images/connection/tor.png differ diff --git a/desktop/src/main/resources/images/connection/tor@2x.png b/desktop/src/main/resources/images/connection/tor@2x.png deleted file mode 100644 index d8528c0c3c..0000000000 Binary files a/desktop/src/main/resources/images/connection/tor@2x.png and /dev/null differ diff --git a/desktop/src/main/resources/images/connection/tor_color@2x.png b/desktop/src/main/resources/images/connection/tor_color@2x.png deleted file mode 100644 index 1f2924adc0..0000000000 Binary files a/desktop/src/main/resources/images/connection/tor_color@2x.png and /dev/null differ diff --git a/desktop/src/main/resources/images/dai-erc20_logo.png b/desktop/src/main/resources/images/dai-erc20_logo.png new file mode 100644 index 0000000000..698ffc48eb Binary files /dev/null and b/desktop/src/main/resources/images/dai-erc20_logo.png differ diff --git a/desktop/src/main/resources/images/dark_mode_toggle.png b/desktop/src/main/resources/images/dark_mode_toggle.png new file mode 100644 index 0000000000..ceeede409d Binary files /dev/null and b/desktop/src/main/resources/images/dark_mode_toggle.png differ diff --git a/desktop/src/main/resources/images/eth_logo.png b/desktop/src/main/resources/images/eth_logo.png new file mode 100644 index 0000000000..17dd1a92a5 Binary files /dev/null and b/desktop/src/main/resources/images/eth_logo.png differ diff --git a/desktop/src/main/resources/images/green_circle.png b/desktop/src/main/resources/images/green_circle.png index 1555f43f0a..439ae2571b 100644 Binary files a/desktop/src/main/resources/images/green_circle.png and b/desktop/src/main/resources/images/green_circle.png differ diff --git a/desktop/src/main/resources/images/green_circle_solid.png b/desktop/src/main/resources/images/green_circle_solid.png new file mode 100644 index 0000000000..1555f43f0a Binary files /dev/null and b/desktop/src/main/resources/images/green_circle_solid.png differ diff --git a/desktop/src/main/resources/images/green_circle@2x.png b/desktop/src/main/resources/images/green_circle_solid@2x.png similarity index 100% rename from desktop/src/main/resources/images/green_circle@2x.png rename to desktop/src/main/resources/images/green_circle_solid@2x.png diff --git a/desktop/src/main/resources/images/light_mode_toggle.png b/desktop/src/main/resources/images/light_mode_toggle.png new file mode 100644 index 0000000000..870c5c209e Binary files /dev/null and b/desktop/src/main/resources/images/light_mode_toggle.png differ diff --git a/desktop/src/main/resources/images/lock@2x.png b/desktop/src/main/resources/images/lock@2x.png index 371f6aeb5d..f176e08203 100644 Binary files a/desktop/src/main/resources/images/lock@2x.png and b/desktop/src/main/resources/images/lock@2x.png differ diff --git a/desktop/src/main/resources/images/lock_circle.png b/desktop/src/main/resources/images/lock_circle.png new file mode 100644 index 0000000000..f176e08203 Binary files /dev/null and b/desktop/src/main/resources/images/lock_circle.png differ diff --git a/desktop/src/main/resources/images/logo_landscape_dark.png b/desktop/src/main/resources/images/logo_landscape_dark.png new file mode 100644 index 0000000000..ce3ffd6747 Binary files /dev/null and b/desktop/src/main/resources/images/logo_landscape_dark.png differ diff --git a/desktop/src/main/resources/images/logo_landscape_light.png b/desktop/src/main/resources/images/logo_landscape_light.png new file mode 100644 index 0000000000..e7ae4282c7 Binary files /dev/null and b/desktop/src/main/resources/images/logo_landscape_light.png differ diff --git a/desktop/src/main/resources/images/logo_splash.png b/desktop/src/main/resources/images/logo_splash.png deleted file mode 100644 index 110edbb85a..0000000000 Binary files a/desktop/src/main/resources/images/logo_splash.png and /dev/null differ diff --git a/desktop/src/main/resources/images/logo_splash@2x.png b/desktop/src/main/resources/images/logo_splash_dark.png similarity index 100% rename from desktop/src/main/resources/images/logo_splash@2x.png rename to desktop/src/main/resources/images/logo_splash_dark.png diff --git a/desktop/src/main/resources/images/logo_splash_light.png b/desktop/src/main/resources/images/logo_splash_light.png new file mode 100644 index 0000000000..45067473c9 Binary files /dev/null and b/desktop/src/main/resources/images/logo_splash_light.png differ diff --git a/desktop/src/main/resources/images/logo_splash_testnet.png b/desktop/src/main/resources/images/logo_splash_testnet.png deleted file mode 100644 index 00e44cc5b1..0000000000 Binary files a/desktop/src/main/resources/images/logo_splash_testnet.png and /dev/null differ diff --git a/desktop/src/main/resources/images/logo_splash_testnet_dark.png b/desktop/src/main/resources/images/logo_splash_testnet_dark.png new file mode 100644 index 0000000000..c7f8fc10f1 Binary files /dev/null and b/desktop/src/main/resources/images/logo_splash_testnet_dark.png differ diff --git a/desktop/src/main/resources/images/logo_splash_testnet@2x.png b/desktop/src/main/resources/images/logo_splash_testnet_light.png similarity index 100% rename from desktop/src/main/resources/images/logo_splash_testnet@2x.png rename to desktop/src/main/resources/images/logo_splash_testnet_light.png diff --git a/desktop/src/main/resources/images/ltc_logo.png b/desktop/src/main/resources/images/ltc_logo.png new file mode 100644 index 0000000000..0c7de04077 Binary files /dev/null and b/desktop/src/main/resources/images/ltc_logo.png differ diff --git a/desktop/src/main/resources/images/red_circle_solid.png b/desktop/src/main/resources/images/red_circle_solid.png new file mode 100644 index 0000000000..c5c182ed94 Binary files /dev/null and b/desktop/src/main/resources/images/red_circle_solid.png differ diff --git a/desktop/src/main/resources/images/alert_round@2x.png b/desktop/src/main/resources/images/red_circle_solid@2x.png similarity index 100% rename from desktop/src/main/resources/images/alert_round@2x.png rename to desktop/src/main/resources/images/red_circle_solid@2x.png diff --git a/desktop/src/main/resources/images/settings.png b/desktop/src/main/resources/images/settings.png new file mode 100644 index 0000000000..8a534387f0 Binary files /dev/null and b/desktop/src/main/resources/images/settings.png differ diff --git a/desktop/src/main/resources/images/support.png b/desktop/src/main/resources/images/support.png new file mode 100644 index 0000000000..b40bdd28e1 Binary files /dev/null and b/desktop/src/main/resources/images/support.png differ diff --git a/desktop/src/main/resources/images/usdc-erc20_logo.png b/desktop/src/main/resources/images/usdc-erc20_logo.png new file mode 100644 index 0000000000..fb2bc4802d Binary files /dev/null and b/desktop/src/main/resources/images/usdc-erc20_logo.png differ diff --git a/desktop/src/main/resources/images/usdt-erc20_logo.png b/desktop/src/main/resources/images/usdt-erc20_logo.png new file mode 100644 index 0000000000..f1d7e2c9aa Binary files /dev/null and b/desktop/src/main/resources/images/usdt-erc20_logo.png differ diff --git a/desktop/src/main/resources/images/usdt-trc20_logo.png b/desktop/src/main/resources/images/usdt-trc20_logo.png new file mode 100644 index 0000000000..f1d7e2c9aa Binary files /dev/null and b/desktop/src/main/resources/images/usdt-trc20_logo.png differ diff --git a/desktop/src/main/resources/images/xmr_logo.png b/desktop/src/main/resources/images/xmr_logo.png new file mode 100644 index 0000000000..dc3d28ccce Binary files /dev/null and b/desktop/src/main/resources/images/xmr_logo.png differ diff --git a/desktop/src/main/resources/images/yellow_circle.png b/desktop/src/main/resources/images/yellow_circle.png index 44e5a272fa..0fe6fc0b4e 100644 Binary files a/desktop/src/main/resources/images/yellow_circle.png and b/desktop/src/main/resources/images/yellow_circle.png differ diff --git a/desktop/src/main/resources/images/yellow_circle_solid.png b/desktop/src/main/resources/images/yellow_circle_solid.png new file mode 100644 index 0000000000..44e5a272fa Binary files /dev/null and b/desktop/src/main/resources/images/yellow_circle_solid.png differ diff --git a/desktop/src/test/java/haveno/desktop/ComponentsDemo.java b/desktop/src/test/java/haveno/desktop/ComponentsDemo.java index 740c2456b3..a73a0b5e43 100644 --- a/desktop/src/test/java/haveno/desktop/ComponentsDemo.java +++ b/desktop/src/test/java/haveno/desktop/ComponentsDemo.java @@ -91,6 +91,7 @@ public class ComponentsDemo extends Application { InputTextField inputTextField = FormBuilder.addInputTextField(gridPane, rowIndex++, "Enter something title"); inputTextField.setLabelFloat(true); + inputTextField.getStyleClass().add("label-float"); inputTextField.setText("Hello"); inputTextField.setPromptText("Enter something");