diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/controls/BisqPopupMenu.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/controls/BisqPopupMenu.java deleted file mode 100644 index bfd14bdd4a..0000000000 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/controls/BisqPopupMenu.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.desktop.components.controls; - -import javafx.geometry.Insets; -import javafx.scene.control.Hyperlink; -import javafx.scene.layout.VBox; - -import java.util.List; - -public class BisqPopupMenu extends BisqPopup { - public BisqPopupMenu(List items, Runnable onClose) { - super(); - - getStyleClass().add("bisq-popup-menu"); - - VBox box = new VBox(); - box.setSpacing(5); - box.setPadding(new Insets(10)); - - for (BisqPopupMenuItem item : items) { - Hyperlink hyperlink = new Hyperlink(item.getTitle()); - hyperlink.setOnAction(e -> { - item.getAction().run(); - hide(); - }); - - box.getChildren().add(hyperlink); - } - - setContentNode(box); - - showingProperty().addListener((observable, wasShowing, isShowing) -> { - if (wasShowing) { - onClose.run(); - } - }); - } -} diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/components/controls/DropdownMenu.java b/apps/desktop/desktop/src/main/java/bisq/desktop/components/controls/DropdownMenu.java index 9e65e48634..2771a689bd 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/components/controls/DropdownMenu.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/components/controls/DropdownMenu.java @@ -18,6 +18,8 @@ package bisq.desktop.components.controls; import bisq.desktop.common.utils.ImageUtil; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.WeakChangeListener; import javafx.collections.ObservableList; @@ -33,19 +35,25 @@ import javafx.stage.PopupWindow; import javafx.stage.WindowEvent; import lombok.Getter; +import lombok.Setter; import java.util.Collection; public class DropdownMenu extends HBox { public static final Double INITIAL_WIDTH = 24.0; - @Getter - private Label label = new Label(); + private final ImageView defaultIcon, activeIcon; + @Getter + private final BooleanProperty isMenuShowing = new SimpleBooleanProperty(false); private final ContextMenu contextMenu = new ContextMenu(); + @Getter + private Label label = new Label(); private ImageView buttonIcon; // We need to pin it as used in a WeakChangeListener private ChangeListener widthPropertyChangeListener; private boolean isFirstRun = false; + @Setter + private boolean openUpwards = false; public DropdownMenu(String defaultIconId, String activeIconId, boolean useIconOnly) { defaultIcon = ImageUtil.getImageViewById(defaultIconId); @@ -84,10 +92,12 @@ public void setLabel(Label label) { private void toggleContextMenu() { if (!contextMenu.isShowing()) { - contextMenu.setAnchorLocation(PopupWindow.AnchorLocation.WINDOW_TOP_RIGHT); - Bounds bounds = this.localToScreen(this.getBoundsInLocal()); + contextMenu.setAnchorLocation(openUpwards + ? PopupWindow.AnchorLocation.WINDOW_BOTTOM_RIGHT + : PopupWindow.AnchorLocation.WINDOW_TOP_RIGHT); + Bounds bounds = localToScreen(getBoundsInLocal()); double x = bounds.getMaxX(); - double y = bounds.getMaxY() + 3; + double y = openUpwards ? bounds.getMinY() - 3 : bounds.getMaxY() + 3; contextMenu.show(this, x, y); } else { contextMenu.hide(); @@ -123,7 +133,7 @@ public void setTooltip(Tooltip tooltip) { } private void attachListeners() { - setOnMouseClicked(event -> toggleContextMenu()); + setOnMouseClicked(e -> toggleContextMenu()); setOnMouseExited(e -> updateIcon(contextMenu.isShowing() ? activeIcon : defaultIcon)); setOnMouseEntered(e -> updateIcon(activeIcon)); @@ -140,11 +150,12 @@ private void attachListeners() { contextMenu.setOnShowing(e -> { getStyleClass().add("dropdown-menu-active"); updateIcon(activeIcon); - + isMenuShowing.setValue(true); }); contextMenu.setOnHidden(e -> { getStyleClass().remove("dropdown-menu-active"); updateIcon(defaultIcon); + isMenuShowing.setValue(false); }); widthPropertyChangeListener = (observable, oldValue, newValue) -> { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java index 83666c278f..fe79af392d 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListController.java @@ -26,14 +26,10 @@ import bisq.common.observable.Pin; import bisq.common.observable.collection.CollectionObserver; import bisq.desktop.ServiceProvider; -import bisq.desktop.common.observable.FxBindings; import bisq.desktop.common.threading.UIScheduler; import bisq.desktop.common.threading.UIThread; import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.common.view.Navigation; -import bisq.desktop.components.controls.BisqPopup; -import bisq.desktop.components.controls.BisqPopupMenu; -import bisq.desktop.components.controls.BisqPopupMenuItem; import bisq.desktop.components.overlay.Popup; import bisq.desktop.main.content.bisq_easy.BisqEasyServiceUtil; import bisq.desktop.main.content.bisq_easy.take_offer.TakeOfferController; @@ -54,7 +50,6 @@ import bisq.user.profile.UserProfileService; import bisq.user.reputation.ReputationScore; import bisq.user.reputation.ReputationService; -import javafx.scene.Node; import javafx.scene.Scene; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -63,7 +58,6 @@ import javax.annotation.Nullable; import java.util.ArrayList; -import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; @@ -388,29 +382,6 @@ public void onSaveEditedMessage(ChatMessage chatMessage, String editedText) { } } - void onOpenMoreOptions(Node owner, ChatMessage chatMessage, Runnable onClose) { - if (chatMessage.equals(model.getSelectedChatMessageForMoreOptionsPopup().get())) { - return; - } - model.getSelectedChatMessageForMoreOptionsPopup().set(chatMessage); - - List items = new ArrayList<>(); - items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.copyMessage"), - () -> onCopyMessage(chatMessage))); - if (!model.isMyMessage(chatMessage)) { - if (chatMessage instanceof PublicChatMessage) { - items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.ignoreUser"), - () -> onIgnoreUser(chatMessage))); - } - items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.reportUser"), - () -> onReportUser(chatMessage))); - } - - BisqPopupMenu menu = new BisqPopupMenu(items, onClose); - menu.setAlignment(BisqPopup.Alignment.LEFT); - menu.show(owner); - } - public void onReportUser(ChatMessage chatMessage) { ChatChannelDomain chatChannelDomain = model.getSelectedChannel().get().getChatChannelDomain(); if (chatMessage instanceof PrivateChatMessage) { diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java index 5509e0fc86..1e5e6648ea 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/ChatMessagesListModel.java @@ -34,7 +34,6 @@ public class ChatMessagesListModel implements bisq.desktop.common.view.Model { private final BooleanProperty layoutChildrenDone = new SimpleBooleanProperty(); private final BooleanProperty isPublicChannel = new SimpleBooleanProperty(); - private final ObjectProperty selectedChatMessageForMoreOptionsPopup = new SimpleObjectProperty<>(null); private final ChatChannelDomain chatChannelDomain; @Setter private Predicate>> searchPredicate = e -> true; diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/BubbleMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/BubbleMessageBox.java index cae9206343..7239e12719 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/BubbleMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/BubbleMessageBox.java @@ -25,6 +25,7 @@ import bisq.desktop.common.utils.ClipboardUtil; import bisq.desktop.common.utils.ImageUtil; import bisq.desktop.components.controls.BisqTooltip; +import bisq.desktop.components.controls.DropdownMenu; import bisq.desktop.main.content.chat.message_container.list.ChatMessageListItem; import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListController; import bisq.desktop.main.content.chat.message_container.list.ChatMessagesListModel; @@ -61,6 +62,7 @@ public abstract class BubbleMessageBox extends MessageBox { protected Label supportedLanguages, userName, dateTime, message; protected HBox userNameAndDateHBox, messageBgHBox, messageHBox; protected VBox userProfileIconVbox; + protected DropdownMenu moreOptionsMenu; public BubbleMessageBox(ChatMessageListItem> item, ListView>> list, @@ -133,7 +135,7 @@ protected void addReactionsHandlers() { private void addOnMouseEventHandlers() { setOnMouseEntered(e -> { - if (model.getSelectedChatMessageForMoreOptionsPopup().get() != null) { + if (moreOptionsMenu != null && moreOptionsMenu.getIsMenuShowing().get()) { return; } dateTime.setVisible(true); @@ -141,7 +143,7 @@ private void addOnMouseEventHandlers() { }); setOnMouseExited(e -> { - if (model.getSelectedChatMessageForMoreOptionsPopup().get() == null) { + if (moreOptionsMenu == null || !moreOptionsMenu.getIsMenuShowing().get()) { dateTime.setVisible(false); reactionsHBox.setVisible(false); } @@ -152,6 +154,7 @@ private void addOnMouseEventHandlers() { public void cleanup() { setOnMouseEntered(null); setOnMouseExited(null); + showHighlightedPin.unsubscribe(); } diff --git a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/PeerOfferMessageBox.java b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/PeerOfferMessageBox.java index 0482fc4d87..f2eef7c853 100644 --- a/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/PeerOfferMessageBox.java +++ b/apps/desktop/desktop/src/main/java/bisq/desktop/main/content/chat/message_container/list/message_box/PeerOfferMessageBox.java @@ -49,8 +49,9 @@ public PeerOfferMessageBox(ChatMessageListItem> item, ListView>> list, @@ -51,7 +50,7 @@ public PeerTextMessageBox(ChatMessageListItem onOpenMoreOptions(pmIcon, chatMessage, () -> { - reactionsHBox.setVisible(false); - model.getSelectedChatMessageForMoreOptionsPopup().set(null); - })); + replyIcon.setOnMouseClicked(e -> controller.onReply(chatMessage)); pmIcon.setOnMouseClicked(e -> controller.onOpenPrivateChannel(chatMessage)); + copyIcon.setOnMouseClicked(e -> onCopyMessage(chatMessage)); + ignoreUserMenuItem.setOnAction(e -> controller.onIgnoreUser(chatMessage)); + reportUserMenuItem.setOnAction(e -> controller.onReportUser(chatMessage)); replyIcon.setVisible(true); replyIcon.setManaged(true); pmIcon.setVisible(chatMessage instanceof PublicChatMessage); pmIcon.setManaged(chatMessage instanceof PublicChatMessage); + + isMenuShowingPin = EasyBind.subscribe(moreOptionsMenu.getIsMenuShowing(), isShowing -> { + if (!isShowing && !isHover()) { + dateTime.setVisible(false); + reactionsHBox.setVisible(false); + } + }); } protected void setUpPeerMessage() { @@ -110,28 +126,6 @@ protected void setUpPeerMessage() { messageBgHBox.getChildren().setAll(userProfileIconVbox, messageVBox); } - private void onOpenMoreOptions(Node owner, ChatMessage chatMessage, Runnable onClose) { - if (chatMessage.equals(model.getSelectedChatMessageForMoreOptionsPopup().get())) { - return; - } - model.getSelectedChatMessageForMoreOptionsPopup().set(chatMessage); - - List items = new ArrayList<>(); - items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.copyMessage"), - () -> onCopyMessage(chatMessage))); - - if (chatMessage instanceof PublicChatMessage) { - items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.ignoreUser"), - () -> controller.onIgnoreUser(chatMessage))); - } - items.add(new BisqPopupMenuItem(Res.get("chat.message.contextMenu.reportUser"), - () -> controller.onReportUser(chatMessage))); - - BisqPopupMenu menu = new BisqPopupMenu(items, onClose); - menu.setAlignment(BisqPopup.Alignment.LEFT); - menu.show(owner); - } - @Override public void cleanup() { super.cleanup(); @@ -142,8 +136,12 @@ public void cleanup() { userProfileIcon.setOnMouseClicked(null); replyIcon.setOnMouseClicked(null); pmIcon.setOnMouseClicked(null); - moreOptionsIcon.setOnMouseClicked(null); + copyIcon.setOnMouseClicked(null); + ignoreUserMenuItem.setOnAction(null); + reportUserMenuItem.setOnAction(null); userProfileIcon.releaseResources(); + + isMenuShowingPin.unsubscribe(); } }