Dynamically Update Tooltip Currently Displayed - java

I'm trying to got a tooltip which displays the current progress of a task. So I want that the tooltip text change while the tooltip is displayed. But, when I call setToolTipText() the displayed text remains the same until I exit the mouse from the tooltip component and enter again. And call setToolTipText(null) before doesn't change anything.

Indeed it does not update itself, even when resetting the tooltip to null between calls.
So far, the only trick I found was to simulate a mouse-move event and forward it on the TooltipManager. It makes him think that the mouse has moved and that the tooltip must be relocated. Not pretty, but quite efficient.
Have a look at this demo code which displays a progress in % from 0 to 100:
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.ToolTipManager;
public class TestTooltips {
protected static void initUI() {
JFrame frame = new JFrame("test");
final JLabel label = new JLabel("Label text");
frame.add(label);
frame.pack();
frame.setVisible(true);
Timer t = new Timer(1000, new ActionListener() {
int progress = 0;
#Override
public void actionPerformed(ActionEvent e) {
if (progress > 100) {
progress = 0;
}
label.setToolTipText("Progress: " + progress + " %");
Point locationOnScreen = MouseInfo.getPointerInfo().getLocation();
Point locationOnComponent = new Point(locationOnScreen);
SwingUtilities.convertPointFromScreen(locationOnComponent, label);
if (label.contains(locationOnComponent)) {
ToolTipManager.sharedInstance().mouseMoved(
new MouseEvent(label, -1, System.currentTimeMillis(), 0, locationOnComponent.x, locationOnComponent.y,
locationOnScreen.x, locationOnScreen.y, 0, false, 0));
}
progress++;
}
});
t.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
initUI();
}
});
}
}

Here's a simplified version of Guillaume Polet's answer which is self-contained in a single method. This code assumes one has called component.setToolTip("..."); previously. This code does not show how to periodically update the tooltip to show the progress.
public static void showToolTip(JComponent component)
{
ToolTipManager manager;
MouseEvent event;
Point point;
String message;
JComponent component;
long time;
manager = ToolTipManager.sharedInstance();
time = System.currentTimeMillis() - manager.getInitialDelay() + 1; // So that the tooltip will trigger immediately
point = component.getLocationOnScreen();
event = new MouseEvent(component, -1, time, 0, 0, 0, point.x, point.y, 1, false, 0);
ToolTipManager.
sharedInstance().
mouseMoved(event);
}

Related

Draw String in runnable not working

I want to draw a string in a runnable, then a few seconds later, add another word to that string, and repeat. The runnable works but it won't drawString?
I've made a mini-application with only what I want if anyone would need to compile and try it.
Here it is:
package timer.problem.example;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
public class TimerProblemExample extends JFrame {
private static final long serialVersionUID = 1L;
public TimerProblemExample() {
setBackground(Color.black);
setVisible(true);
setTitle("Timer Problem Example");
setSize(950, 600);
setDefaultCloseOperation(3);
}
public static void main(String[] args) {
new TimerProblemExample();
}
public void timer(final Graphics graphics) {
new Timer().schedule(new TimerTask() {
int update = 6;
public void run() {
update--;
graphics.setFont(new Font("Splash", Font.PLAIN, 60));
graphics.setColor(Color.white);
if (update == 5) {
graphics.drawString("Test", 50, 130);
repaint();
}
if (update == 3) {
graphics.drawString("Test Test", 50, 100);
repaint();
}
if (update == 1) {
graphics.drawString("Test Test Test", 50, 50);
repaint();
}
}
}, 0L, 1000L);
}
#Override
public void paint(Graphics graphics) {
super.paint(graphics);
timer(graphics);
}
}
Please help with this, thanks.
There are a couple of issues with your sample code.
Every time you call repaint, a new timer is created. You have so many running (and so often) that each one keeps repainting over the last faster than the UI can update. You can test this by adding System.out.println("Timer thread: "+ Thread.currentThread().getId()); right before update--;. Addidtionally, any changes to the frame (changing it's size by dragging with the mouse, for example) also call paint(), which leads to more timers being spawned.
You really shouldn't call setVisible(true); until you've set the properties of the JFrame(size, title etc.). It's not a problem right now, but if you intend to add components to it, it'll be an issue.
I've amended your code and this works as per your need.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
public class TimerProblemExample extends JFrame {
private static final long serialVersionUID = 1L;
public TimerProblemExample() {
setBackground(Color.black);
setTitle("Timer Problem Example");
setSize(950, 600);
setDefaultCloseOperation(3);
setVisible(true);
timer(getGraphics()); //call the timer from here, so there's only one running
}
public static void main(String[] args) {
new TimerProblemExample();
}
public void timer(final Graphics graphics) {
new Timer().schedule(new TimerTask() {
int update = 6;
public void run() {
update--;
System.out.println("Timer thread: "+ Thread.currentThread().getId());
graphics.setFont(new Font("Splash", Font.PLAIN, 60));
graphics.setColor(Color.white);
if (update == 5) {
graphics.drawString("Test", 50, 130);
}
if (update == 3) {
graphics.drawString("Test Test", 50, 100);
}
if (update == 1) {
graphics.drawString("Test Test Test", 50, 50);
}
//Added this section to keep the update variable looping back
if(update<=0)
{
update = 6;
}
}
}, 1000L, 1000L); //0 Delay for start of timer was spawning lot's of threads in the first second. Slowed it down. Delay isn't needed anymore, but it's useful to have it to see the drawString methods being executed.
}
}
Lastly, it's a good idea not to draw anything directly on the Frame. Add a JLabel to the frame and put the text in that.

problems with swing timer

I'm creating an speed reader so I'm using a swingx.Timer object to display the words at a given time.I set the time to 1sec for testing. I had the code working before but then after closing eclipse and coming back to it the code doesn't work anymore. The timer does not call the actionlistener thus not performing the action it is supposed to perform. Here is my code. I read other questions talking about timer being a daemon thread and ending with main. However I don't think this is the case in my code but I could be wrong. Can someone help me point out my error?
package reader;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Font;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JTextPane;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JMenu;
import java.awt.event.*;
import java.io.IOException;
import java.sql.Time;
import javax.swing.Timer;
public class ReaderPad extends JPanel{
public JButton startBtn, stopBtn, resetBtn;
public JList speedMenu;
public JMenuBar topMenu;
public JTextPane textPad;
public JScrollPane scroll;
public Timer timer;//to control the speed of the reader
public int speed; //the speed set for the reader, i.e. 100 words per minute
public JLabel speedLabel;
public JTextField speedText;
int wordLength; //size of text
String[] lines; //a line of text composed of 10 words from the text entered
int wpm;
int i;//index of word set being used
public ReaderPad(){
this.setLayout(new BorderLayout());
i = 0;
speedLabel = new JLabel("words per minute");
speedText = new JTextField();
speedText.setColumns(3);
startBtn = new JButton("Start");
stopBtn = new JButton("Stop");
resetBtn = new JButton("Reset");
textPad = new JTextPane();
topMenu = new JMenuBar();
textPad.setEditable(true);
textPad.setFont(new Font("Serif", Font.PLAIN,25));
textPad.setText("welcome to speed reader. Use CTRL + c to paste your text\n"
+ "Press start to begin");
scroll = new JScrollPane(textPad);
//create top menu bar
topMenu.add(startBtn);
topMenu.add(stopBtn);
topMenu.add(resetBtn);
topMenu.add(speedLabel);
topMenu.add(speedText);
//topMenu.add(speedMenu);
this.add(topMenu, BorderLayout.NORTH);
this.add(scroll, BorderLayout.CENTER);
//set listeners for buttons
Actions listener = new Actions();
startBtn.addActionListener(listener);
stopBtn.addActionListener(listener);
resetBtn.addActionListener(listener);
}//end of constructor
public void setFontSize(String size){
Font font = textPad.getFont();
String s = font.getName();
textPad.setFont(new Font(s, Font.PLAIN, Integer.valueOf(size)));
}
public void setFontColor(String color){
}
//sets background color
public void setBackgroud(String color){
}
public void setSpeed(String speed){
//speed = speed.replace("X", "");//remove the times symbol
if(speed.isEmpty())//set default
this.speed = 100;
else
this.speed = Integer.valueOf(speed);
}
public int getSpeed(){
return this.speed;
}
public void startReader(){//words per minute
int speed = getSpeed();//wpm selected by the user
System.out.println("speed: " + speed);
String text = textPad.getText();
// System.out.println(text);
lines = text.split(" ");
wordLength = lines.length;//get the number of words in the text
System.out.println("length: "+ wordLength);
//System.out.println(wordLength.length);
if(text.isEmpty()){
textPad.setText("You didn't enter any text in the text area.\n"
+"Please enter some text to begin.");
}
else{//set timer
//calculate speed first: time = (speed chosen * number of workds in text) = (sec/words)*wordsize
//wpm = (1/speed)*wordLength*60*1000; //multiply by 60 secons
wpm = 1000;
System.out.println("wpm: "+ wpm);
TimerReader counter = new TimerReader();
timer = new Timer(1000, counter);
timer.start();
System.out.println(timer.isRunning());
}
}
public void stopReader(){
}
//listener class
public class Actions implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
String source = e.getActionCommand();//read the button asking for the request
if(source.equals("Reset")){
textPad.setText("");
if(timer.isRunning())
timer.stop();
}
else if(source.equals("Start")){
//first read the speed selected and set the speed
setSpeed(speedText.getText());
startReader();
}
else if (source.equals("Stop"))
i = 0;
timer.stop();
}
}
public class TimerReader implements ActionListener{
#Override
public void actionPerformed(ActionEvent e){
//once the timer is called the textpad starts displaying the words
String s = lines[i];
i++;
textPad.setText(s);
System.out.println(i);
System.out.println(e.getActionCommand());
System.out.print(textPad.getText());
}
}//end of inner class
}//end of ReaderPad class
Remove timer.stop() from the end of Actions actionPerformed method...
public class Actions implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
String source = e.getActionCommand();//read the button asking for the request
if (source.equals("Reset")) {
textPad.setText("");
if (timer.isRunning()) {
timer.stop();
}
} else if (source.equals("Start")) {
//first read the speed selected and set the speed
setSpeed(speedText.getText());
startReader();
} else if (source.equals("Stop")) {
i = 0;
}
// This is stopping the timer AFTER it was already started
//timer.stop();
}
}

Why does a fireContentsChanged call of a JList freezes the entire GUI?

I had some problems with freezing SWING GUIs when re-rendering a JTable with a custom cell renderer in Java. So I asked the question "Why does a JTable view update block the entire GUI?". The answers pointed to the fact, that a JList without modifying JTable and overwriting doLayout might be a better choice. So I implemented the example with a JList and ran into the same problem: while generating data, everything works fine and the progress bar moves. But when the view is updated, the program freezes and the progress bar stops moving.
Please note, that the sleep statement is there only to let the generation take a longer, more realistic time (reading thousands of data sets via JDBC and create objects out of them takes a lot time). One could remove it and increment the number of generated items. But you can clearly see, that the HTML rendering is quite slow. But I need this colors and the two lines (if not necessarily so many different colors).
So could you please tell me, where my mistake is? I think, that EDT and other work is separated through separate threads and I cannot see any mistke.
Update: I looked around at SO and found this question "https://stackoverflow.com/a/20813122/2429611". There is said:
The more interesting question would be how to avoid that UI blocking, but I don't think that's possible with just Swing, you'll have to implement some lazy loading, or rendering in batches.
This would mean, that I cannot solve my problem. Is this correct?
package example;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.AbstractListModel;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
public class ListExample extends AbstractListModel {
static List<DemoObject> internalList = new ArrayList<>();
#Override
public int getSize() {
return internalList.size();
}
#Override
public DemoObject getElementAt(int index) {
return internalList.get(index);
}
public void fireContentsChanged() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
fireContentsChanged(this, 0, -1);
}
});
}
static class MyCellRenderer extends JLabel implements ListCellRenderer<ListExample.DemoObject> {
public MyCellRenderer() {
setOpaque(true);
}
#Override
public Component getListCellRendererComponent(JList<? extends ListExample.DemoObject> list,
ListExample.DemoObject value,
int index,
boolean isSelected,
boolean cellHasFocus) {
setText("<html>" + value.toString()
+ "<br/>"
+ "<span bgcolor=\"#ff0000\">Line 2; Color = " + value.c + "</span>");
Color background;
Color foreground;
// check if this cell represents the current DnD drop location
JList.DropLocation dropLocation = list.getDropLocation();
if (dropLocation != null
&& !dropLocation.isInsert()
&& dropLocation.getIndex() == index) {
background = Color.BLUE;
foreground = Color.WHITE;
// check if this cell is selected
} else if (isSelected) {
background = Color.RED;
foreground = Color.WHITE;
// unselected, and not the DnD drop location
} else {
background = value.c; //Color.WHITE;
foreground = Color.BLACK;
};
setBackground(background);
setForeground(foreground);
return this;
}
}
static class DemoObject {
String str;
Color c;
public DemoObject(String str, int color) {
this.str = str;
this.c = new Color(color);
}
#Override
public String toString() {
return str;
}
}
static JPanel overlay;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Example");
frame.setLayout(new BorderLayout(4, 4));
// Add JTable
final ListExample model = new ListExample();
JList list = new JList(model);
list.setCellRenderer(new MyCellRenderer());
frame.add(new JScrollPane(list), BorderLayout.CENTER);
// Add button
Box hBox = Box.createHorizontalBox();
hBox.add(new JButton(new AbstractAction("Load data") {
#Override
public void actionPerformed(ActionEvent e) {
new Thread(new Runnable() {
#Override
public void run() {
overlay.setVisible(true);
internalList.clear();
System.out.println("Generating data ...");
SecureRandom sr = new SecureRandom();
for (int i = 0; i < 10000; i++) {
internalList.add(
new DemoObject(
"String: " + i + " (" + sr.nextFloat() + ")",
sr.nextInt(0xffffff)
)
);
// To create the illusion, that data are
// fetched via JDBC (which takes a little
// while), this sleep statement is embedded
// here. In a real world scenario, this wait
// time is caused by talking to the database
// via network
if (i%10 == 0) {
try {
Thread.sleep(1);
} catch (Exception e) {
}
}
}
System.out.println("Updating view ...");
model.fireContentsChanged();
overlay.setVisible(false);
System.out.println("Finished.");
}
}).start();
}
}));
hBox.add(Box.createHorizontalGlue());
frame.add(hBox, BorderLayout.NORTH);
// Create loading overlay
overlay = new JPanel(new FlowLayout(FlowLayout.CENTER)) {
#Override
protected void paintComponent(Graphics g) {
g.setColor(new Color(0, 0, 0, 125));
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
};
overlay.setOpaque(false);
overlay.setBackground(new Color(0, 0, 0, 125));
JProgressBar bar = new JProgressBar();
bar.setIndeterminate(true);
overlay.add(bar);
frame.setGlassPane(overlay);
frame.getGlassPane().setVisible(false);
// Create frame
frame.setSize(600, 400);
frame.setVisible(true);
}
});
}
}
there are three problems (recreating, reseting the model, and custom Renderer stoped to works)
JList (JComboBox hasn't) has an issue by removing more than 999 items, you have to set a new model to JList
see important for ComboBoxModel extends AbstractListModel implements MutableComboBoxModel for setElementAt(to hold current selection)
usage of public void fireContentsChanged() { is wrong, don't see reason to use this way, again is about to replace current, reset the model
. e.g. with success atr runtime and by recrusive testing for/if event (fired)
setModel(new DefaultListModel(list.toArray()) {
protected void fireContentsChanged(Object obj, int i, int j) {
if (!isFired)
super.fireContentsChanged(obj, i, j);
}
});

Showing JDialog as sheet not working

I am currently using this code to create a JDialog;
package com.kamuara.reposync.window;
import java.awt.Dialog;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;
public class SheetDialog {
private JFrame _windowFrame;
public static void main(String[] args) {
System.setProperty("apple.awt.documentModalSheet", "true");
System.setProperty("apple.awt.brushMetalLook", "true");
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new SheetDialog();
} catch (Exception e) {
e.printStackTrace();
}
}
public SheetDialog() {
_windowFrame = new JFrame();
_windowFrame.setResizable(false);
_windowFrame.setBounds(100, 100, 451, 320);
_windowFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
_windowFrame.getContentPane().setLayout(null);
_windowFrame.setVisible(true);
JButton showDialogButton = new JButton("Show Dialog");
showDialogButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
showSheetDialog(_windowFrame, "Test", "This should be a sheet dialog", "Oke");
}
});
showDialogButton.setBounds(328, 263, 117, 29);
_windowFrame.getContentPane().add(showDialogButton);
}
public void showSheetDialog(JFrame owner, String title, String message, String button) {
final JDialog messageDialog = new JDialog(owner, title, Dialog.ModalityType.DOCUMENT_MODAL);
messageDialog.setBounds(30, 0, owner.getWidth() - 60, 130);
// TODO: only when os is osx
messageDialog.getRootPane().putClientProperty("apple.awt.documentModalSheet", "true");
messageDialog.setLayout(null);
int offsetX = 25;
JLabel titleLabel = new JLabel(title);
titleLabel.setFont(new Font("Lucida Grande", Font.BOLD, 13));
titleLabel.setBounds(offsetX, 10, 100, 25);
messageDialog.getContentPane().add(titleLabel);
JLabel messageLabel = new JLabel(message);
messageLabel.setVerticalTextPosition(JLabel.TOP);
messageLabel.setHorizontalTextPosition(JLabel.LEFT);
messageLabel.setFont(new Font("Lucida Grande", Font.PLAIN, 11));
messageLabel.setBounds(offsetX, 10, messageDialog.getWidth() - 10, messageDialog.getHeight() - 60);
messageDialog.getContentPane().add(messageLabel);
JButton okButton = new JButton(button);
okButton.setBounds(messageDialog.getWidth() - 105, messageDialog.getHeight() - 35, 100, 25);
okButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
messageDialog.dispose();
}
});
messageDialog.getContentPane().add(okButton);
messageDialog.setVisible(true);
}
}
I was previously using Java 6 to compile the application and setting the clientProperty apple.awt.documentModalSheet was working perfectly to display the dialog as a "Sheet" on OSX but now I started using Java 7 (update 25) and the dialog is no longer displayed as a Sheet. I can't seem to find any update documentation on this. Have they changed anything about this? How can I solve this? The current interface design looks tons better with a Sheet than a dialog.
Update
I found the following Bug report which seems to be the same issue as I am experiencing;
http://bugs.sun.com/view_bug.do?bug_id=8010197
Does anyone know how to resolve this? I have looked into libraries like QuaQua but I would prefer not using any library because I just want the Sheet functionality.
Update 2
I tried QuaQua, but the library currently has the exact same problem when compiling with Java 7. Any workarounds?
Update 3
Replaced code with working sample (http://pastebin.com/PJ8VGdPb)
Update 4
Found out SWT has a style for their Shell class named SWT.SHEET which still works in Java7, I don't prefer using a library like SWT, but it seems to be the only solution.
As far as I know, Apple didn't officially released their version of JDK 7. The latest version of the JDK Apple has optimized for their OS X is still JDK 6. That is also why updates for Java come thru the AppStore update tab. These updates do not come straight from Oracle.
If you downloaded JDK 7 directly from Oracle, this is a more generic, non-tweaked version.
So, I think you will just have to wait for Apple to release their OS X optimized JDK 7.
I experienced a lot of OS X features not to be working when I download from Oracle:
Trackpad gestures
Native Aqua Look'n'Feel doesn't work, even when trying to set it manually through UIManager.
Application icon not working when using JOptionPane.
JMenu's will stick into the JFrame itself instead of moving to the top of the screen.
It seems before JDK fix the bug, you have to implement the Sheet yourself.
The key points are:
Use the glass pane of the window frame to hold the Sheet dialog
Use the GridBagLayout (with NORTH anchor) to put the dialog at top|center of the pane
Animate the Sheet when show/disappeare by painting the dialog repeatedly, every time paint more/less part of the dialog
The following is the example code
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.LineBorder;
public class SheetableJFrame extends JFrame implements ActionListener {
public static final int INCOMING = 1;
public static final int OUTGOING = -1;
public static final float ANIMATION_DURATION = 1000f;
public static final int ANIMATION_SLEEP = 50;
JComponent sheet;
JPanel glass;
Sheet animatingSheet;
boolean animating;
int animationDirection;
Timer animationTimer;
long animationStart;
BufferedImage offscreenImage;
public SheetableJFrame() {
super();
glass = (JPanel) getGlassPane();
glass.setLayout(new GridBagLayout());
animatingSheet = new Sheet();
animatingSheet.setBorder(new LineBorder(Color.black, 1));
}
public JComponent showJDialogAsSheet(JDialog dialog) {
sheet = (JComponent) dialog.getContentPane();
sheet.setBorder(new LineBorder(Color.black, 1));
glass.removeAll();
animationDirection = INCOMING;
startAnimation();
return sheet;
}
public void hideSheet() {
animationDirection = OUTGOING;
startAnimation();
}
private void startAnimation() {
glass.repaint();
// clear glasspane and set up animatingSheet
animatingSheet.setSource(sheet);
glass.removeAll();
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTH;
glass.add(animatingSheet, gbc);
gbc.gridy = 1;
gbc.weighty = Integer.MAX_VALUE;
glass.add(Box.createGlue(), gbc);
glass.setVisible(true);
// start animation timer
animationStart = System.currentTimeMillis();
if (animationTimer == null) animationTimer = new Timer(ANIMATION_SLEEP, this);
animating = true;
animationTimer.start();
}
private void stopAnimation() {
animationTimer.stop();
animating = false;
}
// used by the Timer
public void actionPerformed(ActionEvent e) {
if (animating) {
// calculate height to show
float animationPercent = (System.currentTimeMillis() - animationStart) / ANIMATION_DURATION;
animationPercent = Math.min(1.0f, animationPercent);
int animatingHeight = 0;
if (animationDirection == INCOMING) {
animatingHeight = (int) (animationPercent * sheet.getHeight());
} else {
animatingHeight = (int) ((1.0f - animationPercent) * sheet.getHeight());
}
// clip off that much from sheet and put it into animatingSheet
animatingSheet.setAnimatingHeight(animatingHeight);
animatingSheet.repaint();
if (animationPercent >= 1.0f) {
stopAnimation();
if (animationDirection == INCOMING) {
finishShowingSheet();
} else {
glass.removeAll();
glass.setVisible(false);
glass.setLayout(new GridBagLayout());
animatingSheet = new Sheet();
}
}
}
}
private void finishShowingSheet() {
glass.removeAll();
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTH;
glass.add(sheet, gbc);
gbc.gridy = 1;
gbc.weighty = Integer.MAX_VALUE;
glass.add(Box.createGlue(), gbc);
glass.revalidate();
glass.repaint();
}
class Sheet extends JPanel {
Dimension animatingSize = new Dimension(0, 1);
JComponent source;
BufferedImage offscreenImage;
public Sheet() {
super();
setOpaque(true);
}
public void setSource(JComponent source) {
this.source = source;
animatingSize.width = source.getWidth();
makeOffscreenImage(source);
}
public void setAnimatingHeight(int height) {
animatingSize.height = height;
setSize(animatingSize);
}
private void makeOffscreenImage(JComponent source) {
GraphicsConfiguration gfxConfig = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
offscreenImage = gfxConfig.createCompatibleImage(source.getWidth(), source.getHeight());
Graphics2D offscreenGraphics = (Graphics2D) offscreenImage.getGraphics();
source.paint(offscreenGraphics);
}
public Dimension getPreferredSize() {
return animatingSize;
}
public Dimension getMinimumSize() {
return animatingSize;
}
public Dimension getMaximumSize() {
return animatingSize;
}
public void paint(Graphics g) {
// get the bottom-most n pixels of source and paint them into g, where n is height
BufferedImage fragment = offscreenImage.getSubimage(0, offscreenImage.getHeight() - animatingSize.height,
source.getWidth(), animatingSize.height);
g.drawImage(fragment, 0, 0, this);
}
}
}
The test code
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
public class SheetTest extends Object implements PropertyChangeListener {
JOptionPane optionPane;
SheetableJFrame frame;
public static void main(String[] args) {
new SheetTest();
}
public SheetTest() {
frame = new SheetableJFrame();
// build JOptionPane dialog and hold onto it
optionPane = new JOptionPane("Do you want to close?", JOptionPane.QUESTION_MESSAGE, JOptionPane.CANCEL_OPTION);
frame.setSize(640, 480);
frame.setVisible(true);
optionPane.addPropertyChangeListener(this);
JDialog dialog = optionPane.createDialog(frame, "irrelevant");
frame.showJDialogAsSheet(dialog);
}
public void propertyChange(PropertyChangeEvent pce) {
if (pce.getPropertyName().equals(JOptionPane.VALUE_PROPERTY)) {
System.out.println("Selected option " + pce.getNewValue());
frame.hideSheet();
}
}
}
reference
http://oreilly.com/pub/h/4852
http://book.javanb.com/swing-hacks/swinghacks-chp-6-sect-6.html
Here's a super dodgy hack I came up with which sets the flag which the JDK now appears to be forgetting to set and manually positions the window in the right place. There's still a missing shadow though, so I wonder if anyone can improve on it. ;)
This messes with internal classes and private fields, so it might break in any given new release of the JDK, but it still works on 8u5. Maybe it will give some insight into how these internal AWT classes are structured.
public static void makeSheet(Dialog dialog) {
dialog.addNotify();
ComponentPeer peer = dialog.getPeer();
// File dialogs are CFileDialog instead. Unfortunately this means this hack
// can't work for those. :(
if (peer instanceof LWWindowPeer) {
LWWindowPeer windowPeer = (LWWindowPeer) dialog.getPeer();
//XXX: Should check this before casting too.
CPlatformWindow platformWindow = (CPlatformWindow) windowPeer.getPlatformWindow();
try {
Method method = CPlatformWindow.class.getDeclaredMethod(
"setStyleBits", int.class, boolean.class);
method.setAccessible(true);
method.invoke(platformWindow, 64 /* CPlatformWindow.SHEET */, true);
Window parent = dialog.getOwner();
dialog.setLocation(dialog.getLocation().x,
parent.getLocation().y + parent.getInsets().top);
} catch (Exception e) {
Logger.getLogger(SheetHack.class.getName())
.log(Level.WARNING, "Couldn't call setStyleBits", e);
}
}
}

Delayed text color change in Swing text field

Is it possible to change the color of a text in a text field?I am trying to build an interpreter, so I was wondering on how would you change the color of the text in real time.
For example the word I enter in the text field is:
printf("hi");
The word printf becomes green after a few seconds.
Is it possible?
package test;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class BlinkColorTextField {
BlinkColorTextField() {
final JTextField blinkingText = new JTextField("Red & Blue");
ActionListener blinker = new ActionListener() {
boolean isRed = true;
public void actionPerformed(ActionEvent ae) {
if (isRed) {
blinkingText.setForeground(Color.BLUE);
} else {
blinkingText.setForeground(Color.RED);
}
isRed = !isRed;
}
};
Timer timer = new Timer(1000, blinker);
timer.start();
JOptionPane.showMessageDialog(null, blinkingText);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
new BlinkColorTextField();
}
});
}
}
Try this:
HighlightPainter greenPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.GREEN);
//in a thread...
Highlighter h = tf.getHighlighter();
h.addHighlight(offset, offset+length, greenPainter);
You have to use JEditorPane / JTextPane instead of JTextField and also you can draw the text/string by overriding the paintComponent method.

Categories