Domanda

Bear with me. I am making a java console, similar to the DragonConsole found here https://code.google.com/p/dragonconsole/. Everything is going as planned, but I would like to implement a feature that is found inside the DragonConsole. I would like to add to mine the ability to have the vertical scroll bar to extend to the bottom of the frame, like it does in the DC.

DragonConsole example

Here is mine.

My console

As you can see the vertical scroll bar doesn't extend to the bottom, and IMO, it looks unprofessional. But im still an amateur :D

Here is how my program is organized: the output is a jScrollPane, and the input is a simple jTextField. The user enters the input, and the resulting output is displayed.

Now, I've looked through the DragonConsole's source, and its way to complicated for me to tell how this stuff functions. From what I know there is a jTextArea where the user inputs the command, but I don't know how the vertical scroll bar functions like it does.

Can somone please help me mimic the DragonConsole's scroll bar?

Also as you can see, the input on the DC moves along with the vertical scroll bar. How can I do this as well.

enter image description here

The input area is no where to be seen...


The "cls" command calls this function:

public void clear() {
        console.setText("");
        print(""); // printing will automatically print the '> ' at the beginning
        startIndex = console.getText().length();
    }

Print function:

public void print(String s) { // prints output to the console
        console.append(System.lineSeparator() + prompt + s);
    }

Error produced:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: bad position: 110
    at javax.swing.text.JTextComponent.setCaretPosition(JTextComponent.java:1678)   at javax.swing.text.JTextComponent.setCaretPosition(JTextComponent.java:1678)
    at Main.Terminal.caretUpdate(Terminal.java:198)
    at javax.swing.text.JTextComponent.fireCaretUpdate(JTextComponent.java:407)
    at javax.swing.text.JTextComponent$MutableCaretEvent.fire(JTextComponent.java:4417)
    at javax.swing.text.JTextComponent$MutableCaretEvent.stateChanged(JTextComponent.java:4439)
    at javax.swing.text.DefaultCaret.fireStateChanged(DefaultCaret.java:798)
    at javax.swing.text.DefaultCaret.changeCaretPosition(DefaultCaret.java:1273)
    at javax.swing.text.DefaultCaret.handleSetDot(DefaultCaret.java:1169)
    at javax.swing.text.DefaultCaret.setDot(DefaultCaret.java:1150)
    at javax.swing.text.DefaultCaret$Handler.removeUpdate(DefaultCaret.java:1796)
    at javax.swing.text.AbstractDocument.fireRemoveUpdate(AbstractDocument.java:260)
    at javax.swing.text.AbstractDocument.handleRemove(AbstractDocument.java:623)
    at javax.swing.text.AbstractDocument.remove(AbstractDocument.java:591)
    at javax.swing.text.AbstractDocument.replace(AbstractDocument.java:667)
    at javax.swing.text.JTextComponent.setText(JTextComponent.java:1718)
    at Main.Terminal.doCommand(Terminal.java:218)
    at Main.Terminal.keyPressed(Terminal.java:168)
    at java.awt.Component.processKeyEvent(Component.java:6463)
    at javax.swing.JComponent.processKeyEvent(JComponent.java:2829)
    at java.awt.Component.processEvent(Component.java:6282)
    at java.awt.Container.processEvent(Container.java:2229)
    at java.awt.Component.dispatchEventImpl(Component.java:4861)
    at java.awt.Container.dispatchEventImpl(Container.java:2287)
    at java.awt.Component.dispatchEvent(Component.java:4687)
    at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1895)
    at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:762)
    at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1027)
    at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:899)
    at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:727)
    at java.awt.Component.dispatchEventImpl(Component.java:4731)
    at java.awt.Container.dispatchEventImpl(Container.java:2287)
    at java.awt.Window.dispatchEventImpl(Window.java:2719)
    at java.awt.Component.dispatchEvent(Component.java:4687)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:729)
    at java.awt.EventQueue.access$200(EventQueue.java:103)
    at java.awt.EventQueue$3.run(EventQueue.java:688)
    at java.awt.EventQueue$3.run(EventQueue.java:686)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87)
    at java.awt.EventQueue$4.run(EventQueue.java:702)
    at java.awt.EventQueue$4.run(EventQueue.java:700)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:699)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:242)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:150)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:146)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:138)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:91)

It seems to be pointing to a problem here:

@Override
    public void caretUpdate(CaretEvent e) {
        // Ensure that the caret position can only be a valid location
        if (e.getDot() < startIndex) {
            console.setCaretPosition(startIndex);
            Toolkit.getDefaultToolkit().beep();
        }
    }

Code that fixes error

public void clear() {
        startIndex = 0; // set it to zero to prevent error from happening
        console.setText(""); // this will erase anything on screen, but start the input seqence on the next line
    }

enter image description here

Once again thank you for your help @amurka :)

È stato utile?

Soluzione

Looking at the DragonConsole source code, there are two modes it supports: inline and not inline. You're first picture where the scrollbar extends to the bottom is DragonConsole in inline mode.

In this mode it is just a single JTextPane which is then added to aJScrollPane. This is why the scrollbar extends to the bottom. It then adds a KeyListener (keyPressed method) and a CaretListener (caretUpdate function) to the JTextPane. Finally it uses a custom DocumentFilter implemented in the InputController class to get the console behavior you see.

So in summary:

  • Create JTextPane. Look at initializeConsole() in DragonConsole.java
  • Add CaretListener and KeyListener to that text pane. Look at caretUpdate() and keyPressed() in DragonConsole.java
  • Set custom DocumentFilter. Look at InputController.java (Actually setting the document style is done in the inializeConsole method.)

The way you did it is virtually the same as the way DragonConsole did it when NOT in inline mode. In this case, it uses a JTextPane for the output area, and a JTextArea for the input area and should look the same in regards to the scrollbar.

Edit: Here's a super simple example of an inline console.

public class Console extends JPanel implements KeyListener, CaretListener {

    private static final String PROMPT = ">>";

    private JScrollPane scrollPane;
    private JTextArea consoleTextPane;

    private int startIndex;

    public Console() {
        super();

        // Create a text area
        consoleTextPane = new JTextArea();
        consoleTextPane.setText(PROMPT);
        consoleTextPane.setBorder(null);
        // Wraps the text if it goes longer than a line, but NOT on word boundary
        // like a normal console
        consoleTextPane.setLineWrap(true);
        consoleTextPane.setWrapStyleWord(false);

        // Set the initial caret position
        startIndex = consoleTextPane.getText().length();
        consoleTextPane.setCaretPosition(startIndex);

        // Add the caret and key listeners
        consoleTextPane.addCaretListener(this);
        consoleTextPane.addKeyListener(this);

        // Scrollbar, always show the vertical one
        scrollPane = new JScrollPane(consoleTextPane);
        scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        scrollPane.setBorder(null);

        JPanel panelCenter = new JPanel(new BorderLayout());
        panelCenter.setPreferredSize(new Dimension(400, 200));
        panelCenter.add(scrollPane, BorderLayout.CENTER);

        add(panelCenter, BorderLayout.CENTER);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("Console");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.add(new Console());
        frame.pack();
        frame.setVisible(true);
    }

    @Override
    public void keyTyped(KeyEvent e) {
        // All processing in keyPressed
    }

    @Override
    public void keyReleased(KeyEvent e) {
        // All processing in keyPressed
    }

    @Override
    public void keyPressed(KeyEvent e) {
        switch(e.getKeyCode()) {
            case KeyEvent.VK_ENTER:
                // ENTER key was pressed
                // Get the "Command"
                String command = consoleTextPane.getText().substring(startIndex);

                if (!command.isEmpty()) {
                    // TODO: do something with the command
                    consoleTextPane.append(System.lineSeparator() 
                            + "Command Entered: " + command);
                }

                // Update the start index and append a new prompt
                consoleTextPane.append(System.lineSeparator() + PROMPT);
                startIndex = consoleTextPane.getText().length();

                // Consume the ENTER key event so further processing is not
                // performed
                e.consume();
                break;
            case KeyEvent.VK_BACK_SPACE:
                // Make sure this is a valid delete
                if (consoleTextPane.getCaretPosition() <= startIndex) {
                    e.consume();
                    Toolkit.getDefaultToolkit().beep();
                }

                break;
            // TODO: add key presses here as desired
            default:
                //System.out.println("Unhandled: " + e.getKeyCode());
                break;
        }
    }

    @Override
    public void caretUpdate(CaretEvent e) {
        // Ensure that the caret position can only be a valid location
        if (e.getDot() < startIndex) {
            consoleTextPane.setCaretPosition(startIndex);
            Toolkit.getDefaultToolkit().beep();
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top