Вкладка JTabbedPane со слушателем мыши - PullRequest
0 голосов
/ 28 октября 2018

Я пытаюсь создать панель вкладок, где пользователь может дважды щелкнуть вкладку, чтобы изменить ее заголовок.До сих пор мне удавалось создать компонент с вкладками, где JPanel и JTextField накладываются друг на друга, который переключается на JTextField при двойном щелчке на JPanel и обратно на JPanelкогда вы нажимаете Enter :

public class EditablePanel extends JPanel {
    private JLabel label;
    private JTextField field;

    public EditablePanel(String title) {
        super();
        setLayout(new OverlayLayout(this));
        setOpaque(false);

        add(label = new JLabel(title));
        label.setFocusable(false);

        field = new JTextField(title);
        field.setBorder(BorderFactory.createEmptyBorder());
        field.setVisible(false);
        field.addActionListener((e) -> finish(true));
        add(field);

        label.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    label.setVisible(false);
                    field.setVisible(true);
                    field.requestFocusInWindow();
                    field.selectAll();
                }
            }
        });
    }

    private void finish(boolean commit) {
        label.setText(field.getText());
        label.setVisible(true);
        field.setVisible(false);
    }
}

Когда я устанавливаю компонент вкладки в JTabbedPane на экземпляр этого EditablePanel, я больше не могу нажимать на вкладкувыбрать его, если моя мышь находится над этикеткой.Кроме того, если LAF что-то делает, когда мышь наводит курсор на вкладку (например, меняет ее цвет, как в Windows), это прекращает применение, когда мышь находится над меткой.Я могу переключать вкладки, если щелкаю область за пределами JLabel, но не могу, если я щелкаю один раз по JLabel.Мне бы хотелось, чтобы это работало так, чтобы, если я щелкну один раз в любом месте вкладки, она переключится на эту вкладку, но если я дважды щелкну на вкладке, она начнет редактировать заголовок вкладки.

Я пробовалиспользуя getMouseListeners в EditablePanel для перенаправления событий мыши панели на JLabel, но, похоже, они игнорируются.Есть ли способ, которым я могу использовать этот компонент в качестве компонента вкладок, сохраняя при этом существующую функциональность при наведении курсора и нажатии для изменения вкладок?Если нет, то можно ли как-нибудь расширить JTabbedPane, чтобы получить желаемую редактируемую функцию заголовка?

Вот полный SCCM, демонстрирующий мою проблему:

import java.awt.Dimension;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.*;

public class EditablePanel extends JPanel {
    private JLabel label;
    private JTextField field;

    public EditablePanel(String title) {
        super();
        setLayout(new OverlayLayout(this));
        setOpaque(false);

        add(label = new JLabel(title));
        label.setFocusable(false);

        field = new JTextField(title);
        field.setBorder(BorderFactory.createEmptyBorder());
        field.setVisible(false);
        field.addActionListener((e) -> finish(true));
        add(field);

        label.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    label.setVisible(false);
                    field.setVisible(true);
                    field.requestFocusInWindow();
                    field.selectAll();
                }
            }
        });
    }

    private void finish(boolean commit) {
        label.setText(field.getText());
        label.setVisible(true);
        field.setVisible(false);
        field.transferFocusUpCycle();
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("Editable Tab Headers");
        frame.setPreferredSize(new Dimension(400, 300));
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JTabbedPane pane = new JTabbedPane();
        pane.addTab("First Tab", new JLabel("First tab contents"));
        pane.addTab("Second Tab", new JLabel("Second tab contents"));
        pane.setTabComponentAt(0, new EditablePanel("First Tab"));
        pane.setTabComponentAt(1, new EditablePanel("Second Tab"));
        frame.add(pane);

        frame.pack();
        frame.setVisible(true);
    }
}

Ответы [ 2 ]

0 голосов
/ 28 октября 2018

В качестве альтернативы тому, что предложил camickr, вы можете использовать MouseListener, который отправляет все события в JTabbedPane.Это должно достичь желаемого эффекта, не влияя на другое поведение, и по-прежнему обрабатывать особый случай двойного щелчка.

Вот MCVE, где я также исправил поведение редактирования, добавив слушателя фокусаэто в основном отменяет редактирование, когда при редактировании выбирается новая вкладка (т. е. когда редактирование не подтверждается нажатием Enter ).

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.OverlayLayout;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class EditablePanel extends JPanel
{
    private JLabel label;
    private JTextField field;

    public EditablePanel(String title)
    {
        super();
        setLayout(new OverlayLayout(this));
        setOpaque(false);

        add(label = new JLabel(title));
        label.setFocusable(false);

        field = new JTextField(title);
        field.setBorder(BorderFactory.createEmptyBorder());
        field.setVisible(false);
        field.addActionListener((e) -> finish(true));
        field.addFocusListener(new FocusListener()
        {
            @Override
            public void focusLost(FocusEvent e)
            {
                finish(false);
            }

            @Override
            public void focusGained(FocusEvent e)
            {
                // Nothing to do here
            }
        });
        add(field);

        TabMouseAdapter mouseAdapter = new TabMouseAdapter()
        {
            @Override
            public void mouseClicked(MouseEvent e)
            {
                super.mouseClicked(e);
                if (e.getClickCount() == 2)
                {
                    label.setVisible(false);
                    field.setVisible(true);
                    field.requestFocusInWindow();
                    field.selectAll();
                }
            }
            @Override
            public void mousePressed(MouseEvent e)
            {
                super.mousePressed(e);
                finish(false);
            }

        };
        label.addMouseListener(mouseAdapter);
    }

    static class TabMouseAdapter implements MouseListener
    {
        @Override
        public void mouseClicked(MouseEvent e)
        {
            redispatch(e);
        }

        @Override
        public void mousePressed(MouseEvent e)
        {
            redispatch(e);
        }

        @Override
        public void mouseReleased(MouseEvent e)
        {
            redispatch(e);
        }

        @Override
        public void mouseEntered(MouseEvent e)
        {
            redispatch(e);
        }

        @Override
        public void mouseExited(MouseEvent e)
        {
            redispatch(e);
        }

        private void redispatch(MouseEvent e)
        {
            Component source = e.getComponent();
            Component target = source.getParent();
            while (true)
            {
                if (target == null)
                {
                    break;
                }
                if (target instanceof JTabbedPane)
                {
                    break;
                }
                target = target.getParent();
            }
            if (target != null)
            {
                MouseEvent targetEvent =
                    SwingUtilities.convertMouseEvent(source, e, target);
                target.dispatchEvent(targetEvent);
            }
        }
    }

    private void finish(boolean commit)
    {
        if (commit)
        {
            label.setText(field.getText());
        }
        label.setVisible(true);
        field.setVisible(false);
        field.transferFocusUpCycle();
    }

    public static void main(String[] args)
    {
        JFrame frame = new JFrame("Editable Tab Headers");
        frame.setPreferredSize(new Dimension(400, 300));
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JTabbedPane pane = new JTabbedPane();
        pane.addTab("First Tab", new JLabel("First tab contents"));
        pane.addTab("Second Tab", new JLabel("Second tab contents"));
        pane.setTabComponentAt(0, new EditablePanel("First Tab"));
        pane.setTabComponentAt(1, new EditablePanel("Second Tab"));
        frame.add(pane);

        frame.pack();
        frame.setVisible(true);
    }
}
0 голосов
/ 28 октября 2018

есть ли способ расширить JTabbedPane, чтобы получить желаемую редактируемую функцию заголовка?

Я бы добавил MouseListener на панель с вкладками.

Затем вmouseClicked(...) событие, которое вы можете проверить на двойной щелчок и отобразить JTextField поверх вкладки.Когда вы нажимаете ввод в текстовом поле, вы удаляете текстовое поле из панели с вкладками.

Таким образом, основы отображения текстового поля будут:

JTabbedPane tabbedPane = (JTabbedPane)e.getComponent();
TabbedPaneUI ui = tabbedPane.getUI();

int tab = ui.tabForCoordinate(tabbedfPane, e.getX(), e.getY());

if (tab != -1) // I believe -1 is returned if you don't click on a tab
{
    Rectangle bounds = ui.getTabBounds(tabbedPane, tab);
    JTextField textField = new JTextField();
    textField.setText(...);
    textField.setBounds( bounds );
    textField.addActionListener(...);
    tabbedPane.add( textField );
    tappedPane.repaint();
}

Затем в ActionListenerвы получите текст и обновите заголовок вкладки, а затем удалите текстовое поле из области вкладок.

Теперь вкладка должна вести себя как обычно, поскольку текстовое поле отображается только временно, пока вы редактируете текстовый заголовок.

Обратите внимание, что в основном так работает редактор JTable.При двойном щелчке по ячейке текстовое поле добавляется в таблицу поверх ячейки и затем удаляется по окончании редактирования.

...