Swing/EditableTitledBorder の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/EditableTitledBorder へ行く。
- Swing/EditableTitledBorder の差分を削除
--- category: swing folder: EditableTitledBorder title: TitledBorderのタイトルを直接編集する tags: [TitledBorder, GlassPane, JTextField] author: aterai pubdate: 2018-07-23T17:10:16+09:00 description: TitledBorderのタイトルをダブルクリックすると、GlassPaneに配置したJTextFieldをその上に表示して文字列を編集可能にします。 image: https://drive.google.com/uc?id=1Js7r-iMG7F3VdWkVGB0A6jcfSadZCxBz3A --- * 概要 [#summary] `TitledBorder`のタイトルをダブルクリックすると、`GlassPane`に配置した`JTextField`をその上に表示して文字列を編集可能にします。 #download(https://drive.google.com/uc?id=1Js7r-iMG7F3VdWkVGB0A6jcfSadZCxBz3A) * サンプルコード [#sourcecode] #code(link){{ class EditableTitledBorder extends TitledBorder implements MouseListener { protected final Container glassPane = new EditorGlassPane(); protected final JTextField editor = new JTextField(); protected final JLabel dummy = new JLabel(); protected final JLabel renderer = new JLabel(); protected final Rectangle rect = new Rectangle(); protected Component comp; protected final Action startEditing = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { if (comp instanceof JComponent) { Optional.ofNullable(((JComponent) comp).getRootPane()) .ifPresent(r -> r.setGlassPane(glassPane)); } glassPane.add(editor); glassPane.setVisible(true); Point p = SwingUtilities.convertPoint(comp, rect.getLocation(), glassPane); rect.setLocation(p); rect.grow(2, 2); editor.setBounds(rect); editor.setText(getTitle()); editor.selectAll(); editor.requestFocusInWindow(); } }; protected final Action cancelEditing = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { glassPane.setVisible(false); } }; protected final Action renameTitle = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { if (!editor.getText().trim().isEmpty()) { setTitle(editor.getText()); } glassPane.setVisible(false); } }; // ... }} * 解説 [#explanation] 上記のサンプルでは、`TitledBorder`のタイトルをマウスでダブルクリックすると`GlassPane`に配置した`JTextField`を重ねて表示されてタイトル文字列が編集可能になります。 - タイトル文字列をダブルクリックで編集開始 - `JTextField`にフォーカスがある状態でKBD{Enter}キーを入力すると編集完了 - `JTextField`にフォーカスがある状態でKBD{ESC}キーで編集をキャンセル ---- - `TitledBorder`のタイトルは`JLabel`を使用して描画されているが`private`なのでその位置やサイズの取得が不可 - 以下のように`TitledBorder#paintBorder(...)`メソッドから`JLabel`の位置とサイズを計算しているコードを抜き出してその領域を取得 - 所得した領域内でダブルクリックされたら編集を開始し`JTextField`の表示位置とサイズをその領域に合わせて`GlassPane`上に表示 #code{{ private Rectangle getTitleBounds(Component c, int x, int y, int width, int height) { String title = getTitle(); if (Objects.nonNull(title) && !title.isEmpty()) { Border border = getBorder(); int edge = border instanceof TitledBorder ? 0 : EDGE_SPACING; JLabel label = getLabel(c); Dimension size = label.getPreferredSize(); Insets insets = makeBorderInsets(border, c, new Insets(0, 0, 0, 0)); int labelY = y; int labelH = size.height; int position = getTitlePosition(); switch (position) { case ABOVE_TOP: insets.left = 0; insets.right = 0; break; case TOP: insets.top = edge + insets.top / 2 - labelH / 2; if (insets.top >= edge) { labelY += insets.top; } break; case BELOW_TOP: labelY += insets.top + edge; break; case ABOVE_BOTTOM: labelY += height - labelH - insets.bottom - edge; break; case BOTTOM: labelY += height - labelH; insets.bottom = edge + (insets.bottom - labelH) / 2; if (insets.bottom >= edge) { labelY -= insets.bottom; } break; case BELOW_BOTTOM: insets.left = 0; insets.right = 0; labelY += height - labelH; break; default: break; } insets.left += edge + TEXT_INSET_H; insets.right += edge + TEXT_INSET_H; int labelX = x; int labelW = width - insets.left - insets.right; if (labelW > size.width) { labelW = size.width; } switch (getJustification(c)) { case LEFT: labelX += insets.left; break; case RIGHT: labelX += width - insets.right - labelW; break; case CENTER: labelX += (width - labelW) / 2; break; default: break; } return new Rectangle(labelX, labelY, labelW, labelH); } return new Rectangle(); } }} * 参考リンク [#reference] - [[JTabbedPaneのタブタイトルを変更>Swing/EditTabTitle]] - [[TitledBorderにタイトル文字列までの内余白を設定する>Swing/TitledBorderHorizontalInsetOfText]] * コメント [#comment] #comment #comment