0

I have a class called SuggestionField It will show the user a list of items to autocomplete his input. he let of inputs is displayed in a JDialog(suggestionFrame). I need to set the JDialogs position to be below its parent, but I can not get the parents (JTextField) X and Y relative to the screen.

Example of the issue

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Arrays;

public class Runner {
    public static void main(String[] args) {

        JFrame frame = new JFrame();
        String[] items = new String[]{"Tiger", "Wolf", "Car", "Cat", "Space", "Sing", "Scene"};
        SuggestionField suggestionField = new SuggestionField(new ArrayList<>(Arrays.asList(items)), frame);

        frame.setDefaultCloseOperation(3);
        frame.setSize(100, 65);
        frame.setLocationRelativeTo(null);

        JPanel panel = new JPanel();

        panel.setLayout(new GridLayout(1,1));
        panel.add(suggestionField);

        frame.add(panel);

        frame.setVisible(true);

    }


    public static class SuggestionField extends JTextField implements DocumentListener, KeyListener {

        String[] values = new String[0];
        ArrayList<String> displayValues = new ArrayList<>(0);
        JDialog suggestionFrame;
        JPanel suggestionPanel = new JPanel();
        Color backGround = new Color(109, 104, 104, 133);
        Color selectBackGround = new Color(109, 104, 104, 133);
        Color textColor = new Color(5, 19, 88, 255);
        Color selectTextColor = new Color(115, 134, 238, 255);
        BoxLayout bl = new BoxLayout(suggestionPanel, BoxLayout.Y_AXIS);
        int selectedEntry = 0;

        public SuggestionField(ArrayList<String> values, JFrame parentDisplay) {
            this.getDocument().addDocumentListener(this);
            this.addKeyListener(this);
            suggestionFrame = new JDialog(parentDisplay);
            suggestionFrame.add(suggestionPanel);
            suggestionFrame.setUndecorated(true);
            suggestionFrame.setAlwaysOnTop(true);
            suggestionFrame.setFocusable(false);
            suggestionPanel.setFocusable(false);
            bl.maximumLayoutSize(suggestionPanel);
            this.values = values.toArray(this.values).clone();
        }

        public boolean updateSuggestions() {
            boolean added = false;
            boolean add;
            displayValues.clear();
            for (int i = 0;i < values.length;i++) {
                add = true;
                for (int k = 0;k < this.getText().length() && k < values[i].length();k++) {
                    if (values[i].toUpperCase().charAt(k) != this.getText().toUpperCase().charAt(k)) {
                        add = false;
                        break;
                    }
                }
                if (add && !values[i].equalsIgnoreCase(this.getText()) && values[i].length() > this.getText().length()) {
                    added = true;
                    displayValues.add(values[i]);
                }
            }
            return added;
        }

        private void updateDisplay() {

            suggestionFrame.setSize(this.getWidth(), 16 * displayValues.size());
            suggestionPanel.removeAll();
            suggestionPanel.setLayout(new BoxLayout(suggestionPanel, BoxLayout.Y_AXIS));
            for (int i = 0;i < displayValues.size();i++) {
                JLabel a = new JLabel(displayValues.get(i));
                if (i == selectedEntry) {
                    a.setBackground(selectBackGround);
                    a.setForeground(selectTextColor);
                } else {
                    a.setBackground(backGround);
                    a.setForeground(textColor);
                }
                suggestionPanel.add(a);
            }
            suggestionPanel.revalidate();
            suggestionFrame.revalidate();
            suggestionFrame.setLocation(this.getX(), this.getY() + this.getHeight());
            suggestionFrame.setVisible(true);
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            if (updateSuggestions()) {
                updateDisplay();
            } else suggestionFrame.setVisible(false);
            selectedEntry = 0;
            this.requestFocus();
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            if (updateSuggestions()) {
                updateDisplay();
            } else suggestionFrame.setVisible(false);
            selectedEntry = 0;
            this.requestFocus();
        }

        @Override
        public void changedUpdate(DocumentEvent e) {

        }

        @Override
        public void keyTyped(KeyEvent e) {

        }

        @Override
        public void keyPressed(KeyEvent e) {
            int ID = e.getKeyCode();
            switch (ID) {

                case 40:
                    if (selectedEntry < displayValues.size() - 1) selectedEntry++;
                    updateDisplay();
                    break;

                case 38:
                    if (selectedEntry > 0) selectedEntry--;
                    updateDisplay();
                    break;

                case 10:
                    this.setText(displayValues.get(selectedEntry));
                    break;

                case 27:
                    suggestionFrame.setVisible(false);
                    break;

            }
            this.requestFocus();
            this.grabFocus();
        }

        @Override
        public void keyReleased(KeyEvent e) {

        }

    }
}

Is there any way that inside the updateDisplay() (line 97) method I could get the absolute position of the JTextField for suggestionFrame

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Parzavil
  • 388
  • 3
  • 13
  • *"Getting the position of a swingComponent relative to the screen"* 1) See [What is the XY problem?](http://meta.stackexchange.com/q/66377) 2) For better help sooner, [edit] to add a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). – Andrew Thompson Jun 17 '20 at 09:01
  • 1
    Thankyou @AndrewThompson I've updated the question with a self-contained version of the issue and clarified my question. – Parzavil Jun 17 '20 at 09:26

2 Answers2

2

You can use SwingUtilities.convertPointToScreen to do this.

Just pass (x, y+height) and your input field to get the starting point to display your dropdown.

    JButton component = new JButton();
    Point pt = new Point(component.getLocation());
    pt.y += component.getHeight();
    SwingUtilities.convertPointToScreen(pt, component);

    // pt is now in screen coords... so you can use it to position the pop-up

The full documentation can be found here: https://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html

Lennart Steinke
  • 584
  • 5
  • 11
  • Before I accept this as the answer could you expand on how to use the SwingUtilites.convertPointToScreen, or at least link to the documentation for it please? – Parzavil Jun 17 '20 at 09:59
  • Sure, I just added some example code and the link to the docs. Hope tis helps solving the problem. – Lennart Steinke Jun 17 '20 at 10:15
  • @Parzavil, *or at least link to the documentation for it please?* why do you need a link to the documentation. I'm sure you are capable of reading the API on your own. You should always have a link to the JDK documentation handy. *Before I accept this as the answer* - still waiting for you to accept the answer to your last question: https://stackoverflow.com/a/62306672/131872 – camickr Jun 17 '20 at 14:10
  • @camickr Yes, of course, I found the documentation. I just think it is good practice to link to the documentation for people who are very new to coding as I always found documentation slightly overwhelming and it was always a big help having it linked. Sorry for not accepting your previous answer it must of slipt my mind, thanks for reminding me :D – Parzavil Jun 17 '20 at 14:43
0

I need to set the JDialogs position to be below its parent..

Use Window.setLocationRelativeTo(Component).

This will just place the JDialog ontop of the JTextFiled

Actually, it's more subtle than that. It will take into account if the dialog would otherwise be off the screen, for example, and move it back on. That is something that would need to be explicitly handled, if basing the position on the 'location on screen'.

enter image description here

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433