How do I make the caret of a JTextComponent skip selected text? - java

the normal behaviour of native text fields in many environments is as follows:
Textfield with text "abcdefg". I use the mouse to select "efg" from left to right. The caret is now behind "g". When I move the caret to the left by pressing the cursor left key once, the selection is removed and the caret is right before "e". When I do the same in a JTextField or JTextArea (tested on Mac OS) doing the exact same thing results in the caret being right before "g".
I know how I could change that programmatically by using a KeyListener and registering it with each component but I am looking for a way to change that for my entire application. Is that possible? Is there a Flag, I am not finding or do I have to hack my look and feel?
Thanks

I am looking for a way to change that for my entire application
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class CaretAction extends TextAction
{
private boolean caretForward;
public CaretAction(boolean caretForward)
{
super(caretForward ? "Caret Forward" : "Caret Backward");
this.caretForward = caretForward;
}
public void actionPerformed(ActionEvent e)
{
JTextComponent textComponent = getFocusedComponent();
int start = textComponent.getSelectionStart();
int end = textComponent.getSelectionEnd();
int offset = textComponent.getCaretPosition();
if (start != end)
{
if (caretForward)
offset = (offset == end) ? offset + 1 : end;
else
offset = (offset == start) ? offset -1 : start;
}
else
{
offset += (caretForward) ? 1 : -1;
}
offset = Math.max(offset, 0);
offset = Math.min(offset, textComponent.getDocument().getLength());
textComponent.setCaretPosition( offset );
}
private static void createAndShowUI()
{
JTextField textField1 = new JTextField(10);
JTextField textField2 = new JTextField(10);
JPanel panel = new JPanel();
panel.add( textField1 );
panel.add( textField2 );
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( panel );
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
ActionMap map = (ActionMap)UIManager.get("TextField.actionMap");
map.put(DefaultEditorKit.backwardAction, new CaretAction(false));
map.put(DefaultEditorKit.forwardAction, new CaretAction(true));
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
You would also need to change the ActionMap for JTextArea, JFormattedTextField ...

What you can do is addCaretListener :
anyField.addCaretListener(new CaretListener() {
public void caretUpdate(CaretEvent evt) {
anyFieldCaretUpdate(evt);
}
});
And set the Caret at the last again:
private void anyFieldCaretUpdate(CaretEvent evt) {
anyField.setCaretPosition(anyField.getText().length());
}

Related

command line indicator in JTextArea

I've made a JTextArea where I input different commands and separate them by newline "\n", and if there is an error in one of the lines then I write it in a console output. Here I made a very simple, and not the best solution to make this line indication, but it's a bit buggy.
How I made it
I've defined a textArea where I can type different information/commands, and if one of the commands/lines is invalid I write it in the console just to display something for now. I basically count the lines by splitting the textArea rows up by "\n" and then count which line the error occurs in, and the left consoleLineNum is using the amount of rows in textArea, to then make a string containing all the numbers of rows+"\n".
But here my question is, is this a good enough way? If so, why/how can I make it more robust? Or how can I make this indication with line numbers, in the left? It has to increase each time the user makes a new line in the textArea.
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(width, height);
frame.setLayout(new BorderLayout(3,3));
/*----- Panels -----*/
JPanel panel1 = new JPanel();
JPanel panel2 = new JPanel();
JPanel panel3 = new JPanel();
//Add Components to this panel.
JScrollPane scrollPane = new JScrollPane(textArea);
JScrollPane scrollPaneOutput = new JScrollPane(consoleOutput);
textArea.setCaretPosition(textArea.getDocument().getLength());
consoleOutput.setEditable(false);
consoleLineNum.setEditable(false);
ButtonPanel_listener(buttonPanel);
textArea.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
consoleLineNum.setText("");
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= Integer.parseInt(String.valueOf(textArea.getText().split("\n").length)); i++) {
sb.append(i + " \n");
}
consoleLineNum.setText(sb.toString());
}
#Override
public void keyReleased(KeyEvent e) {
}
});
//Background
panel1.setBackground(Color.LIGHT_GRAY);
//Preferred size
panel1.setPreferredSize(new Dimension(100, 100));
scrollPane.setPreferredSize(new Dimension(200, 200));
panel2.setLayout(new BorderLayout());
panel2.add(scrollPane, BorderLayout.CENTER);
panel2.add(buttonPanel, BorderLayout.SOUTH);
panel2.add(consoleLineNum, BorderLayout.WEST);
consoleLineNum.setText(num);
panel3.setLayout(new BorderLayout());
panel3.add(drawCanvas, BorderLayout.CENTER);
panel1.setLayout(new BorderLayout());
panel1.add(scrollPaneOutput, BorderLayout.CENTER);
//textArea.addActionListener(e -> drawCanvas.drawCircle(250, 250, 200));
//Add contents to the window.
frame.add(panel2, BorderLayout.WEST);
frame.add(panel3, BorderLayout.CENTER);
frame.add(panel1, BorderLayout.SOUTH);
//Display the window.
frame.pack();
frame.setVisible(true);
}
Difference between your expected and actual results
When I hit newline/Enter, it doesn't show the number right away, only when I start typing.
Or
Here I want it to match where the user is, so if the user hit enter, and go to the next line, then the number matches and is shown right away.
If I delete all lines, except some, it still shows the numbers
Here I want it to wipe all the numbers and update it to match the amount of data in textArea.
Tried this, and it works almost as expected. The only problem is that when I delete lines, it's one behind.
#Override
public void keyPressed(KeyEvent e) {
numI = textArea.getLineCount();
if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE && numI > 0) {
numI = numI - 1;
}
if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE || e.getKeyCode() == KeyEvent.VK_ENTER){
StringBuilder sb = new StringBuilder();
for (int i = 0; i <= numI; i++) {
sb.append(i+1 + " \n");
}
consoleLineNum.setText(sb.toString());
}
}
Use a DocumentListener to listen for changes in the text of the JTextArea. The API of JTextArea already provides a method that tells you how many lines it contains, namely getLineCount(). After placing the JTextArea in a JScrollPane, set a JList as the row header for the JScrollPane.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JViewport;
import javax.swing.WindowConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class CountLns implements DocumentListener, Runnable {
private JFrame frame;
private JList<Integer> lineNumbersList;
private JTextArea textArea;
public void changedUpdate(DocumentEvent docEvent) {
// Never called.
}
public void insertUpdate(DocumentEvent docEvent) {
handleDocEvent();
}
public void removeUpdate(DocumentEvent docEvent) {
handleDocEvent();
}
#Override
public void run() {
showGui();
}
private JScrollPane createTextArea() {
textArea = new JTextArea(20, 50);
textArea.getDocument().addDocumentListener(this);
JScrollPane scrollPane = new JScrollPane(textArea);
lineNumbersList = new JList<>(new Integer[]{1});
lineNumbersList.setBackground(Color.cyan);
lineNumbersList.setFont(textArea.getFont());
lineNumbersList.setFixedCellHeight(16);
JViewport rowHeader = new JViewport();
rowHeader.setView(lineNumbersList);
scrollPane.setRowHeader(rowHeader);
return scrollPane;
}
private void handleDocEvent() {
DefaultListModel<Integer> model = new DefaultListModel<>();
List<Integer> lineNumbers = IntStream.range(0, textArea.getLineCount())
.boxed()
.map(i -> i + 1)
.collect(Collectors.toList());
model.addAll(lineNumbers);
lineNumbersList.setModel(model);
}
private void showGui() {
frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(createTextArea(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
textArea.requestFocusInWindow();
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new CountLns());
}
}
Whenever the contents of the JTextArea change, the DocumentListener counts the lines in the JTextArea and sets the JList model to contain exactly that number of elements. So if the JTextArea has 12 lines, the JList will contain all the numbers from 1 to 12.
Try doing it like this:
yourTextArea.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
// Returns true every time the user presses either backspace or enter.
if (e.getKeyChar() == '\n' || e.getKeyChar() == '\b') {
int lines;
// Checks if the text ends with a newline, if so,
// it adds 1 to the line count, so that would make your line
// appear even if the user just started a new line.
if (yourTextArea.getText().endsWith("\n")) {
lines = yourTextArea.getText().split("\n").length + 1;
} else {
lines = yourTextArea.getText().split("\n").length;
}
// Removes previous count.
linePanel.setText("");
// Appends a new line to the area for every line.
for (int i = 0; i < lines; i++) {
linePanel.append((i + 1) + "\n");
}
}
}
});
I did not write in Java for 1 year so sorry, if I messed up syntax
First of all, why won't you implement line wrap and line count increment by pressing Enter? It looks much more simple to me
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER) {
textArea.append("\n");
Integer lineCount = Integer.parseInt(consoleLineNum.getText());
Integer newLineNumber = new Integer(lineCount.intValue() + 1)
consoleLineNum.append("\n" + newLineNumber.toString())
}
}
However you still will not get actual number of lines, if some of them will be deleted. So you can also add trickier logic to your keyPressed() method
if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
// Well I actually googled this line for counting occurences
Integer linesCountActual = textArea.getText().split("\n", -1).length - 1);
Integer linesCountLeft = consoleLineNum.getText().split("\n", -1).length - 1);
if (linesCountActual > linesCountLeft) {
String oldText = consoleLineNum.getText();
String newText = oldText.substring(0, oldText.length() - 2);
consoleLineNum.setText(newText);
}
}

How can a JTextArea's properties be updated by an event?

I have made this simple text editor program but can't figure out how to change GUI component's properties while the program is running.
Suppose this is a part of my Text Editor's source code:
boolean wordwrap = false;
void mainFrame() {
frame = new JFrame("Text Editor");
textArea = new JTextArea(50,20);
textArea.setLineWrap(wordwrap);
and let's say I have an event source(JButton) added as Listener to change
textArea's .setLineWrap(boolean). Just like this:
public void actionPerformed(ActionEvent event) {
if(wordwrap) wordwrap = false;
else wordwrap = true;
textArea.setLineWrap(wordwrap);
frame.repaint();
}
But this code is not working!!. So, what is the correct way to update or edit a JAVA GUI component while the program is running ?
revalidate and validate()
will update the frame.
You do not need to use repaint().
Final Method:
public void actionPerformed(ActionEvent event) {
if(wordwrap) wordwrap = false;
else wordwrap = true;
textArea.setLineWrap(wordwrap);
frame.revalidate(); //is preferable but validate() also works.
}
You can either update the whole frame or just update the jComponent (insert TextArea instead of "frame".revalidate();)
Just FYI, after I got a chance to test it, it works fine without either the revalidate() or the repaint(). I suspect the problem was somewhere else in your code.
public class TestTextArea
{
private final static String testLine =
"This is some rather long line that I came up with for testing a textArea.";
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run()
{
gui();
}
} );
}
private static void gui()
{
JFrame frame = new JFrame();
final JTextArea textArea = new JTextArea();
JPanel span = new JPanel();
JButton toggle = new JButton( "Switch line wrap" );
toggle.addActionListener( new ActionListener()
{
#Override
public void actionPerformed( ActionEvent e )
{
textArea.setLineWrap( !textArea.getLineWrap() );
}
} );
for( int i = 0; i < 10; i++ )
textArea.append( testLine + testLine + "\n" );
span.add( toggle );
frame.add( span, BorderLayout.SOUTH );
frame.add( textArea );
frame.pack();
frame.setSize( 500, 500 );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}

How to attach key binding to up and down arrows of JSplitPane divider?

I have JSplitPane that has oneTouchExpandable set to true.
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
splitPane.setDividerSize(10);
splitPane.setOneTouchExpandable(true);
The problem is that I do not know how to attach key bindings to up and down arrows on JSplitPane's divider. For up arrow I want Ctrl+U and for down - Ctrl + D.
Thanks!
Implementation of the arrow button shown by OneTouchExpandable is UI label and will take extra work unnecessarily to bind them. You can easily use Key Binding on JSplitPane itself to control the JSplitPane divider location using setDividerLocation(int). Increase on Ctrl + U and Decrease on Ctrl + D. For example:
Action incrDividerLoc = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JSplitPane srcSplitPan = (JSplitPane) e.getSource();
(srcSplitPan).setDividerLocation(srcSplitPan.getDividerLocation()+10);
}
};
Action decrDividerLoc = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JSplitPane srcSplitPan = (JSplitPane) e.getSource();
(srcSplitPan).setDividerLocation(srcSplitPan.getDividerLocation()-10);
}
};
jSplitPane1.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK),
"increaseDivider");
jSplitPane1.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK),
"decreaseDivider");
jSplitPane1.getActionMap().put("increaseDivider", incrDividerLoc);
jSplitPane1.getActionMap().put("decreaseDivider", decrDividerLoc);
Note: method A value less than 0 passed to setDividerLocation(int) implies the divider should be reset to a value that attempts to honor the preferred size of the left/top component. After notifying the listeners, the last divider location is updated, via setLastDividerLocation.
The problem is that I do not know how to attach key bindings to up and down arrows on JSplitPane's divider.
Normally you would try to access the Action of the button. In many cases the component will already define an Action that you can use. See Key Bindings for a list of the default bindings for a JSplitPane. Unfortunately there is no Action to support the one touch clicking options.
So we need to access the buttons directly from the UI:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
public class SplitPaneDividerAction extends AbstractAction
{
private boolean leading;
public SplitPaneDividerAction(boolean leading)
{
this.leading = leading;
}
#Override
public void actionPerformed(ActionEvent e)
{
JSplitPane splitPane = (JSplitPane)e.getSource();
BasicSplitPaneUI ui = (BasicSplitPaneUI)splitPane.getUI();
BasicSplitPaneDivider divider = ui.getDivider();
if (leading)
((JButton)divider.getComponent(0)).doClick();
else
((JButton)divider.getComponent(1)).doClick();
}
private static void createAndShowUI()
{
JPanel leading = new JPanel();
leading.setPreferredSize( new Dimension(200, 100) );
leading.setBackground( Color.BLUE );
leading.setFocusable(true);
JPanel trailing = new JPanel();
trailing.setPreferredSize( new Dimension(200, 100) );
trailing.setBackground( Color.RED );
trailing.setFocusable(true);
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leading, trailing);
splitPane.setOneTouchExpandable(true);
splitPane.setDividerLocation(100);
InputMap im = splitPane.getInputMap(JSplitPane.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
ActionMap am = splitPane.getActionMap();
im.put(KeyStroke.getKeyStroke("control U"), "leading");
im.put(KeyStroke.getKeyStroke("control D"), "trailing");
am.put("leading", new SplitPaneDividerAction(true));
am.put("trailing", new SplitPaneDividerAction(false));
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( splitPane );
frame.setSize(200, 200);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Of course this approach will only work if your LAF extends from the BasicSplitPaneUI.

ActionListener for a specific text inside a JTextArea?

I have in my app a chat component which has a JTextArea on it.
Now, how can I add an ActionListener-like event for a specific text (like student://xxxx)?
So when I click on that text (student://xxxx) something will happen.
Thank you.
Here try this small program, try to click at the start of student://, that will pop up a message Dialog
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TextAreaExample extends JFrame
{
private JTextArea tarea = new JTextArea(10, 10);
private JTextField tfield = new JTextField(10);
private void createAndDisplayGUI()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tarea.setText("Hello there\n");
tarea.append("Hello student://");
JScrollPane scroll = new JScrollPane(tarea);
tfield.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
tarea.append(tfield.getText() + "\n");
}
});
tarea.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent me)
{
int x = me.getX();
int y = me.getY();
System.out.println("X : " + x);
System.out.println("Y : " + y);
int startOffset = tarea.viewToModel(new Point(x, y));
System.out.println("Start Offset : " + startOffset);
String text = tarea.getText();
int searchLocation = text.indexOf("student://", startOffset);
System.out.println("Search Location : " + searchLocation);
if (searchLocation == startOffset)
JOptionPane.showMessageDialog(TextAreaExample.this, "BINGO you found me.");
}
});
getContentPane().add(scroll, BorderLayout.CENTER);
getContentPane().add(tfield, BorderLayout.PAGE_END);
pack();
setLocationByPlatform(true);
setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new TextAreaExample().createAndDisplayGUI();
}
});
}
}
No, don't even consider this since ActionListeners are for JButtons or anything else derived from AbstractButton but not for JTextComponents (except for JTextFields). Perhaps you want a MouseListener.
Having said this, perhaps you'll be better off with two text components, a JTextArea to display all responses, including the user's, and right below this in a BorderLayout.SOUTH type of position, a JTextField to allow the user to enter text into the chat. Then give that JTextField an ActionListener (this is legal) so that "enter" will actuate the listener.
Edit 1
You state:
Well I have that jtextfield , the text in it is being sent to the server and server sends the message to all clients which appears in the JTextArea. But my problem is here: I want to popup a window when someone clicks on a student://id text .
Yeah, looking at your comments, my vote is for you to display the chats not in a JTextArea but rather in a JList, one with a SelectionListener. You can then respond easily to mouse click events, and will more easily get useful information from the "line" clicked on (if you fill the JList with smart objects). You will need to write a custom cell renderer that allows multiple lines of text to be displayed, probably one that shows a JTextArea, but the tutorial on JLists will get you started on this.
Is hitting ENTER instead of mouse-click ok?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class StudentID extends JFrame implements ActionListener
{
private static final String progname = "StudentID 0.1";
private JTextField student;
private JTextArea feedback;
private JButton exit;
public StudentID ()
{
super (progname);
JPanel mainpanel = new JPanel ();
mainpanel.setLayout (new BorderLayout ());
this.getContentPane ().add (mainpanel);
student = new JTextField ("student://");
exit = new JButton ("exit");
student.addActionListener (this);
exit.addActionListener (this);
feedback = new JTextArea ();
mainpanel.add (student, BorderLayout.NORTH);
mainpanel.add (feedback, BorderLayout.CENTER);
mainpanel.add (exit, BorderLayout.SOUTH);
setSize (400, 400);
setLocation (100, 100);
setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
setVisible (true);
}
public void actionPerformed (final ActionEvent e)
{
SwingWorker worker = new SwingWorker ()
{
protected String doInBackground () throws InterruptedException
{
String cmd = e.getActionCommand ();
if (cmd.equals ("exit"))
{
System.exit (0);
}
else if (cmd.matches ("student://[0-9]+"))
{
feedback.setText ("student found: " + cmd.replaceAll ("student://([0-9]+)", "$1"));
}
else
{
feedback.setText ("cmd: " + cmd);
}
return "done";
}
protected void done ()
{
feedback.setText (feedback.getText () + "\ndone");
}
};
worker.execute ();
}
public static void main (final String args[])
{
Runnable runner = new Runnable ()
{
public void run ()
{
new StudentID ();
}
};
EventQueue.invokeLater (runner);
}
}

Why JComboBox ignore PrototypeDisplayValue

In connections with these two post #iMohammad,
Increasing/Decreasing Font Size inside textArea using JButton and
Changing Font Style when Clicking on a JButton Java ..., I'm facing with really funny issue that came from JComboBoxby passing setPrototypeDisplayValue as an argument for JComboBox's size on the screen
please how can I resize JComboBox dynamically depends of Font, same as works correctly for another JComponents that I tried in my sscce
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ComboBoxFontChange extends JFrame {
private static final long serialVersionUID = 1L;
private JComboBox cbox = new JComboBox();
private JTextField tfield = new JTextField("Change");
private JLabel label = new JLabel("Cash");
private JButton button = new JButton("++ Font");
private JTextField text;
private JPanel panel = new JPanel();
public ComboBoxFontChange() {
super("Combo Box Font change");
text = (JTextField) cbox.getEditor().getEditorComponent();
cbox.addItem("Change");
cbox.addItem("Cash");
cbox.addItem("Font");
tfield.setColumns(5);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Font font = cbox.getFont();
font = font.deriveFont((float) (font.getSize2D() * 1.10));
cbox.setFont(font);
// EDIT
cbox.setPrototypeDisplayValue(cbox.getSelectedItem().toString());
tfield.setFont(font);
button.setFont(font);
label.setFont(font);
//panel.revalidate();
//panel.repaint();
pack();
}
});
//panel.setLayout(new GridLayout(2, 2, 10, 10));
panel.add(cbox);
panel.add(label);
panel.add(tfield);
panel.add(button);
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(panel);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ComboBoxFontChange frame = new ComboBoxFontChange();
frame.pack();
frame.setVisible(true);
}
});
}
}
I debugged your SSCCE, and the value passed to setPrototypeDisplayValue is the empty string. Changing the line to
cbox.setPrototypeDisplayValue(cbox.getSelectedItem());
Makes everything work as expected. Removing the call to setPrototypDisplayValue also makes the program behave as expected.
EDIT:
The other problem is that no event is fired for the prototype display value because you set it to the previous value as before, and an event is only fired if the value actually changes. Adding cbox.setPrototypeDisplayValue(""); before cbox.setPrototypeDisplayValue(cbox.getSelectedItem().toString()) makes everything behave as expected, even on JDK 1.6. I agree that given that the font is changed, the preferred size should be recomputed, but at least this change is a workaround.
I tried what JB Nizet said. But for me the comboBox size didnt change. How about you?
So i tried the following and the combobox size increased as i increased the font size.
cbox.setFont(font);
cbox.updateUI();
I also removed the line
cbox.setPrototypeDisplayValue(text.getText());
For reference, a GridLayout and eight clicks gave this result on Mac OS X:
panel.setLayout(new GridLayout(0, 1, 10, 10));
Combo: Popup:
As an aside, cbox.updateUI() restored the defaults prescribed by the Aqua UI delegate, com.apple.laf.AquaComboBoxUI.
Here is the code from the BasicComboBoxUI:
else if ( propertyName == "font" ) {
listBox.setFont( comboBox.getFont() );
if ( editor != null ) {
editor.setFont( comboBox.getFont() );
}
isMinimumSizeDirty = true;
comboBox.validate();
}
else if ( propertyName == JComponent.TOOL_TIP_TEXT_KEY ) {
updateToolTipTextForChildren();
}
else if ( propertyName == BasicComboBoxUI.IS_TABLE_CELL_EDITOR ) {
Boolean inTable = (Boolean)e.getNewValue();
isTableCellEditor = inTable.equals(Boolean.TRUE) ? true : false;
}
else if (propertyName == "prototypeDisplayValue") {
isMinimumSizeDirty = true;
isDisplaySizeDirty = true;
comboBox.revalidate();
}
For some reason a Font change only resets the "minimum size" not the "display size".

Categories

Resources