JTextArea-Dialog, поскольку JTable-CellEditor пропускает первый набранный символ - PullRequest
0 голосов
/ 09 мая 2018

Нам нужно CellEditor для JTable для редактирования большого многострочного текста. Мы попытались использовать всплывающее окно, визуально расширяющее TableCell, которое перекрывало ячейки справа и снизу. Это приводило к различным проблемам, если ячейка находилась в правом нижнем углу, около границ экрана и т. Д.

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

Теперь проблемы начались ;-)

Мы не можем "перенаправить" первый набранный символ в диалог. Существует много примеров переполнения стека, где эта проблема решается для пользовательских CellEditor s, которые отображаются непосредственно в таблице (ячейке), например, здесь: Потеря первого символа в редакторе ячеек на основе панели JTable

Следующий SSCCE (из ответа camickrs: https://stackoverflow.com/a/3591230/361227) показывает, что первое нажатие клавиши во втором TableColumn теряется большую часть времени.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Frame;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

/**
 * Example taken from this answer: https://stackoverflow.com/a/3591230/361227
 * 
 * @author camickr
 */
public class TablePopupEditor extends DefaultCellEditor
{
  private PopupDialog popup;
  private String      currentText = "";
  private JButton     editorComponent1;

  public TablePopupEditor()
  {
    super( new JTextField() );

    setClickCountToStart( 2 );

    //  Use a JButton as the editor component
    editorComponent1 = new JButton();
    editorComponent1.setBackground( Color.white );
    editorComponent1.setBorderPainted( false );
    editorComponent1.setContentAreaFilled( false );

    //  Set up the dialog where we do the actual editing
    popup = new PopupDialog();
  }

  @Override
  public Object getCellEditorValue()
  {
    return currentText;
  }

  @Override
  public Component getTableCellEditorComponent( JTable table, Object value, boolean isSelected, int row, int column )
  {

    SwingUtilities.invokeLater( new Runnable()
    {
      @Override
      public void run()
      {
        System.out.println( "run" );
        popup.setText( currentText );
        Point p = editorComponent1.getLocationOnScreen();
        popup.setLocation( p.x, p.y + editorComponent1.getSize().height );
        popup.setVisible( true );
        fireEditingStopped();
      }
    } );

    currentText = value.toString();
    editorComponent1.setText( currentText );
    return editorComponent1;
  }

  /*
  *   Simple dialog containing the actual editing component
  */
  class PopupDialog extends JDialog implements ActionListener
  {
    private JTextArea textArea;

    public PopupDialog()
    {
      super( (Frame) null, "Change Description", true );

      textArea = new JTextArea( 5, 20 );
      textArea.setLineWrap( true );
      textArea.setWrapStyleWord( true );
      KeyStroke keyStroke = KeyStroke.getKeyStroke( "ENTER" );
      textArea.getInputMap().put( keyStroke, "none" );
      JScrollPane scrollPane = new JScrollPane( textArea );
      getContentPane().add( scrollPane );

      JButton cancel = new JButton( "Cancel" );
      cancel.addActionListener( this );
      JButton ok = new JButton( "Ok" );
      ok.setPreferredSize( cancel.getPreferredSize() );
      ok.addActionListener( this );

      JPanel buttons = new JPanel();
      buttons.add( ok );
      buttons.add( cancel );
      getContentPane().add( buttons, BorderLayout.SOUTH );
      pack();

      getRootPane().setDefaultButton( ok );
    }

    public void setText( String text )
    {
      textArea.setText( text );
    }

    /*
    *   Save the changed text before hiding the popup
    */
    @Override
    public void actionPerformed( ActionEvent e )
    {
      if ( "Ok".equals( e.getActionCommand() ) )
      {
        currentText = textArea.getText();
      }

      textArea.requestFocusInWindow();
      setVisible( false );
    }
  }

  public static void main( String[] args )
  {
    String[] columnNames = { "Item", "Description" };
    Object[][] data =
        { { "Item 1", "Description of Item 1" }, { "Item 2", "Description of Item 2" }, { "Item 3", "Description of Item 3" } };

    JTable table = new JTable( data, columnNames );
    table.getColumnModel().getColumn( 1 ).setPreferredWidth( 300 );
    table.setPreferredScrollableViewportSize( table.getPreferredSize() );
    JScrollPane scrollPane = new JScrollPane( table );

    // Use the popup editor on the second column
    TablePopupEditor popupEditor = new TablePopupEditor();
    table.getColumnModel().getColumn( 1 ).setCellEditor( popupEditor );

    JFrame frame = new JFrame( "Popup Editor Test" );
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    frame.getContentPane().add( scrollPane );
    frame.pack();
    frame.setLocationRelativeTo( null );
    frame.setVisible( true );
  }
}

Есть ли надежный способ поймать первого персонажа?

1 Ответ

0 голосов
/ 09 мая 2018

Я даже не могу понять, как персонаж добавляется в текстовую область. Я пробовал около 200 раз вызвать редактор, и он появился только один раз. Так что, очевидно, существует некоторая временная проблема. Случайные проблемы, подобные этой, обычно являются признаком того, что код не выполняется в EDT.

В любом случае я придумал работу вокруг:

public Component getTableCellEditorComponent(
    JTable table, Object value, boolean isSelected, int row, int column)
{
    AWTEvent event = EventQueue.getCurrentEvent();

    SwingUtilities.invokeLater(new Runnable()
    {
        public void run()
        {
            String append = "";

            if (event.getID() == KeyEvent.KEY_PRESSED)
            {
                KeyEvent ke = (KeyEvent)event;
                String keyText = ke.getKeyText(ke.getKeyCode());

                if (keyText.length() == 1)
                    append += ke.getKeyChar();
            }

            popup.setText(currentText + append);
            //popup.setLocationRelativeTo( editorComponent );
            Point p = editorComponent.getLocationOnScreen();
            popup.setLocation(p.x, p.y + editorComponent.getSize().height);
            popup.show();
            fireEditingStopped();
        }
    });

    currentText = value.toString();
    editorComponent.setText( currentText );
    return editorComponent;
}

Приведенный выше код сохраняет событие, которое использовалось для вызова редактора. Поэтому, когда отображается всплывающее окно, он может проверить наличие ключевого события и получить нажатый символ.

...