概要

JEditorPaneのスクロールに連動して表示状態になったリンクと同名のJTreeノードを検索・選択します。

スクリーンショット

Swing/Scrollspy.png

サンプルコード

JScrollPane scroll = new JScrollPane(editor);
scroll.getVerticalScrollBar().getModel().addChangeListener(e -> {
  HTMLDocument.Iterator itr = doc.getIterator(HTML.Tag.A);
  for (; itr.isValid(); itr.next()) {
    try {
      Rectangle r = editor.modelToView(itr.getStartOffset());
      if (r != null && editor.getVisibleRect().contains(r.getLocation())) {
        searchTreeNode(tree, itr.getAttributes().getAttribute(HTML.Attribute.NAME));
        break;
      }
    } catch (BadLocationException ex) {
      UIManager.getLookAndFeel().provideErrorFeedback(editor);
    }
  }
});
view all

解説

上記のサンプルでは、JEditorPaneを配置したJScrollPaneの縦ScrollBarChangeListenerを設定し、表示状態になったリンクと同名のJTreeノードを検索して選択します。

  • リンクが表示されているかはHTMLDocument#getIterator(HTML.Tag.A)で取得した<a>タグの先頭位置がJEditorPane#getVisibleRect()で取得した表示範囲に含まれるかで判断
  • JTree側にはノードをマウスクリックなどで選択したらJEditorPane#scrollToReference(ref)でそのリンクまでスクロールするTreeSelectionListenerを追加しているので、JEditorPane側からのノード選択を実行する場合はそのTreeSelectionListenerが反応しないよう設定する必要がある
    tree.addTreeSelectionListener(e -> {
      if (!tree.isEnabled()) { // JEditorPane側からのノード選択は無視する
        return;
      }
      Object o = e.getNewLeadSelectionPath().getLastPathComponent();
      if (o instanceof DefaultMutableTreeNode) {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) o;
        String ref = Objects.toString(node.getUserObject());
        editor.scrollToReference(ref);
      }
    });
    // ...
    private static void searchTreeNode(JTree tree, Object name) {
      TreeModel model = tree.getModel();
      DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
      Collections.list((Enumeration<?>) root.preorderEnumeration()).stream()
          .filter(DefaultMutableTreeNode.class::isInstance)
          .map(DefaultMutableTreeNode.class::cast)
          .filter(node -> Objects.equals(name, Objects.toString(node.getUserObject())))
          .findFirst()
          .ifPresent(node -> {
            tree.setEnabled(false); // JTreeに設定したTreeSelectionListenerを無効にする
            TreePath path = new TreePath(node.getPath());
            tree.setSelectionPath(path);
            tree.scrollPathToVisible(path);
            tree.setEnabled(true); // JTreeに設定したTreeSelectionListenerを有効に戻す
          });
    }
    

参考リンク

コメント