Saturday, June 5, 2010

Animated Scrolling text

Some days ago, a friend(hey Luiz!) asked me about a component that could display text with a kind of scrolling animation as you see in many media players where you see the band name / title name going from right to left. He wanted to add this kind of component to one of his application. As he knows I like to play with swing/java2D he asked me about it.
I like the idea of creating such component; so I had to come up with an implementation, even If I guess you can find some basic implementations on the net.

I wanted to add a way to control the animation to be able to have some basic animations (Left to right / Right to left / Top to bottom / Bottom to top) and also some more complex animation like sinusoidal animation.


I rapidly had a first draft working, doing the wished animation, but it took a few refractor sessions to have some clean code that gives full control over the animation.


Let’s have a look at the code.

First the interface that will be used to control the animation:


public static interface IScrollTextUtils {
    int getYpos(float animProgress, int charnum);

    int getXpos(float animProgress, int charnum);

    double getRotate(float animProgress, final int charnum);

    float getTransformedFontSize(float animProgress, int fontSize, final int charnum);

    boolean isTimeTolaunchNewTimeline(float animProgress);

    void textChanged();

    void paintCharacter(Graphics2D g, String s);

    int getDuration();
}


It will be used to find out the position of each character, paint the text, the duration of the animation and to determine when to start the next animation.

More than one of the same animation can run at the same time (you want to start the next animation before the end of the current one to always have some text to display).

So to deal with multiple animations at the same time we need a list of something that can handle an animation.
AnimationProgressHandler is used for this purpose.
It contains a timeline and a way to start the next animation when required




public static class AnimationProgressHandler {
        private Timeline timeline;
        private float animProgress = 0;
        private boolean AlreadyStartedNext = false;
        private ScrollingText scrolling;

        private AnimationProgressHandler(final ScrollingText scrolling) {
            this.scrolling = scrolling;
            //prepare the timeline
            this.timeline = new Timeline(this);
            this.timeline.addPropertyToInterpolate("animProgress", 0f, 1f);
            this.timeline.setDuration(scrolling.scrollTextUtils.getDuration());
            this.timeline.addCallback(createCallBack(this.timeline));
        }

        public void setAnimProgress(final float animProgress) {
            this.animProgress = animProgress;
            if (!this.AlreadyStartedNext && this.scrolling.scrollTextUtils.isTimeTolaunchNewTimeline(animProgress)) {
                this.AlreadyStartedNext = true;
                //start the next animation
                this.scrolling.createTimeline().play();
            }
            this.scrolling.repaint();
        }

        private TimelineCallback createCallBack(final Timeline parent) {
            return new TimelineCallbackAdapter() {
                @Override
                public void onTimelineStateChanged(final TimelineState oldState, final TimelineState newState,
                        final float durationFraction, final float timelinePosition) {
                    if (newState == TimelineState.DONE) {
                        //when the animation is done 
                        //remove it
                        AnimationProgressHandler.this.scrolling.animHandlers.remove(this);
                    }
                }
            };
        }
    }


the interesting methods in scrolling text are the paint methods


paintComponent paint the text for every active timeline

    protected void paintComponent(final Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // paint the text for each running animation
        for (int i = 0; i < this.animHandlers.size(); i++) {
            paintText(g2, this.animHandlers.get(i).animProgress);
        }
        g2.dispose();
    }


paintText call scrollTextUtils to find the x and y coordinates
check if the coordinates are in the visible area
get and set the rotation/font and then paint each character of the text



protected void paintText(final Graphics2D g2, final float animProgress) {
        Font f = getFont();
        for (int i = 0; i < this.text.length(); i++) {
            int x = this.scrollTextUtils.getXpos(animProgress, i);
            if (x < -f.getSize() || x > getWidth()) {
                // do not paint, out of the visible bounds
            } else {
                int y = this.scrollTextUtils.getYpos(animProgress, i);
                if (y < 0 || y > getHeight() + f.getSize()) {
                    // do not paint, out of the visible bounds
                } else {
                    double angle = this.scrollTextUtils.getRotate(animProgress, i);
                    g2.rotate(angle, x, y);
                    g2.setFont(f.deriveFont(this.scrollTextUtils.getTransformedFontSize(animProgress, f.getSize(), i)));
                    g2.translate(x, y);
                    this.scrollTextUtils.paintCharacter(g2, String.valueOf(this.text.charAt(i)));
                    g2.translate(-x, -y);
                    g2.rotate(-angle, x, y);
                }
            }
        }
        g2.setFont(f);
    }

you can try it here







the full source code is available at https://free-the-pixel.dev.java.net/source/browse/free-the-pixel/trunk/src/com/community/xanadu/components/text/ScrollingText.java

Friday, April 2, 2010

GHOST drag and drop, over multiple windows

I guess many people seen the example(the photocollage application) from Romain Guy, when he was still playing with java, about ghost drag and drop using the glass pane to render the ghost image:
  http://www.jroller.com/gfx/entry/drag_and_drop_effects_the


One problem with this implementation is that it used the glasspane, a unique resource of a window.
If that’s a problem for you, you can simply fix it by using JLayeredPane or JXlayer (I won’t talk about this implementation in this article).

The biggest problem is (and even more lately considering that palettes/multi windows application are vastly used now), how to allow ghost DnD through multiple windows?
You can easily imagine, in the photocollage application of Romain, that the photo thumbnails would be inside a different window than the drop location.
To do that, it’s not possible to use any kind of trick that span over only a window to draw the ghost image.

The solution I came up with is to use a non opaque window on which the ghost image is drawn and that is dragged around

While implementing this solution I ran into a unexpected problem: the drop method from DropTarget was never call, so in the DragGestureListener.dragDropEnd(DragSourceDropEvent) , DragSourceDropEvent.getDropSuccess() was always returning false.
I didn’t investigate much on that but I guess it’s because when the drop happen the cursor is over a window (the window where the ghost image is drawn) and not actually over the component that has the drop target.
So my first fix for this, was to add an offset of 1 pixel between the cursor and the ghost window, it worked well. But what I actually wanted is to have the cursor in the middle of the ghost window.
So the true problem was how to get the mouse event to be consumed by the component below the ghost window.
At this time I remember the testing I have done with non opaque window, you could click through a non opaque window if nothing was painting were the click takes place.

In the example below ,it contains 2 buttons with a bit of space between them, if you click between them the window will lose focus and it’s actually the component behind the window that will grab the focus.





JDialog d = new JDialog();
WindowsUtils.setOpaque(d, false);
JButton b1 = new JButton("button 1");
JButton b2 = new JButton("button 2");
d.getContentPane().setLayout(new FlowLayout());
d.getContentPane().add(b1);
d.getContentPane().add(b2);
d.pack();
d.setVisible(true);

So how could I apply this to my ghost window?

I need the mouse release event to get through the window, so that’s mean the pixel behind the cursor should not be painted. So that’s lead in my implementation to change the clip of the graphics when I am painting the image to keep away one pixel from being painted:


Area a1 = new Area(getBounds());
//remove the center point (where the cursor is)
a1.subtract(new Area(new Rectangle(getWidth() / 2, getHeight() / 2, 1, 1)));
g2.setClip(a1);

The missing pixel is normaly not visible by the user as the cursor is over it, but you can see it by taking a screen shot that does not contain the cursor.






To allow a component to be drag:

DnDGhostManager.enableDrag(JComponent,Transferable)

to declare a drop location

DnDGhostManager.enableDrop(JComponent,DataFlavor,DnDSuccessCallBack)

where DnDSuccessCallBack is an interface containing a single method

void dropSuccess(DataFlavor,DropTargetContext,Transferable,Point);


Demo:



Sunday, March 14, 2010

Substance L&F: Button with multiline text

Usually when you need a button with text on multiple lines, you have to use html to format the text.

This works fine, but as I am using Substance L&F, I ran into an unsupported featured concerning the foreground colour using html.
First, let’s see how substance displays the text in a button.
The foreground color depend of the button state (default,pressed,armed,…)
There is an animated transition when switching state.

When using html as text the htmlView won’t behave like normal text, I mean by that, that the foreground color will not change because the rendering is delegated to the basicHtml View and not to substance.


SubstanceButtonUI default state with HTML text




SubstanceButtonUI rollover state with HTML text, here you can barely see the text





SubstanceButtonUI default state with normal text





In most of the substance LAF, it’s not big a problem as the foreground color doesn’t change with the button state. But with some LAF as SubstanceMagellanLookAndFeel there is a problem as the text become barely readable during the rollover state.
And even if it stay readable, it just seem strange that some buttons have a black foreground when rollover and some a white one as it’s the case for my custom substance LAF.

So the plan was to make a custom SubstanceButtonUI to manage multiple text lines on JButton, without using the basic html rendering, using \n to split the text.

There are 2 things to do:
To split the text when it’s changing
To paint the text

Splitting the text
Cache of the lines


private String[] splittedText = null;

private class TextChangeListener implements PropertyChangeListener {
        @Override
public void propertyChange(final PropertyChangeEvent evt) {
    if (evt.getNewValue() != null && !evt.getNewValue().toString().isEmpty()) {
        SubstanceMultiLineButtonUI.this.splittedText = evt.getNewValue().toString().split("\n");
    } else {
        SubstanceMultiLineButtonUI.this.splittedText = null;
    }
    SubstanceMultiLineButtonUI.this.button.repaint();
}
};


Install the text change listener
@Override
    protected void installListeners(final AbstractButton b) {
        super.installListeners(b);
        this.textChangeListener = new TextChangeListener();
        b.addPropertyChangeListener("text", this.textChangeListener);
    }


Painting:
Loop through the lines
Find the correct position to paint each line
Paint the line using the paintButtonText from substanceButtonUI
Find the correct position for the icon and paint it.
for (final String s : this.splittedText) {
    viewRect.x = i.left;
    viewRect.y = i.top;
    viewRect.width = b.getWidth() - (i.right + viewRect.x);
    viewRect.height = b.getHeight() - (i.bottom + viewRect.y);

    textRect.x = textRect.y = textRect.width = textRect.height = 0;
    iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;

    String line = SwingUtilities.layoutCompoundLabel(c, fm, s, b.getIcon(), b.getVerticalAlignment(), b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(), viewRect, iconRect, textRect, b.getText() == null ? 0 : b.getIconTextGap());

    // find the icon location
    if (b.getIcon() != null) {
        if (trueIconRect.x != 0) {
            trueIconRect.x = Math.min(trueIconRect.x, iconRect.x);
        } else {
            trueIconRect.x = iconRect.x;
        }
        trueIconRect.y = iconRect.y;
        trueIconRect.width = iconRect.width;
        trueIconRect.height = iconRect.height;
    }

    //compute the text Y position
    double part = fontMetrics.getStringBounds(line, g2).getHeight();
    double y = 0.5 * this.button.getHeight();
    y -= part * 0.5 * this.splittedText.length;
    y += part * lineNumber;
    textRect.y = (int) y;
    //deletegate to substance
this.paintButtonText(g2, b, textRect, line);
                        
    lineNumber++;
}





//paint the icon
if (b.getIcon() != null) {
    if (this.splittedText==null||this.splittedText.length==0) {
    // if no text
        SwingUtilities.layoutCompoundLabel(c, fontMetrics, "", b.getIcon(), b.getVerticalAlignment(), b.getHorizontalAlignment(),viewRect, trueIconRect, textRect, b.getText() == null ? 0 : b.getIconTextGap());
    }
    paintIcon(g2, c, trueIconRect);
}


SubstanceMultiLineButtonUI default state




SubstanceMultiLineButtonUI rollover state





To use this UI on a button:
JButton b = new JButton("Do this\nand that");
b.setUI(new SubstanceMultiLineButtonUI(b));



Monday, March 1, 2010

Customs Toggle buttons



First I will talk about why I ended up creating this customs jtoggle button.

I was asked, what would be the best way to show a choice among 2 available options (option A or option B).

Usually when you want the user to select one option among a few (let’s say less than 5) you can use some JRadioButton/JCheckBox/JToggleButton.
JCombobox/JList are usually used if you have more possible options, doesn’t really make sense (at least for me) to have a JComboBox/JList with only 2 or 3 available options

I had to show the icon corresponding to each of the 2 options.
I could have used JRadioButton + a JLabel (to show the icon) or a JToggleButton.
The image being quiet big (> 100*100 pixels), using a JToggleButton is just wrong and plain ugly for this kind of icon.
So my only option left was to use a JRadioButton with a JLabel to show the icon.

After some tests, I wasn’t really satisfied about how it looked.
I really don’t like the look of the selected/unselected icon sticked to the icon representing the option.











So I though why not use directly the image as component with some kind of visual feedback apply to the image (and not outside like for the JRadioButton)

The visual feedbacks I considered for this are the following:
-translucent if not selected
-smaller image if not selected
-a selection ring
And of course it should have animated transition between selected/unselected state switch


You can find the implementation at: ShrinkingToggleImageButton








Some time later, I needed to have a panel to select among 3 options, but this time there was no icon. So I should have just used 3 JRadioButton and be done with it. But considering the application is running on a device with touch-screen and that selecting an option trigger a big change in the screen, it made more sense to have JToggleButton used there.



















As having 3 JToggleButton side by side doesn’t look that great, I decided to see If I could get something that make sense with a better look.


I rapidly decided to try using ShrinkingToggleImageButton, after playing a bit to create the image, I came up with the following result.




















I was happy about the result, it looks way better on the screen it’s used on, than some JRadioButton/JToggleButton.





Some time later, I needed a way to select options among 3 again, but this time it had an icon + some text, as I had the same constraints (touch-screen – big buttons needed), I started to play again with ShrinkingToggleImageButton.

This time I created a class to hold this component, instead of just giving the right image to the shrinkingToggleImageButton: ShrinkingToggleImageAndTextButton

Also it can deal with multiple line text, use \n to wrap to the next line


























Thursday, February 11, 2010

Bean reader JTable

I have already written about JTable on this blog, a lot can be written about it.
This time I will talk about a custom jtable I developed in order to have the simplest way to show READ ONLY data.

First how I was coding before:


class Person{
String firstName;
String lastName;
/*..*/
}

DefaultTableModel dtm= new DefaultTableModel();
for(Person p: getListPersons()){
Dtm.add(new Object[]{p.getFirstName(),p.getLastName()});
}

Dtm.addColumn(“firstName”);
Dtm.addColumn(“lastName”);

JTable table=new JTable();
table.setModel(dtm);


And to know which Object is currently selected:

Int index=table.getSelectedIndex();
Int modelIndex=table.convertRowIndexToModel(index);
Person selected=getListPersons.get(modelIndex);


After coding this way too many time I wondered:
As we only show only one kind of object in the table, wouldn’t it be easier:
to have a table of T
to be able to add/remove a T
to be able to get the selected T
All this without coding a custom JTable or/and TableModel each time


So the plan is to have an easy way to
-Define the class of objects to show using the JTable
-Define the columns
-Add/remove an object from the table
-Get the selected objects


So now let’s see the implementation I called BeanReaderJTable

First we need a generic parameter

public class BeanReaderJTable<T> extends JTable {/*…*/}


The contructor take the field names and the column names you want as header value.


public BeanReaderJTable(String[] fields, String[] title)


Adding/removing a row or getting the selected objects can’t be easier:

addRow(T)
addRow(T[])
addRow(Collection<T>)
removeRow(T)
getSelectedObject():T
getSelectedObjects():T[]



What is doing the actual job is the GenericTableModel.
The important job is done in getValueAt(int,int). The reflexion API is used to retrieve the value of a given pair field+row


Now let’s see a sample:


// declare the type + fields+column title
String[] fields = new String[] { "size", "size.width", "size.height", "class", "visible", null };
String[] titles = new String[] { "size", "width", "height", "class", "is visible", null };
BeanReaderJTable<Component> table = new BeanReaderJTable<Component>(fields, titles);
//populate the table
table.addRow(getAllComponents(frame));


You may have noticed, I left the last field empty, doing so, an empty column is created that can be used to set for example a button cell editor

And also what you can see, is that you can access nested field like size.width

This time you should actually be able to access the code source repository, my java.net project has been approved:
BeanReaderJTable





Sunday, February 7, 2010

Closing windows in style

This week some fun stuff!

In the post about dialog with drop shadow i mentioned the class WindowFadeInManager to open or close a window with a fadein / fadeout effect, but there is another way to close window in style.

First, don’t make me say what I didn’t, such close transitions should not be used on every windows in your app, if you do it will just annoy the user.
I usually use them to close loading/splash/main frame of an application.



The base class where all the main work is done is
CloseTransition , it’s an abstract class where the only abstract method is

protected abstract void paintImage(Graphics g,float animFraction);


This method is responsible of drawing the closing window for a given animation fraction (from 0 to 1)

The CloseTransition class take care of making a screenshot of the actual window that will get closed. Then it starts the close animation.

So what to do in this paintImage method?
One easy thing is to show that a window is closing via shapes. For example simply by making the window shrink.

This is done by extending the ShapeTransition class.
This class has one abstract method:

public abstract Shape getShape(float animFraction);



This method will get called to set the clip before painting the image of the closing window. There is also an option to automaticly shrink the image of the closing screen.
So to create a close transition, this method will have to return an empty shape when animFraction=1 and a shape that cover totally the original window when animFraction=0

By default in the ShapeTransition the original window is painted with an alpha value of 1-animfraction to make the window disappear smoothly, you can change this behaviour by setting the fadeout property to false.

In the repository you will find the following implementation of close transitions:
ShrinkTransition
CircleTransition
RectanglesTransition (the one I use in my apps)
FadeOutTransition
RotationTransition
PinchTransition, the funniest one, only work well on small window


To start a close transition you can follow this example:

myFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
myFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
RectanglesTransition transition = new RectanglesTransition(myFrame);
transition.startCloseTransition();
}
});

By default the action at the end of the transition is system.exit(0); because those close transitions are meant to be used to close main frame of an application but anyway you can set the end action:

transition.setEndAction(AbstractAction);












Friday, February 5, 2010

Moving to java.dev.net

As kenai is closing i am moving my code repository to https://free-the-pixel.dev.java.net


edit:
here is the mail i got a few hours after migrating to java.net :


In an effort to get information out to the Kenai community quickly, while trying to manage the integration of our two companies, I think we did a poor job at communicating our plans for Kenai.com to you. I would like to remedy that now. Our strategy is simple. We don't believe it makes sense to continue investing in multiple hosted development sites that are basically doing the same thing. Our plan is to shut down kenai.com and focus our efforts on java.net as the hosted development community. We are in the process of migrating java.net to the kenai technology. This means that any project currently hosted on kenai.com will be able to continue as you are on java.net. We are still working out the technical details, but the goal is to make this migration as seamless as possible for the current kenai.com projects. So in the meantime I suggest that you stay put on kenai.com and let us work through the details and get back to you later this month.

Thanks for your feedback and patience.

Ted Farrell
Oracle Corporation


that's great news,
well i did the manual migrating to java.net but i won't complain i didn t have so many stuff as i only started begining on this years.