hansmuller's Blog
Introducing the SceneGraph Project
Introducing SceneGraph
I haven't written a blog entry since January when I advertised the fledgling
href="http://weblogs.java.net/blog/hansmuller/archive/2007/01/application_fra.html">
Swing Application Framework (JSR-296)
project with an uncharacteristically brief item. Work on that
kept me busy until summer, when the Java FX juggernaut got
underway here at Sun. Since then I've devoted much of my time
to leading a project we've called "Scenario" that provides the
graphical runtime for Java FX Script. We're now a public
project on java.net called
scenegraph.dev.java.net
.
You'll find downloads of the the 0.4.1 version of the Scenario
source and binaries and (very) sketchy javadoc on the site. The
0.x version number is intended to convey the fact that the API
hasn't stabilized yet. It's sufficient for experimentation and
the implementation was robust enough to support a port of the
Java FX Script interpreter, which we've showcased with a
Scenario version of the
FXPad Demo
.
Obviously, we don't recommend putting anything based on Scenario
into production. Not yet.
The code is being made available under the GPLv2 license.
Passion about open source licenses is not something I possess so
I'll leave the shouting about the implications of this choice to
others. Suffice it to say that, per my limited understanding of
these matters, you're free to share the code, and in the process of
developing it further. We're moving our discussions of the API
to the newly minted
Scene Graph java.net forum
.
If you're interested in the project's evolution, that would be a
good place to start looking.
There are quite a few engineers working on Scenario, most of
whom have made bigger contributions to the new software than I
have, and you'll be hearing from them in their own blogs before
too long. For now, what I'd like to do is to provide an
introduction to the new Java APIs and just one demo. The team
has written a whole raft of demos and we'll be opening up a
subproject before too long, that contains the entire
demo catalog.
Demo
All of the examples that follow are part of a demo, each one
occupies a tab. If you press "control-T" after clicking on a
demo, you'll get a nice interactive tree view of the scene
graph's structure, thanks to
Amy Fowler
for that! So press the orange button to launch the demo.
Note also: the demo scales the selected example scene to fit.
This is done with a small extension to JSGPanel,
take a look at the
code
if you're interested.
Basics, Hello World
src="http://weblogs.java.net/blog/hansmuller/archive/intro-screenshot1.PNG"
width="290"
height="195" />
A scene graph, really a tree for now, is a data structure you
create from leaf nodes that represent visual elements
like 2D graphics and Swing components, filter nodes that
represent visual state, like 2D transforms and composition, and
group nodes that manage a list of children. All nodes
are subclasses of SGNode and have a parent node,
rectangular bounds, visibility, and support for event
dispatching. Leaf nodes extend SGLeaf and have
paint/repaint methods similar to
AWT/Swing. SGFilter nodes have one child, and
SGGroups have a list of them. To display a scene
graph you set the scene property of a
JSGPanel, and add the panel to a Swing application
in the usual way. Here's a simple example:
SGText text = new SGText();
text.setText("Hello World");
JSGPanel panel = new JSGPanel();
panel.setScene(text);
panel.setPreferredSize(new Dimension(640, 480));
// create a JFrame, add the panel to it, etc..
One unusual line from the previous example, from a Swing
programmer's perspective, is that we've explicitly set the
preferred size of the JSGPanel. Although the
JSGPanel will compute (and recompute...) a preferred size based
on bounds of its scene, it's usually a good idea to define a
reasonable fixed preferred size instead. To make the scene
slightly more interesting to look at, we can set the
SGText node's font and color, turn on antialiasing,
and configure the panel's background color:
SGText text = new SGText();
text.setText("Hello World");
text.setFont(new Font("SansSerif", Font.PLAIN, 36));
text.setAntialiasingHint(RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
text.setFillPaint(Color.WHITE);
JSGPanel panel = new JSGPanel();
panel.setBackground(Color.BLACK);
panel.setScene(text);
panel.setPreferredSize(new Dimension(640, 480));
More Hello World: Groups and Shapes
src="http://weblogs.java.net/blog/hansmuller/archive/intro-screenshot2.PNG"
width="290"
height="195" />
Most of the Scenario classes are simple Java Beans. They
provide null (no parameters) constructors and mutable
properties. The API is intended to be "minimal" in the sense
that it exposes the capabilities of the underlying Java 2D and
Swing classes but does attempt to substantially simplify or
abstract them. Scenario is intended to serve as the basis for
higher level abstractions that require a scene graph, notably
Java FX Script. For that reason, you may find programming
against the Scenario API directly to be a bit tedious. Creating
your own abstractions that simplify creating scene graphs is
definitely the order of the day.
To add a border to the simple Hello World scene, we'll write a
method that takes any scene graph node and puts a garish rounded
rectangle border behind it. This example demonstrates using the
SGShape leaf node which can render any Java 2D
href="http://java.sun.com/javase/6/docs/api/java/awt/Shape.html">
Shape
,
like lines, arcs, bezier curves, and of course rounded
rectangles. Just to highlight the support for both
filling and stroking (drawing the outline of)
shapes, we'll do both here. The createBorder
method creates a red and yellow rounded rectangle that's a
little bigger than the node it's given. It returns an
SGGroup scene graph node that contains both the
original node and the "border".
SGNode createBorder(SGNode node) {
Rectangle2D nodeR = node.getBounds();
double borderWidth = 10;
double x = nodeR.getX() - borderWidth;
double y = nodeR.getY() - borderWidth;
double w = nodeR.getWidth() + (2 * borderWidth);
double h = nodeR.getHeight() + (2 * borderWidth);
double a = 1.5 * borderWidth;
SGShape border = new SGShape();
border.setShape(new RoundRectangle2D.Double(x, y, w, h, a, a));
border.setFillPaint(new Color(0x660000));
border.setDrawPaint(new Color(0xFFFF33));
border.setDrawStroke(new BasicStroke(borderWidth / 2.0));
border.setMode(SGShape.Mode.STROKE_FILL);
border.setAntialiasingHint(RenderingHints.VALUE_ANTIALIAS_ON);
SGGroup borderedNode = new SGGroup();
borderedNode.add(border);
borderedNode.add(node);
return borderedNode;
}
Adding a the border doesn't change the code that creates the
scene graph very much:
SGText text = new SGText();
text.setText("Hello World");
// same as before ...
panel.setScene(createBorder(text));
Rotating Hello World: Transforms
src="http://weblogs.java.net/blog/hansmuller/archive/intro-screenshot3.PNG"
width="290"
height="195" />
As you can see in the previous example, the
SGNode#getBounds() method returns a bounding box
for the its node, in the way same way
Component#getBounds() does for AWT and Swing
components. In AWT and Swing, a component's parent node
recursively defines its origin in terms of a translation, which
is the value of getParent().getLocation(). Scene
graphs are much more flexible than that. The relationship
between a child and its parent node can be defined with any 2D
affine transformation.
To rotate a scene graph node around a point, you have to
assemble a chain of three transforms that: translate the node so
that the rotation point is at the origin, rotate the desired
amount, translate the node back to its original location.
Transforms are created with SGTransform nodes,
which are SGFilter subclasses because they have
just one child, which is the node the transform is to be
applied to. Here's a method that creates such a chain and uses
the node's bounds' center as the rotation point:
SGTransform createRotation(SGNode node) {
Rectangle2D nodeR = node.getBounds();
double cx = nodeR.getCenterX();
double cy = nodeR.getCenterY();
SGTransform toOriginT = SGTransform.createTranslation(-cx, -cy, node);
SGTransform.Rotate rotateT = SGTransform.createRotation(0.0, toOriginT);
return SGTransform.createTranslation(cx, cy, rotateT);
}
To use the createRotation method we apply it to the
node that's going to spin around its center, and then add the returned
value to the scene instead of the node itself. The return
value is the chain of three transform nodes followed by the
original node. To specify a rotation you have to refer to the
second SGTransform.Rotate from the chain and change
its rotation property:
SGTransform scene = createRotation(node);
SGTransform.Rotate rotateT = (SGTransform.Rotate)scene.getChild();
rotateT.setRotation(...);
Images and More Layout
src="http://weblogs.java.net/blog/hansmuller/archive/intro-screenshot4.PNG"
width="290"
height="195" />
Images can be incorporated in a scene graph with the
SGImage node type. To add one to our scene so that
it appears to the right of the "Hello World" text, we'll have to
create an SGGroup node that contains the text and
the image, and then use SGTransform nodes to
arrange the group's children along a row. Here's a method that
creates such a group:
SGNode createRow(SGNode... nodes) {
double rowHeight = 0.0;
for(SGNode node : nodes) {
rowHeight = Math.max(rowHeight, node.getBounds().getHeight());
}
SGGroup row = new SGGroup();
double x = 0.0;
double gap = 8.0;
for(SGNode node : nodes) {
Rectangle2D nodeR = node.getBounds();
double y = (rowHeight - nodeR.getHeight()) / 2.0;
double dx = x - nodeR.getX();
double dy = y - nodeR.getY();
SGTransform xlate = SGTransform.createTranslation(dx, dy, node);
row.add(xlate);
x += nodeR.getWidth() + gap;
}
return row;
}
The code to create the SGImage node and the overall scene looks
like this:
SGNode createEarth() {
BufferedImage image = null;
// ... code to load the image file
SGImage sgImage = new SGImage();
sgImage.setImage(image);
return sgImage;
}
// ...
SGNode row = createRow(createHelloWorldText(), createEarth());
SGNode scene = createBorder(row);
JSGPanel panel = new JSGPanel();
panel.setScene(scene);
Handling Input
src="http://weblogs.java.net/blog/hansmuller/archive/intro-screenshot5.PNG"
width="290"
height="195" />
All nodes can handle mouse and keyboard events and the support for
doing so is very similar to what AWT/Swing provides. For
example to make a node handle mouse events you add an
SGMouseListener. SGMouseListener
combines the methods from AWT's MouseListener and
MouseMotionListener and it adds an
SGNode argument to each one. SGKeyListener
and SGFocusListener are similar. The source of a scene
graph mouse or keyboard event is always a JSGPanel and the
additional node argument indicates the node the event was
actually dispatched to.
In this example, we've added a mouse listener to the
earth node in the scene from the previous example and restored
the support for rotating the scene. Dragging the earth with the
mouse rotates the scene. Here's the code that sets up the scene
and its SGMouseListener:
SGNode earth = createEarth();
SGNode row = createRow(createHelloWorldText(), earth);
SGTransform scene = createRotation(createBorder(row));
final SGTransform.Rotate rotateT = (SGTransform.Rotate)scene.getChild();
SGMouseListener changeRotation = new SGMouseAdapter() {
@Override public void mouseDragged(MouseEvent e, SGNode node) {
Rectangle2D r = e.getComponent().getBounds();
double x = e.getX() - r.getCenterX();
double y = e.getY() - r.getCenterY();
rotateT.setRotation(Math.atan2(y, x));
}
};
earth.addMouseListener(changeRotation);
JSGPanel panel = new JSGPanel();
panel.setScene(scene);
Although there's a great deal more to say by way of introducing
Scenario, this blog entry has grown long enough that I'd better
just take the same tack
href="http://weblogs.java.net/blog/chet/archive/2008/01/been_there_scen.html">
that Chet did
,
and declare this "Part 1".
The code for the examples can be found here:
http://weblogs.java.net/blog/hansmuller/archive/Intro.java
A easy to build version will appear along with the other
Scenario demos, later this month.
- Login or register to post comments
- Printer-friendly version
- hansmuller's blog
- 3807 reads
Application Framework Prototype Bows
I've made a prototype of the fledgling JSR-296 API available,
it's
href="https://appframework.dev.java.net/">
https://appframework.dev.java.net/.
There's a quick overview doc and downloads of the source code,
the javadoc, and the AppFramework.jar file. If you're
interested in this API, please take a look at the overview, and
download the code and then take a look at some of the examples
and the javadoc. You can post feedback here or, if you want to
participate in the long-term discussion, subscribe to the
appframework.dev.java.net "users" mailing list:
https://appframework.dev.java.net/servlets/ProjectMailingListList
.
The users alias is the last one listed.
That's all I really wanted to say. I don't want to make too
much of a commotion about this version of the design because
there's still quite a bit that remains to be done. I was hoping
that this would be a sort-of stealth release: not
terribly noticeable, unless you know where to look. On the
other hand, I know there are Swing developers who aren't members
of the JSR-296 expert group, who'd like to take stock of where
this project is going. And I know there are experienced Swing
developers out there, some of whom have built their own
application frameworks, that would like see how this one
measures up. I'd welcome feedback from anyone who's interested
and I'll promise to respond promptly, unless you bring up a
really difficult issue or a really large number of them. That
might take longer.
Note also: the JCP defines a milestone called "Early Draft
Review" that means the expert group thinks the spec is complete
enough to begin fine tuning. We have not reached that milestone
yet.
- Login or register to post comments
- Printer-friendly version
- hansmuller's blog
- 2701 reads
Property Syntax for Java? A Constructive Alternative
Having written, by conservative estimates, about a jillion Java
Beans classes over the years, I have to say that I'm amazed that
we'd seriously consider changing the Java language to trivialize
this kind of Java Bean property. It certainly is a property per
the spec, a read/write property at that, but - as a Swing
developer - it's the kind of property I almost never write. And
if repetitive boilerplate is what we're hunting with this
language change, then we're shooting at rabbits while a herd of
buffalo thunders by. The mighty buffalo of the Java Beans
boilerplate animal kingdom are bound properties. They're the
kind of properties we write so that our beans can be
automatically and dynamically synchronized with a GUI or with
each other. As a desktop developer, I almost always write bound
properties.
To write a bound property properly you've got to ensure that
your class defines or inherits support for a
PropertyChangeListener. That's about 20 lines of code just to
get started:
class FooBean {
private final java.beans.PropertyChangeSupport pcs;
public FooBean () {
pcs = new PropertyChangeSupport(this);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
public PropertyChangeListener[] getPropertyChangeListeners() {
return pcs.getPropertyChangeListeners();
}
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
pcs.firePropertyChange(propertyName, oldValue, newValue);
}
}
And then there's the definition of each read/write property
which should: check the validity of new values in its
set method as well as calling firePropertyChange to notify
PropertyChangeListeners, and defensively copy the return value
(if necessary) in its get method. I suppose one could
concoct syntax that would simplify all of this, at least a
little, as well as allowing for read-only/write-only variants.
But that's not the proposal I wanted to make here.
If you consider the property keyword proposal in light of Java's
origins in the C language, then it's pretty clear what the
proposal's proponents are really after: structs. It's not
about defining properties, it's about simplifying defining a
Java class that's comparable to a struct in the C language. So
perhaps the proposal should really focus on allowing one to
write classes, not properties. Where this:
struct FooBean { Foo foo; }
Would by equivalant to this (as before):
class FooBean {
private Foo foo;
public Foo getFoo() { return foo; }
public void setFoo(Foo foo) { this.foo = foo; }
}
If you admit that the focus of the property proposal is really
adding support for defining structs in Java, then using "->" to
refer to struct properties feels like coming home again.
struct Point { int x, y; }
Point p = new Point();
p->x = p->y = 0; // oh joy
I'm not a language design expert however I would think that I
would be among the target developers for Java language feature
designed to support properties. In my humble (ha) opinion, the
current proposal serves the needs of Java Beans developers
poorly by targeting a special case that doesn't warrant language
support. Although I would welcome a proposal that also simplified
defining bound properties, I would guess that it would be hard
to invent syntax that would handle the general case without
being obscure. If there is a consituency for the current
proposal, I say: give them structs instead.
- Login or register to post comments
- Printer-friendly version
- hansmuller's blog
- 8544 reads
Comments
Properties access with pointers p->x
by bemowski - 2009-10-20 13:53
I am wondering if an extension to java.util.Properties that I've published over on Google Code meets this need?http://code.google.com/p/eproperties/ EProperties supports nested properties objects, and allows deep access using the 'pointer' syntax.
Javapolis Session Interrupted by Marriage Proposal
Javapolis 2006
I
spent most of last week at the Javapolis conference in Antwerp
Belgium. With nearly 3000 attendees, it's the second biggest
Java conference after JavaOne, and it's held in a movie cineplex
complex called "Metropolis". The venue delivers large halls,
comfortable seats (with beverage caddies!), and dramatic
lighting. Half of each enormous movie screen is devoted to a
live image of the presenter and the camerawork is quite good,
pausing for a dramatic closeup now and then. My laptop wasn't
working correctly so I had to turn around periodically, to
visually sync up with my slides. That meant that the audience
was treated to six linear feet of my balding hemisphere glowing
in the klieg lights. I was thankful that everyone put up with
that, and also with my raspy voice. I'd managed to pick up a
cold the day before leaving, and only hours before I was
scheduled to speak, I could do little more than croak and
wheeze. Things were somewhat better by the showtime, however
from the sound of my voice you might think I'd spent the day
smoking heavily and shrieking.
My presentation topic was JSR-296 "Swing Application Framework".
If you're interested, there's a copy
here.
Within a few months you'll find a nicely edited slides-and-video
version of the presentation on
href="http://www.parleys.com">parleys.com, thanks to
Stephan Janssen, the tireless Javapolis organizer. And when it
appears, do not miss the finale. As Richard Bair and I finished
up a demo of a prototype of the Application Framework
in NetBeans, Stephan appeared and asked to
interrupt the proceedings for a special announcment. He picked
up the microphone and asked a woman who's name I didn't catch to
come forward. Sure enough, a tall young blonde woman came to
the front, looking nonplussed. After a brief pause, a young man
wearing a suit came loping down the aisle and in a moment he was
facing the woman and making a declaration of some kind in Dutch.
She looked pretty nervous and, not knowing where this was all
going, we started feeling a bit anxious too. Until he dropped
to one knee and assumed the international marriage proposal
position. Then we really started to worry, because
here was a situation that could only end really well, or really
badly. The woman was crying (uh oh) and smiling (whew!), and
then nodding her head, and to everyone's relief, we had a happy
betrothal. The couple bounded up the steps to a nice round of
applause, and Richard and I tried to remember what it was we'd
been talking about.
The Javapolis conference was memorable as always. One evening
the organizers arranged for enough french fries and mayonaise to
serve 3000 (that's a lot), they showed the new James Bond movie,
Mark Fleury performed his keynote dressed up like Flavor Flav,
Sun raffled away a Nintendo Wii, the technical sessions were
solid, and, mine was interrupted by a proposal of marriage.
With a happy ending.
Merry Christmas!
Update: Stephan Janssen has published the video, you'll find it here:
http://www.javapolis.com/confluence/display/JP06/2006/12/22/JavaPolis+20...
- Login or register to post comments
- Printer-friendly version
- hansmuller's blog
- 4016 reads
Dialog Diatribe
I've been writing the occasional small application recently and
now and then I blunder into a problem with Java SE that's,
uh..., well, annoying. I realize that I'm not the only one
who's had this experience and I'm probably not the only one who
seeks relief by writing a lengthy diatribe and then sending it
to whomever might be guilty of creating the situation. Of
course, in my case that's often me, and since relief usually
doesn't come from berating oneself, I'm guilty of sending the
occasional long crabby missive to the people who are currently
responsible for maintaining things that I'm probably responsible
for bollocksing up in the first place. It's not a particularly
endearing habit.
I sent the following to Swing's technical lead,
Shannon
Hickey, and he confirmed that the details, though twisted
with bile, are essentially correct. So in the interest of
furthering my own therapy, and also to ensure that some record of
this will be stored away in Google's indices till the end
of time, I thought I'd share.
I would think that a fairly common idiom in a Swing application
would be to popup a dialog in response to selecting a menu item.
Given Matisse, we'll
assume that the JDialog has been created with the IDE, rather
than some JOptionPane convenience method, and given rudimentary
aesthetics, assume the dialog should be centered over the menu
item's frame. Accomplishing this seems to be much too
difficult:
public void showMyDialog(ActionEvent e) {
// How to find the Dialog's Frame owner?
Window dialogOwner = null;
JDialog dialog = new MyDialog(dialogOwner, true); // true => modal Dialog
dialog.pack();
// How to center the Dialog?
dailog.setVisible(true);
}
The first problem to deal with is mapping from the menu item's
ActionEvent to the frame that contains the menu item. The
frame will be the dialog's owner as well as the component we're
going to center the dialog relative to.
There seems to be an overabundance of SwingUtilities methods that
address this trivial problem:
Window getWindowAncestor(Component c)Window windowForComponent(Component c)Component getRoot(Component c)
Sadly, none of them "work" for a JMenuItem. They all simply
traipse up the parent chain and in our case they find a
JPopupMenu and then null.
To find the Frame that owns a JMenuItem, we have to follow the JPopupMenu's
"invoker" property, which gets us back into the component
hierarchy. So to find the frame that corresponds to an ActionEvent
one must write (!):
Frame frameForActionEvent(ActionEvent e) {
if (e.getSource() instanceof Component) {
Component c = (Component)e.getSource();
while(c != null) {
if (c instanceof Frame) {
return (Frame)c;
}
c = (c instanceof JPopupMenu) ? ((JPopupMenu)c).getInvoker() : c.getParent();
}
}
return null;
}
It would more useful to have written
<i>window</i>ForActionEvent but sadly support for
creating Dialogs whose owner is a Window (the parent class for
Frames and Dialogs) only appeared in Java SE 6, and I needed
code that worked for Java SE 5. It's also worth noting that
this works for Applets too, although you'd be forgiven for not
guessing that this is true. Applets do have a Frame parent
that's created by the Java plugin and whose bounds are the same
as the Applet (Panel) itself.
But we're still not done, because we must also center the dialog
over the frame. Naturally there are other useful positions for
the dialog. Centering a dialog over its frame happens to be what
started me on this quest.
I have it on good authority that
href="http://download.java.net/jdk6/docs/api/java/awt/Window.html#setLocationRelativeTo(java.awt.Component)">
Windows.setLocationRelativeTo() is the handy method for
this job. The javadoc for this method isn't promising:
Sets the location of the window relative to the specified
component.
OK so far. Except it sounds like I'm going to have to compute the
relative origin of my dialog and deal with edge (of the screen)
conditions. Yech.
If the component is not currently showing, or c is null, the
window is placed at the center of the screen. The center point
can be determined with GraphicsEnvironment.getCenterPoint
[sic]
Huh? What does "the component" refer to in this sentence? I
assume they're not referring to this Window and I have to
wonder what "showing" means in this context. Is is the same
thing as getVisible() being true? If not, do I
have to hope that my menu item is still "showing" when this
method is called? And what's this advice about
getCenterPoint (and where's the period)? In my
case the Window and its menu item are visible, so I'm hoping
none of this stuff applies. Because I don't really understand
it.
If the bottom of the component is offscreen, the window is
placed to the side of the Component that is closest to the
center of the screen. So if the Component is on the right part
of the screen, the Window is placed to its left, and visa
versa.
This, no doubt, means that the method will endeavor to find a
location for my dialog that respects the relative location I've
specified, without making part of the dialog appear off-screen.
Good, I think.
So I still appear to be stuck with computing an origin for my
dialog that centers it relative to its owner. Before I code
that, I try leaving the origin of the new dialog at 0,0, which
is the default:
public void showMyDialog(ActionEvent e) {
Window dialogOwner = frameForActionEvent(e);
JDialog dialog = new MyDialog(dialogOwner, true);
dialog.pack();
aboutBox.setLocationRelativeTo(dialogOwner);
dialog.setVisible(true);
}
Miraculously, this works. The dialog appears centered over the
dialogOwner unless that would cause the dialog to appear
off-screen. I have no idea why it works, since according to the
"spec" (and the name of the method) I should have had to compute
an appropriate relative origin for the dialog. But I guess I
don't.
Frankly, I think this whole mess is a mini-travesty. If I'm
going to show a dialog, I should be able to do so without
writing code that digs around the component hierarchy and
without experimentally determining what something as simple (and
not terribly useful) as Window.setLocationRelativeTo() does.
There, that feels a little better.
A
href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4231737">
seven year old bug that covers the menu item to frame lookup
problem is still open. Given the fact that it's accumulated
exactly 0 votes in that time, perhaps no one has ever cared about
the problem quite as much as I do at this moment. I would think
that a cleaner way to handle this case would be some static
methods that handled the entire idiom, for example:
public void showMyDialog(AWTEvent event) {
Window dialogOwner = Window.eventToWindow(event);
JDialog dialog = new MyDialog(dialogOwner, true);
Window.showModalDialog(dialog); // Center dialog relative to its owner
}
And Shannon suggested that the method name might be rationalized
as implying that the Window is be moved to a location that makes
its relationship to the component parameter obvious.
Typically that means centering the Window relative to the
component. In return for that tortured explanation, I had to
agree to file an RFE about the
Window.setLocationRelativeTo() javadoc. I haven't
done so yet. But I will.
Thanks for listening.
- Login or register to post comments
- Printer-friendly version
- hansmuller's blog
- 1162 reads
JSR 296 Bows - Swing Application Framework
For the past five months or so, I've been working on a fledgling
project to define an application framework for Swing.
Some people are highly organized. They set long and short term
goals according to a stable set of priorities and then focus on
nothing else but progress. These people, with their powerful
work ethic and bulging resumes, are often successful. They get
up every morning, attack the day's tasks, take stock of their
progress, and then dream of how to get the job done just a
little more efficiently in the morning. And after exactly 6
hours sleep, they do it all over again. These people get things
done.
I am not these people. I admire their drive and their
accomplishments, and sometimes I wave as their phalanx trots by.
I could try and justify my own meandering progress by pointing
out all the things that these people miss as they forge forward,
shields on forearms, eyes straight ahead. But I will not,
because the truth is that I'm just not highly organized. Not
meant to be at the head of the V formation, with a list of the
five things that must be done before sundown taped to the inside
of my protective helmet. I'll be successful, but in doing so,
I'll surprise everyone a little. Even me.
Earlier this year I spent some time studying the NetBeans and
Eclipse IDE subsets that are referred to as "Rich Client
Platforms". I was impressed by the scope of both frameworks and
the evolving support for them in the corresponding tools. On
the other hand, I was concerned by their complexity, and by the
steep learning curve that new desktop application developers
would face. Making a substantial investment in learning a rich
client platform seems warranted when the application you're
building roughly approximates the size and shape of a general
purpose IDE. Similarly, if you're trying to marshall a very
large loosely coupled engineering team, like the teams that have
built NetBeans and Eclipse, then you'll find that the structure
of these platforms help to organize the work. I don't think
those situations are typical.
We've made a scientific survey of Swing developers and
discovered that of the 2.5 jillion applications that have been
written in the past 8 or 9 years, most of them are smaller than
your average IDE. Most of them employ smaller teams and most of
them are built by developers with less time and less experience
then the authors of our big IDEs. I think we need to make it
easier for the average developer to start the average
application, and to finish it. To me that implies that a small
desktop application framework is needed. Not a framework for
small applications, but a framework that's small enough for a
developer to understand after an hour's study.
That said, I don't think
there's any value in trying to encapsulate or hide the
underlying Java platform. That vast API landscape is vital to
modern applications. Networking, threading, IO, GUI, graphics,
XML, and all the rest are in there because they're essential to
most applications. Perching an enormous application framework
on top would only make the overall platform more intimidating.
We surely don't want that.
Having decided that a narrowly focused application framework was
a good idea, I immediately plotted a course for a prototype, and
then, clipboard in hand, carpenter's pencil tucked behind my ear,
I spent about two weeks building a
href="http://weblogs.java.net/blog/hansmuller/archive/2006/03/multisplitpane.html">
multi-way split pane.
It's not particularly easy to explain why this was the right
next step to take. So I will not try. You can read the article
and try the web started demos on java.net:
href="http://today.java.net/pub/a/today/2006/03/23/multi-split-pane.html">
MultiSplitPane: Multi-Way Splitting Without Nesting. Of
course, if you do, you're demonstrating the same disorganized
tendencies that led me to write it in the first place.
Thankfully, since then, two processes have kept me (mostly) on
track. At JavaOne Scott Violet and I gave a technical session
about the application framework prototype, and I've filed a JSR
to cover the development of the API. The JavaOne session was
href="http://weblogs.java.net/blog/hansmuller/archive/ts-3399-final.pdf">
A Simple Framework for Desktop Applications (TS-3399).
Click on the link to download a PDF of the slides. I've also
filed JSR 296,
and on Tuesday May 15th, it was approved by the JCP executive
committee. If you're interested in joining the JSR 296 expert
group (EG), you can do so from the "I would like to join this
Expert Group" link on the JSR 296 page. I'm setting up the EG
now and hope to begin work in a few weeks. If all goes well,
we'll produce reference implementations of the framework for
Tiger and Mustang Java SE releases, and the framework will be
incorporated in the next major SE release, code named Dolphin.
This blog is my way of publicly introducing the Swing
Application project and JSR 296. As the expert group gets
underway I hope to have a java.net project up an running that
will contain snapshots of the evolving spec and implementation.
I'd also like to host a set of smallish applications, built with
the framework ofcourse, alongside the project. There's no
better way to keep a framework project focused on real problems
than by using it. So we'll do so, and as things stabilize,
hopefully other developers will give the framework a test drive.
So I wrote all of the above on Monday (four days ago) and then I
thought: it would be nice to conclude this announcement with a
little demo app. Something amusing to reward the reader for
slogging their way through all of this. So, I decided to create
a little slide show, using Chet Haase's
timing framwork
,
that displayed a few pictures in sync with some music. I
used the
JLayer (pure Java!) MP3 player
library to play the Sunrise movement from Also Sprach
Zarathustra (that's the opening theme from 2001: A Space Odyssey).
Jeff Dinkins applied some magic to shorten the
music to about 51 seconds, because we knew that none of you
would have enough patience to listen to the complete minute and
a half, when accompanied by my feeble photo montage. Finishing
this project was supposed to have consumed an afternoon. Sadly,
life imitates blog, and here it is Friday. On the upside, I
managed to uncover a bad interaction (bug) between Mustang and
the new NetBeans Java Web Start plugin. On the downside, after
spending more hours than I'd care to admit trying to find the
perfect sequence of photos on Flickr and Yahoo and Google, I've
created the following bit of pretentious codswallop.
You'll need to make sure to turn up the VOLUME ON YOUR PC to
hear the soundtrack. Dawn starts out dark, and it's not very
interesting without the catchy music.
![]() |
|
width="88" height="23" style="border-style:none"/> |
So, in closing, I'd like to thank the JCP executive committee
for approving JSR 296. I'm working on getting the expert group
sorted out right now. We have many more volunteers than we
can handle and I appreciate the show of interest and support.
Updates about JSR 296 progress will appear on javadesktop.org
and often right here. So long as I stay focused.
- Login or register to post comments
- Printer-friendly version
- hansmuller's blog
- 4154 reads
Fire Marshall Extinguishes Dukelele Performance
Tuesday morning this week, I was seated in the vast Moscone
keynote cavern, with 15,000 other Java developers, taking in the
start of another JavaOne conference. The keynotes and demos
were entertaining and I hope you didn't miss the HUGE Swing
href="https://aerith.dev.java.net/">Aerith demo at the
conclusion of the morning. Sadly I did, although I've seen
quite a lot of it over last few weeks. I had to dart out early,
because my first-ever JavaOne musical gig started at about 10:30
and I had to get my bass and set up in time for the big Dukelele
show.
That's right, Dukelele. A Ukelele painted like so:
src="http://weblogs.java.net/blog/hansmuller/archive/dukelele.jpg" width="400" height="533" />
A group of us played music in front of the JavaOne store at one end of
the corridor that connects the Moscone's North and South subterranean
chambers. In addition to me, the band was Hideya Kawahara and Yuichi
Sakuraba playing Ukeleles (Dukeleles!) and singing, Mark Anenberg on a
guitar-shaped drum synthesizer, Chet Haase on a laptop powered
keyboard, and Kaoru Nakamura playing a keyboard/harmonica hybrid
called a Pianaca. And to top if off, Duke danced and hugged people.
We started playing as the keynote audience began flooding past and to
our delight, many of them stopped to listen. Tragically, the Moscone
Fire Marshall did not share our joy. After about 10 minutes he
swooped in and put a stop to the show. I guess we were a fire hazard,
or at least Hideya was. He was really putting his heart into singing
and playing and I suspect that the Fire Marshall was afraid that he
might suddenly burst into flames.
Another rock and roll show, shut down by the man. Not exactly
Altamont Speedway, but definitely a strange and abrupt ending to
our little performance. Fortunately one of our colleagues
videotaped the whole thing and so now you can check it out on
href="http://www.youtube.com/watch?v=wf5_TN602lc">YouTube.com.
No animals were harmed in the making of this video.
We played again, in the afternoon, outside on the sidewalk. It was a
bit breezy and most of the people who drifted by gave us an odd look
and then slipped indoors. One was exception was
href="http://weblogs.java.net/blog/timboudreau/">Tim Boudreau, who
took in the entire set and then threw a quarter in our tip jar.
Except we didn't have a tip jar. Our friend the Fire Marshall seemed
to be pleased that we finished without spontaneously combusting.
Cautious man that he is, he kept a sharp eye on the proceedings from a
safe distance, clutching his fire extinguisher in one hand, and a
fistful of swag from the Motorola booth in the other. Next year
we'll bring our own fire protection.
- Login or register to post comments
- Printer-friendly version
- hansmuller's blog
- 1081 reads
MultiSplitPane: Splitting Without Nesting [Shameless Plug]
One aspect of many docking GUIs is support for reconfiguring tiled
subwindows by dragging shared subwindow edges. MultiSplitPane and
MultiSplitLayout support arbitraily complex tiled layouts that can be
reconfigured interactively and programatically.
As introductory paragraphs go, the previous one has to set a new
record for "not catchy". It always seems easier to start these things
with a personal story or recollection. So how about this:
Earlier this year I was arguing with
Tim Boudreau about docking
frameworks and how best to compute an initial layout, when it occurred
to me that a tree structured model would be a nice way to encode the
relative sizes and positions of the tiles. If nodes in the tree
corresponded to tiles arranged in rows and columns, and if tiles were
allocated space proportionally, it seemed like only a small project to
build a two dimensional analag of JSplitPane. Tim suggested that my
project estimating skills were suspect and besides, there were more
important things to do. He was right on both counts, and yet.
Sometimes, when you're orbiting around an idea, headed somewhere else,
you find yourself captured by the idea's gravitational field. As it
turns out, I was in the software-idea equivalent of a tractor beam.
Over the next two days, I implemented the idea. Since subwindow tiles
were always allocated a fixed percentage of the available space, it
was easy to write a recursive layout algorithm that arranged the rows
and columns. As I was just finishing up,
Josh Marinacci dropped by,
and so I proudly demo'd my creation. The nice way to respond to
someone's new demo, on Friday afternoon, is to smile, deliver a bland
compliment, and suggest that it's time for a cold one. Josh said:
"that's nice, but I don't think anyone would really want to use it".
After I stopped crying, he pointed out that people expect subwindow
tile boundaries to stay put when the window is resized. This is true,
and it makes the problem substantially more difficult. It means
that growing and shrinking the layout aren't symmetrical, and it
implies that the layout should honor preferred tile sizes, until
the user has indicated otherwise by dragging an edge. So, with tears
in my eyes, I started over again.
Version two of the layout algorithm took considerably longer than the
original version. The result is the aforementioned MultiSplitPane and
MultiSplitLayout classes, a small set of demos, a smattering of unit
tests, javadoc, a nod to accessibility (more on that later), and an
article that explains how it all works. The article bows today on
java.net, it's called
MultiSplitPane: Multi-Way Splitting Without
Nesting. I hope you'll find the time to read it. The classes will
become part of the SwingLabs project now and perhaps be incorporated
into a future Java release.
- Login or register to post comments
- Printer-friendly version
- hansmuller's blog
- 3328 reads
Comments
missing file
by zacchia - 2010-02-23 16:48
Hello, is there a way to get the file Multisplit.zip referred to in: http://www2.java.net/article/2006/03/22/multisplitpane-splitting-without... ??? thanksA Reusable BuddyList Component
Every now and then someone drops by to ask about the slick chat/IM
demo components that were shown in the
href="http://developers.sun.com/learning/javaoneonline/2005/desktop/TS-3605.pdf">
Extreme GUI Makeover JavaOne session last year. The Swing
components created for those demos where hacked together in order to
show what's possible and sadly, they're not available as production
quality components just yet. I certainly like the idea of
resuable, configurable/extensible, chat client GUI parts. If I
were building that kind of application I'd be happy to avoid starting
from scratch. This blog is a brief look at one such part. You can
try it out by pressing the launch button.
|
width="88" height="23" style="border-style:none; margin-right:44px"/> |
style="border: none; "/>BuddyList Demo Screenshot |
BuddyCellRenderer is an attempt to build a somewhat reusable
JList CellRenderer for Chat/IM buddy lists. It's job is to
render an object that represents a Buddy roughly like this:
screen name
[status] short message [icon]
Here "status" is one of online, offline, or away. Away status means
that the user is online but busy. The "screen name" is the Buddy's
name, "short message" is an optional short message from the Buddy, and
icon is a picture that represents the Buddy. All of this is quite
conventional. These elements appear in most chat/IM application
buddy-lists in one form or another. If a "short message"
isn't provided we change the layout just slightly:
[status] screen name [icon]
The BuddyListCellRenderer must also provide a Buddy-specific tooltip
that's displayed if the user lingers over one BuddyList element.
JList renders list elements or "cells" by delegating to an
implementation of ListCellRenderer. ListCellRenderers have only one
method, getListCellRendererComponent(), which returns a Component that
the JList uses to paint a single list element. The JList really just
uses the cell renderer component's paint() method to draw or "rubber
stamp" a list element. The getListCellRendererComponent() method is
passed the JList model's "value" for each list element, and its
responsibility is to return a component that's been configured to
display that value.
The default ListCellRenderer is quite simple. It just uses the same
JLabel for every list element, roughly like this:
JLabel label = new JLabel();
Component getListCellRendererComponent(JList l, Object value, ...) {
jLabel.setText(value.toString());
return jLabel;
}
To display the properties of a Buddy in the way we've layed out above
will require more than just a JLabel. BuddyCellRenderer uses a JPanel
with subcomponents for the various properties and GridBagLayout to
define the layout.
A generic ListCellRenderer that configures our JPanel composite to
display a Buddy value is difficult because we don't want to
dictate the type of the Buddy object but we do need to
extract its status, screen name, icon, and message. What's needed
is an adapter that extracts the properties needed by the
BuddyCellRenderer from the app-specific Buddy object. The
BuddyListCellRenderer.Adapter class does this. The way it works
is easiest to explain with an example. Lets assume that our chat/IM
application has a Buddy class that looks like this:
class MyBuddy {
boolean isOnline() { ... }
boolean isAway() { ... }
String getScreenName() { ... }
ImageIcon getIcon() { ... }
}
A JList ListModel that encapsulated the list of MyBuddy objects would
have to be created; I will not delve into that here. The adapter for
MyBuddy objects could be defined and used like this:
class MyBuddyAdapter extends BuddyCellRenderer.Adapter {
private MyBuddy getBuddy() { return (MyBuddy)getValue(); }
public String getName() { return getBuddy().getScreenName(); }
public String getMessage() { return getBuddy().getMessage(); }
public ImageIcon getBuddyIcon() { return getBuddy().getIcon(); }
public Status getStatus() {
if (getBuddy().isAway()) {
return Status.AWAY;
}
else if (getBuddy().isOnline()) {
return Status.ONLINE;
}
else {
return Status.OFFLINE;
}
}
}
BuddyCellRenderer cellRenderer = new BuddyCellRenderer();
cellRenderer.setAdapter(new MyBuddyAdapter());
myBuddyJList.setCellRenderer(cellRenderer);
That's pretty much all there is to it. The BuddyCellRenderer
scales (and caches) the Icons provided by the Adapter if they're
bigger than BuddyCellRenderer.getBuddyIconSize().
It also caches the "grayed out" version of the icon that's used
when a Buddy's status is offline. Alternating rows are
rendered in an off-white color to help with readability and
the whole thing is layed out internally with the old Swing
layout veteran: GridBagLayout.
If you'd like to try making some changes to BuddyCellRenderer
and the demo, you can download a NetBeans
project with the source code and the jar files here:
Download BuddyList NetBeans Project .
- Login or register to post comments
- Printer-friendly version
- hansmuller's blog
- 3895 reads
Using Java Web Start to Launch NetBeans
About six months ago I had a dream. Not the sort of dream that makes
you wake up shrieking or smiling, and not the kind that brings you
down from the mountain top or even gets you off the couch. Mine was
the kind of dream programmers have. The kind of sloth inspired idea
that comes to you while staring at the screen, wondering if there's
a way to eliminate all of the mouse clicking and key pressing effort
that makes you weary without actually burning calories.
I spend quite a bit of time looking at Java blogs and articles that
incorporate lots of source code. Usually there's a link for zip file
that contains all of the files the document refers to, and maybe a jar
file with a build. Sometimes articles include direct links to source files,
however scanning a pile of source code with the web browser isn't
terribly appealing. The nicest way to look at code and try out APIs,
is to just load everything into a Java IDE like NetBeans. Once that's
done it's possible to use the editor and debugger and all of the other
IDE features to explore the code. Like a real programmer should. And
just like a real programmer, I'm usually too lazy to bother.
So the big idea was to write a web started app that would download a
complete NetBeans project, launch NetBeans, and open the key source
files in the editor. That way, if someone was reading a blog or an
article about some Java project, they could click on a JNLP link - and
with no additional effort! - peruse the code from within the IDE. To
me, this seems like a civilized way to do business. Sadly, I wasn't
able to con one of my colleagues into building such a web started app.
Towards the end of last year, as Sun began to slow down in
anticipation of the Christmas break, I took a crack at building a
NetBeans launcher. You can try it now, by clicking on the handy
launch button below. It's a signed application, because it creates a
temp file and launches a (NetBeans) process on your machine, so you'll
have to click through a security dialog.
To give the example launcher a try, just click the Launch button:
src="http://weblogs.java.net/blog/hansmuller/archive/jws-launch-button.png"
width="88"
height="23"/>
The launch app depends on the NetBeans OpenFile module to start the
NetBeans IDE, if necessary. Sadly (at the moment) there isn't
an "OpenProject" module, so the best I can do is to show a single
Java source file. If the launch app is unable to locate an installed
copy of NetBeans, it displays a little form that allows choosing
the install directory, or downloading the current NetBeans release.
I haven't tested launch very carefully (works on my machine :-) which
is unwise, since part of the code to find and launch NetBeans is
platform specific. It should work on Windows XP and it might
work on Linux or the Mac or Solaris. I'd be happy to
hear from anyone who's tried it.
The example file downloaded by the launcher, NetBeans.java, deals
with figuring out if and where NetBeans is installed. I didn't
pick this one file out of pride, it's incomplete and contains
some moderately embarrassing hacks. On the other hand, it does
all of the important work. The class is used like this:
NetBeans nb = new NetBeans();
nb.initialize();
if (!nb.isInstalled()) {
// give the user the opportunity to choose
// the install directory or download NetBeans
}
else {
File file = new File("C:\MyProject\src\pkg\MyFile.java");
try {
nb.openFile(file);
}
catch (NetBeans.Failure e) {
// report the problem to the user
}
}
The initialize method uses some shameful heuristics to try and figure
out where NetBeans was installed. Check out the source code from
within the IDE to see what I mean. It would certainly be much nicer
to be able to look in a well known (per platform) place to find out
what versions of NetBeans were installed and where; maybe in the
future that kind of support will emerge.
You can use the launch app on you own web site, just by making a
copy of the JNLP file file
and replace the URL in argument element at the bottom.
The current version links to the example NetBeans.java file:
<argument>http://download.java.net/javadesktop/blogs/hansmuller/launch/NetBeans.java</argument>
You don't need to copy the launch jar files or anything else. Just
create a link to your version of launch.jnlp on your site, and make
sure your web server
is configured to support the JNLP MIME type.
This was originally intended to be a quick project that I'd finish on
on the long flight home from Prague back in December. It didn't turn
out that way, in part because building (usable) GUIs is always more
work than you'd think. It's also because I took the opportunity to
get introduced to
Matisse.
It's been a long time since I've been comfortable writing Swing GUIs
with a tool. Using Matisse turned out to be pretty inspiring: it
worked well and it greatly simplified the task of evolving a GUI.
Using it was a constant reminder of all of the additional desktop app
building support that should be in NetBeans. More about that in
another blog.
I have a long laundry list of worthy improvements for the NetBeans
launcher. I'd be interested to hear what other developers think of
it and what you-all think should be changed/improved. Here are a
few of TODO items from the top of my list:
- Download a complete project packed into a jar file and expand
that into a temp directory. Open a set of project relative source
files in NetBeans. - Log errors, and warnings and provide a way to show them.
- Show the main window roughly in the middle of the screen.
And wouldn't it be nice to know which Screen NetBeans was
already running on - if there were multiple screens. - An "OpenProject" NetBeans module. I've been assured that this
wouldn't be so difficult to write. Making sure that it was
deployed to the IDE, before the project was launched would be a
bit of a trick.
If you'd like to look at the complete NetBeans launch project, you'll
find a
zip file of the complete project here. I realize that it's more
than a little ironic to publish such a link. Hopefully I'll be able to
support loading entire projects (not just files) in round two.
- Login or register to post comments
- Printer-friendly version
- hansmuller's blog
- 6289 reads


style="border: none; "/>


Comments
by hansmuller - 2008-01-14 12:02
Posted by: diverson on January 09, 2008 at 01:37 PM Regarding the rotation example: if the rotation were static (or will be rendered many more times than it will be changed) then optimally you would want to concatenate the three transforms into one transform. Will the scene graph be able to detect this and optimize automatically similar to the way Java2D handles images for you? Or, perhaps more realistically, will there be a method we can call to perform such optimizations? Some scene graphs (Java3D, for example) have this ability to "compile" scene graphs.I agree: caching the relatively static parts of the scene graph in a form that's much closer to what's needed for rendering is definitely the path to better performance. We're working on that.
by hansmuller - 2008-01-14 11:58
Posted by: pdoubleya on January 09, 2008 at 02:16 AM Also, in the demo code, the first method, createFill(), sets anti-aliasing on but only fills a shape with a black color--what does AA mean in the context of a black fill?For the black filled arcs that appear in the "smiley" demo I didn't describe, the AA flag will cause the value of pixels that only partially overlap the curve to have an interpolated value that's relative to the color already there. You can see it if you use a screen magnifier.
About enabling AA in general: I agree that doing so per node is clunky, albeit flexible. It would be useful to be able to
enableAA(), as you say, for a subtree.by hansmuller - 2008-01-14 11:48
The problem with control-T appeared to have been that the demo never requested focus. That was easily fixed however when I tried the (supposedly) updated app, no soap. I was somewhat busy with another project at the time and asked Igor Kushnirskiy to take a look at the problem. He pointed out that I hadn't been updating the right .jar file. He might have also pointed out that I'm a knucklehead, but tactful man that he is, he let the facts speak for themselves.
So the good news is that Amy's scene graph viewer now appears on control-T, if you've clicked on the demo first.
by kirillcool - 2008-01-09 01:38
java.security.AccessControlException: access denied (java.util.PropertyPermission com.sun.scenario.animationTimeline.nogaps read) at java.security.AccessControlContext.checkPermission(Unknown Source) at java.security.AccessController.checkPermission(Unknown Source) at java.lang.SecurityManager.checkPermission(Unknown Source) at java.lang.SecurityManager.checkPropertyAccess(Unknown Source) at java.lang.System.getProperty(Unknown Source) at java.lang.Boolean.getBoolean(Unknown Source) at com.sun.scenario.animation.MasterTimer.(MasterTimer.java:54) at com.sun.scenario.animation.Timeline.addFrameJob(Timeline.java:52) at com.sun.scenario.scenegraph.JSGPanelRepainter.(JSGPanelRepainter.java:53) at com.sun.scenario.scenegraph.JSGPanelRepainter.(JSGPanelRepainter.java:45) at com.sun.scenario.scenegraph.JSGPanel.markDirty(JSGPanel.java:525) at com.sun.scenario.scenegraph.JSGPanel.setScene(JSGPanel.java:102) at demo.intro.Intro$ScalingSGPanel.setScene(Intro.java:366) at demo.intro.Intro.createMainPanel(Intro.java:336) at demo.intro.Intro.startup(Intro.java:347)by hansmuller - 2008-01-09 10:40
OK: control-T doesn't work for me either! It does work when I launch the application locally, but not with the web started version. Will figure what's wrong, sorry about that.
by tball - 2008-01-09 10:10
If you check the MasterTimer.java source (Joe-Bob says check it out!), you'll find that the exception is a harmless diagnostic. I've asked that it be removed, since everyone (including myself) assume it is a fatal error.by quintesse - 2008-01-09 01:39
Control-T doesn't seem to do anything? (at least on my system: Windows XP, Java 6 (1.6.0_03))by twilightworkshop - 2008-01-09 08:37
CTRL T does not work on OSX either. And I have the same exceptionby pdoubleya - 2008-01-09 03:16
Hi Hans--nice overview, thanks.
I have the same problems with the demo--Ctrl T doesn't work for me. But the panels display correctly.
Also, in the demo code, the first method, createFill(), sets anti-aliasing on but only fills a shape with a black color--what does AA mean in the context of a black fill?
The AA method call makes me think there's room for (maybe the community to offer) some cover methods like "enableAA()" as an alternative to "setAntialiasingHint(RenderingHints.VALUE_TEXT_ANTIALIAS_ON)". The number of method calls involved makes me think an "extension" API designed with fluent interfaces might make this more fun to program with--JMock has been a real inspiration for me as regards fluent interfaces. Those are minor, and something a community member could take on to simplify use of the API.
Looking forward to looking into the framework further, keep up the good writing.
Thanks, Patrick
by mattnathan - 2008-01-09 08:24
I get the same exception under linux Java Web Start 1.6.0_03
Using JRE version 1.6.0_03 Java HotSpot(TM) Server VM and ctrl+t doesn't work for me either.
Also the spacing between the characters in rotated text seems to be affected by rounding errors of some kind, this is most noticeable under the mouse tab where the characters seem to 'wobble' when rotated by small values. Other than that I'm looking forward to what comes out of this project.
by swv - 2008-04-10 12:05
Datapoint- Ctrl-T worked fine. mouse didn't respond to mouse at all. XP64 / 6N / run as webstartby paksegu - 2008-01-09 21:10
Nice, Definately Nice...Sun is definately stepping is its game with this JavaFx thing...No frills just JAVAby diverson - 2008-01-09 14:37
Thanks for the intro, Hans.Regarding the rotation example: if the rotation were static (or will be rendered many more times than it will be changed) then optimally you would want to concatenate the three transforms into one transform. Will the scene graph be able to detect this and optimize automatically similar to the way Java2D handles images for you? Or, perhaps more realistically, will there be a method we can call to perform such optimizations? Some scene graphs (Java3D, for example) have this ability to "compile" scene graphs.
by mikeazzi - 2008-01-09 12:26
When I resize the frame I notice some lines flashing around the edges, giving it a not so smooth resizing feel. Otherwise it looks great. I am using Windows XP with Update N b08.by hansmuller - 2008-01-09 11:18
I've removed the alarming stack trace print that occurs when MasterTimer can't read it's System properties. It was harmless and unneccessary. I wish we had a better way to test sandboxed applications.
by throwable - 2008-01-29 10:02
It is rather difficult to build and manipulate with scene graph using AffineTransform nodes. I think it may be simplified for example by integrating of SGNode with SGTransform allowing user to use simple bean methods like setX(), setY(), rotate(), scale(), etc... The main reason is that every "visual" node must be positioned inside scene and thus will always have corresponding translation node.