概要

JTextAreaを幅固定、文字列の長さに応じた折り返しで高さ伸縮可能に設定し、これをJFrameに配置して親枠外でも表示可能なJListセルラベルエディタとして使用します。

サンプルコード

// private final Container glassPane = new EditorGlassPane();
// private final JPopupMenu popup = new JPopupMenu();
private final JFrame window = new JFrame();
// private final JTextField editor = new JTextField();
private final JTextArea editor = new JTextArea();
// ...
window.setUndecorated(true);
window.setAlwaysOnTop(true);
window.addWindowListener(new WindowAdapter() {
  @Override public void windowDeactivated(WindowEvent e) {
    if (editor.isEditable()) {
      renameTitle.actionPerformed(new ActionEvent(editor, ActionEvent.ACTION_PERFORMED, ""));
    }
  }
});
window.add(editor);
editor.setBorder(BorderFactory.createLineBorder(Color.BLACK));
editor.setLineWrap(true);
editor.setFont(UIManager.getFont("TextField.font"));
editor.setComponentPopupMenu(new TextComponentPopupMenu());
editor.getDocument().addDocumentListener(new DocumentListener() {
  private int prev = -1;
  private void update() {
    EventQueue.invokeLater(() -> {
      int h = editor.getPreferredSize().height;
      if (prev != h) {
        Rectangle rect = editor.getBounds();
        rect.height = h;
        editor.setBounds(rect);
        window.pack(); // popup.pack();
        editor.requestFocusInWindow();
      }
      prev = h;
    });
  }

  @Override public void insertUpdate(DocumentEvent e) {
    update();
  }

  @Override public void removeUpdate(DocumentEvent e) {
    update();
  }

  @Override public void changedUpdate(DocumentEvent e) {
    update();
  }
});
View in GitHub: Java, Kotlin

解説

  • GlassPane + JTextField
  • GlassPane + JTextArea
    • JTextArea#setLineWrap(true)を設定すれば、幅固定で文字列長に応じて行方向に拡大縮小が可能になる
      • JTextAreaの折り返しが変化すると推奨サイズの高さが更新される
    • JTextAreaを使用するため中央揃えが不可
    • 行数が増加して親JFrame外になるとエディタが途切れてしまう
  • JPopupMenu + JTextArea
    • JFrame外にエディタを配置可能
    • エディタの折り返しが変化するとJPopupMenu#pack()を使用してJPopupMenuのサイズをJTextAreaと同じになるよう更新
      • JPopupMenu#pack()を実行すると子のJTextAreaからフォーカスが移動してしまうのでJTextArea#requestFocusInWindow()で再設定する必要がある
      • JFrame外に表示さている場合(HeavyWeightWindow(JWindow)に配置されている場合)一瞬親JFrameタイトルバーの描画などが乱れる
    • JPopupMenuPopupMenuListenerを追加してエディタでの編集のコミットを実行
    • JPopupMenu内のJTextAreaからJPopupMenuを開くことができない
  • JFrame + JTextArea
    • JFrame#setUndecorated(true)JFrame#setAlwaysOnTop(true)を設定したJFrameJPopupMenuの代わりに使用
      • JFrameではなくJWindowを使用すると子コンポーネントがフォーカスが取得できない JWindow内にフォーカス可能なコンポーネントを配置するのように所有フレームを表示中のJFrameを指定してJWindowを作成すれば子コンポーネントがフォーカス取得可能になる
      • JFrameがアクティブWindowでなくなるためグローバルフォーカスが外れる
    • JFrame内のJTextAreaからJPopupMenuを開くことが可能になる
    • JFrameWindowListenerを追加してエディタでの編集のコミットを実行
      • 編集キャンセルの場合はコミットせずにJFrameを非表示にするよう注意が必要
    • JFrameのタイトルバーをクリックしても編集終了できない

参考リンク

コメント