1

I am developing a code editor with a code completion tool. My goal is to have a small internal frame appear once it gets activated. But now I have a problem where the layer methods of the JDesktopPane don't seem to work.

Code might not make much sense because I chopped it down to a minimal reproducible example.

Main.java

package questions;

import javax.swing.*;

public class Main {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException | InstantiationException |
                    IllegalAccessException | UnsupportedLookAndFeelException e) {
                e.printStackTrace();
            }
            JavaEditor editor = new JavaEditor(true); // "true" means it will be windowed.
            editor.display();
        });
    }
}

JavaEditor.java

package questions;

public final class JavaEditor extends CodeEditor {

    private CodeCompletion codeCompletion;

    public JavaEditor(boolean windowed) {
        super(windowed);
        this.codeCompletion = new CodeCompletion(this);
        codeCompletion.enable();

        // Loop for debugging, prints the classes found added to the JDesktopPane.
        for (int i = 0; i < this.getRootPane().getComponents().length; i++) {
            System.out.println(getRootPane().getComponent(i).getClass());
        }
    }

}

CodeEditor.java

package questions;

import javax.swing.*;
import java.awt.*;

public abstract class CodeEditor {

    protected final String title = "Java Editor";

    private JFrame frame;

    private JDesktopPane rootPane;

    private JSplitPane mainSplitPane;

    private JPanel sidePanel;

    private JScrollPane editorScrollPane;

    private JScrollPane sideScrollPane;

    protected JEditorPane editorPane;

    private JToolBar toolBar;

    private LayoutManager layoutManager = new BorderLayout();

    private boolean windowed;

    protected CodeEditor(boolean windowed) {
        this.windowed = windowed;
        createComponents();
        applyComponentHierarchy();
    }

    protected void createComponents() {
        if (windowed) frame = new JFrame(title);
        rootPane = new JDesktopPane();
        sidePanel = new JPanel();
        editorPane = new JEditorPane();
        editorScrollPane = new JScrollPane();
        sideScrollPane = new JScrollPane();
        mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sideScrollPane, editorScrollPane);
        toolBar = new JToolBar();
    }

    protected void applyComponentHierarchy() {
        if (windowed) {
            frame.setContentPane(rootPane);
        }
        editorScrollPane.setViewportView(editorPane);
        sideScrollPane.setViewportView(sidePanel);
        rootPane.setLayout(layoutManager);
        rootPane.setLayer(toolBar, 0);
        rootPane.setLayer(mainSplitPane, 0);
        rootPane.add(toolBar, BorderLayout.NORTH);
        rootPane.add(mainSplitPane, BorderLayout.CENTER);
    }

    public JDesktopPane getRootPane() {
        return rootPane;
    }

    public void display() {
        if (!windowed) throw new IllegalStateException("Editor must be in windowed mode to be displayed.");
        frame.setPreferredSize(new Dimension(1000, 600));
        frame.pack();
        frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.requestFocus();
        frame.setVisible(true);
    }

}

CodeCompletion.java

package questions;

import javax.swing.*;
import java.awt.*;

public class CodeCompletion {

    private JInternalFrame completionFrame;

    private JScrollPane mainPanel;

    private JList<String> completionList;

    private DefaultListModel<String> listModel = new DefaultListModel<>();

    private CodeEditor editor;

    public CodeCompletion(CodeEditor editor) {
        this.editor = editor;

        // Parameters are: title, resizeable, closable, maximizable, iconifiable
        this.completionFrame = new JInternalFrame("Title", false, false, false, false);
        this.mainPanel = new JScrollPane();
        this.completionList = new JList<>();

        mainPanel.setViewportView(completionList);
        completionFrame.setContentPane(mainPanel);
    }

    public void enable() {
        completionList.setModel(listModel);
        editor.getRootPane().setLayer(completionFrame, 1);
        editor.getRootPane().add(completionFrame);
        editor.getRootPane().revalidate();
        completionFrame.setSize(new Dimension(100, 100));
        completionFrame.pack();
    }

    public void disable() {
        completionFrame.dispose();
    }

}

The debugging loop in JavaEditor says that all of the components are indeed added to the JDesktopPane, and when I comment the line editor.getRootPane().add(completionFrame);, it displays the other two components correctly. What is happening?

CLR 123
  • 161
  • 1
  • 8
  • 1
    There is no need for the setLayer() method. Read the section from the Swing tutorial on [How to Use Internal Frames](https://docs.oracle.com/javase/tutorial/uiswing/components/internalframe.html). You need to make the frame visible and selected for it to be displayed on top of other internal frames in the desktop pane. Also, please don't call your "desktop pane" a "root pane". It is confusing since a JFrame does actually have a JRootPane (see: [How to Use Root Panes](https://docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html)). Why not just call it a desktop pane? – camickr Jun 10 '21 at 21:59
  • Oh I see. In the original code I have a `show()` method, but it was only called when the text editor fired a key event, not when the frame was actually shown. Thanks! (Also I will rename it to "desktopPane", I put "rootPane" probably because of its function on the program) – CLR 123 Jun 10 '21 at 22:11

0 Answers0