由于嵌套的 MouseWheelListener,MouseEvents 未被处理

问题描述 投票:0回答:1

我调试了一个必须解决的问题,并发现了 Swing 的局限性

如果您的容器收到

MouseEvent
,它不会立即处理它。相反,它将尝试递归地将其处理委托给其子级之一。这种方法的问题是父级可能会将点击处理委托给仅处理滚轮运动的子级,并且单击事件将被忽略

这里是 Swing 库的相关片段(评论大部分是我的)

// java.awt.Container#getMouseEventTargetImpl

// comp is a child of "this", it happens inside a loop
                    if (comp instanceof Container) {
                        Container child = (Container) comp;
                        // recursive call on a container child
                        Component deeper = child.getMouseEventTarget(
                                x - child.x,
                                y - child.y,
                                includeSelf,
                                filter,
                                searchHeavyweightDescendants);
                        // if a child processing MouseEvents is found, it will be the event processor 
                        if (deeper != null) {
                            return deeper;
                        }
                    } else {
                        if (filter.accept(comp)) {
                            // there isn't a deeper target, but this component
                            // is a target
                            return comp;
                        }
                    }
// java.awt.Container.MouseEventTargetFilter

// this filter will return true when called on a MouseWheelListener
// even if the original event was about clicks

    static class MouseEventTargetFilter implements EventTargetFilter {
        static final EventTargetFilter FILTER = new MouseEventTargetFilter();

        private MouseEventTargetFilter() {}

        public boolean accept(final Component comp) {
            return (comp.eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0
                || (comp.eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0
                || (comp.eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0
                || comp.mouseListener != null
                || comp.mouseMotionListener != null
                || comp.mouseWheelListener != null;
        }
    }

我还写了一篇MRE。不需要外部依赖

package demos.popup;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.WindowConstants;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;

/**
 * Right click to trigger a popup.
 */
public class PopupDemo {
    public static void main(String[] args) {
        Container mainPanel = createMainPanel();
        JFrame frame = new JFrame("Popup Demo");
        frame.setContentPane(mainPanel);
        frame.setLocationRelativeTo(null);
        frame.pack();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    private static JComponent createMainPanel() {
        JComponent mainComp = createPanelWithPopup();
        return mainComp;
    }

    private static JPanel createPanelWithPopup() {
        JPanel panel = new JPanel(new BorderLayout()) {
            @Override
            public String toString() {
                return "Popup Panel";
            }
        };
//         uncomment this to "break" the popup
//        panel.add(createScrollPane());
        panel.setPreferredSize(new Dimension(250,150));
        panel.setComponentPopupMenu(createPopupMenu());
        return panel;
    }

    private static JScrollPane createScrollPane() {
        JScrollPane scroller = new JScrollPane();
        return scroller;
    }

    private static JPopupMenu createPopupMenu() {
        JPopupMenu popupMenu = new JPopupMenu();
        popupMenu.add(createTestMenuItem());
        return popupMenu;
    }

    private static JMenuItem createTestMenuItem() {
        JMenuItem menuItem = new JMenuItem("Test menu item");
        menuItem.addActionListener(e -> System.out.println("Test menu item triggered..."));
        return menuItem;
    }
}

我需要将一个弹出窗口附加到包含许多(动态)

JScrollPane
的面板。右键单击由滚动窗格“处理”,事实上被忽略

如何解决这个问题?

java swing
1个回答
0
投票

致电

scroller.setInheritsPopupMenu(true)
应该可以修复它:

    private static JScrollPane createScrollPane() {
        JScrollPane scroller = new JScrollPane();
        scroller.setInheritsPopupMenu(true);
        return scroller;
    }

JScrollPane 仍然会消耗 MouseEvent,但现在当有人咨询时它会查看其父级

scroller.getComponentPopupMenu()

© www.soinside.com 2019 - 2024. All rights reserved.