Skip to content

MattHicks.com

Programming on the Edge

Menu
Menu

JavaFX Wrapper: jSeamless 2.0

Posted on March 7, 2009 by mhicks

I made a post recently (I Hate JavaFX; I Love JavaFX) in which I did a preliminary investigation on the idea of using the JavaFX API directly in Java rather than relying on JavaFX Script to do the job. I used the Clock example I saw on a tutorial on Sun’s site as a practical example of something already done in JavaFX that showed several of the features of JavaFX. The resulting code was rather hideous as the API was not designed to be used directly, but it did work.

My next step was to begin writing a wrapper around this ugly Java API to give an elegant API structure for developing Java applications that utilize the awesome functionality of JavaFX for those people that don’t care to use the scripting language. After a few days of work I’m close to having an alpha build ready to release and decided to redo the Clock example using this wrapper to see how easily it could be done.

I give you the resulting code:


package org.jseamless.example;

import java.util.Calendar;

import org.jseamless.Container;
import org.jseamless.View;
import org.jseamless.core.HorizontalAlignment;
import org.jseamless.core.VerticalAlignment;
import org.jseamless.effect.DropShadow;
import org.jseamless.paint.Color;
import org.jseamless.resource.URLResource;
import org.jseamless.shape.Ellipse;
import org.jseamless.shape.Line;
import org.jseamless.shape.path.ArcTo;
import org.jseamless.shape.path.LineTo;
import org.jseamless.shape.path.MoveTo;
import org.jseamless.shape.path.Path;
import org.jseamless.ui.Image;
import org.jseamless.ui.Text;
import org.xjava.bean.properties.Binding;
import org.xjava.bean.properties.Property;
import org.xjava.delegates.MethodDelegate;
import org.xjava.operation.Operation;
import org.xjava.operation.OperationParser;
import org.xjava.work.DelegateWorkUnit;
import org.xjava.work.ThreadManager;

public class Clock extends Container {
private float radius;
private float centerX;
private float centerY;

private Calendar calendar;
private Property hours;
private Property minutes;
private Property seconds;

public Clock() {
// Initial setup
calendar = Calendar.getInstance();
radius = 77.0f;
centerX = 144.0f;
centerY = 144.0f;

hours = new Property(null, 0.0f);
minutes = new Property(null, 0.0f);
seconds = new Property(null, 0.0f);

nextTick();

size.fill();

Image image = new Image(); {
image.source.set(new URLResource(getClass().getClassLoader().getResource("resource/clock_background.png")));

children.add(image);
}

Container face = new Container(); {
face.translate.x.set(centerX);
face.translate.y.set(centerY);

// Every third hour
for (int i = 3; i <= 12; i += 3) {
Text t = new Text(); {
t.translate.x.set(-5.0f);
t.translate.y.set(5.0f);
t.fontSize.set(16);
t.text.set(String.valueOf(i));

float x = radius * ((i + 0) % 2 * (2 - i / 3));
float y = radius * ((i + 1) % 2 * (3 - i / 3));
t.location.set(x, y);

face.children.add(t);
}
}

// Black circle for the rest of the hours
for (int i = 1; i < 12; i++) {
if (i % 3 == 0) {
continue; // Don't show a circle on the items that have text
}

Ellipse ellipse = new Ellipse(); {
ellipse.rotation.set(30.0f * i);
ellipse.location.x.set(radius);
ellipse.location.x.align.set(HorizontalAlignment.CENTER);
ellipse.location.y.align.set(VerticalAlignment.MIDDLE);
ellipse.size.set(6.0f, 6.0f);
ellipse.fill.set(Color.BLACK);

face.children.add(ellipse);
}
}

// Center circles
Ellipse ellipse = new Ellipse(); {
ellipse.location.x.align.set(HorizontalAlignment.CENTER);
ellipse.location.y.align.set(VerticalAlignment.MIDDLE);
ellipse.size.set(10.0f, 10.0f);
ellipse.fill.set(Color.DARK_RED);

face.children.add(ellipse);
}
ellipse = new Ellipse(); {
ellipse.location.x.align.set(HorizontalAlignment.CENTER);
ellipse.location.y.align.set(VerticalAlignment.MIDDLE);
ellipse.size.set(6.0f, 6.0f);
ellipse.fill.set(Color.RED);

face.children.add(ellipse);
}

// Second hand
Line line = new Line(); {
DropShadow ds = new DropShadow(); {
ds.offsetY.set(3.0f);

line.effect.set(ds);
}
Binding b = line.rotation.bind(seconds); {
OperationParser p = new OperationParser("(seconds * 6.0) + 90.0"); {
p.setVariable("seconds", seconds);
}
Operation operation = p.generateOperation();
b.setOperation(operation);
}

line.endX.set(-radius - 3.0f);
line.stroke.set(Color.RED);
line.stroke.width.set(2.0f);

face.children.add(line);
}

// Hour hand
Path path = new Path(); {
DropShadow ds = new DropShadow(); {
ds.offsetY.set(3.0f);

path.effect.set(ds);
}
Binding b = path.rotation.bind(seconds); {
OperationParser p = new OperationParser("((hours + (minutes / 60.0)) * 30.0) - 90.0"); {
p.setVariable("hours", hours);
p.setVariable("minutes", minutes);
}
Operation operation = p.generateOperation();
b.setOperation(operation);
}
path.fill.set(Color.BLACK);

MoveTo e1 = new MoveTo(); {
e1.x.set(4.0f);
e1.y.set(4.0f);

path.elements.add(e1);
}
ArcTo e2 = new ArcTo(); {
e2.x.set(4.0f);
e2.y.set(-4.0f);
e2.radiusX.set(1.0f);
e2.radiusY.set(1.0f);

path.elements.add(e2);
}
LineTo e3 = new LineTo(); {
e3.x.set(radius - 15.0f);
e3.y.set(0.0f);

path.elements.add(e3);
}

face.children.add(path);
}

// Minute hand
path = new Path(); {
DropShadow ds = new DropShadow(); {
ds.offsetY.set(3.0f);

path.effect.set(ds);
}
Binding b = path.rotation.bind(seconds); {
OperationParser p = new OperationParser("(minutes * 6.0) - 90.0"); {
p.setVariable("minutes", minutes);
}
Operation operation = p.generateOperation();
b.setOperation(operation);
}

path.fill.set(Color.BLACK);

MoveTo e1 = new MoveTo(); {
e1.x.set(4.0f);
e1.y.set(4.0f);

path.elements.add(e1);
}
ArcTo e2 = new ArcTo(); {
e2.x.set(4.0f);
e2.y.set(-4.0f);
e2.radiusX.set(1.0f);
e2.radiusY.set(1.0f);

path.elements.add(e2);
}
LineTo e3 = new LineTo(); {
e3.x.set(radius);
e3.y.set(0.0f);

path.elements.add(e3);
}

face.children.add(path);
}

children.add(face);
}
}

public void start() throws InterruptedException {
while (true) {
nextTick();

Thread.sleep(100);
}
}

public void nextTick() {
calendar.setTimeInMillis(System.currentTimeMillis());

seconds.set((float)calendar.get(Calendar.SECOND));
minutes.set((float)calendar.get(Calendar.MINUTE));
hours.set((float)calendar.get(Calendar.HOUR));
}

public static void main(String[] args) throws Exception {
Clock clock = new Clock();
View view = View.createWindow(clock);
view.title.set("jSeamless 2.0 Clock Example");
view.size.set("800px", "600px");

ThreadManager.getInstance().addWork(new DelegateWorkUnit(MethodDelegate.create(clock, "start")));
}
}

It looks strikingly similar to the uglier direct calls, but has been cleaned up a lot. This is still just the first pass, so more will come, but there are a ton of additional features and standardization features that have been thrown in as well as work-arounds for the broken key event functionality and so on.

I will be posting more in the near future as development continues on this wrapper, but I have decided this is the next logical step for jSeamless, so jSeamless 2.0 will not end up being a true UI abstraction, but rather a powerful wrapper around JavaFX and Swing functionality with some icing on the top.

3 thoughts on “JavaFX Wrapper: jSeamless 2.0”

  1. Oliver Rolle says:
    April 22, 2009 at 10:39 pm

    javaFX is for flash kiddies or bad payed web designers 😀
    Your wrapper is what i am looking for. If it gets as good as your vision projects it could provide good extensibility, quality and fast integration in a java environment while having a nice UI design. (Joke: If Swing were a girl I would chase away this girl with a baseball bat 😉

    Reply
  2. Unknown says:
    February 25, 2010 at 5:05 am

    Could jSeamless be used to replace JMF with a JavaFX audio/videoplayer with the nice native codec options it provides?

    Reply
  3. Matt Hicks says:
    February 25, 2010 at 7:35 pm

    Yes, that was the goal behind the endeavor was to leverage JMC directly instead of relying using JavaFX to accomplish the task.

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • Courio: E-Mail 2.0
  • Scribe 2.0: Fastest JVM Logger in the World!
  • Logging Performance
  • Publicity in Open-Source
  • Play Framework for Scala: An Evaluation

Recent Comments

  1. Luke Hutchison on Multithreaded Unzip?
  2. Luke Hutchison on Multithreaded Unzip?
  3. Unknown on Multithreaded Unzip?
  4. D L on Multithreaded Unzip?
  5. Matt Hicks on Multithreaded Unzip?

Archives

  • March 2019
  • February 2018
  • January 2017
  • June 2016
  • July 2015
  • February 2015
  • December 2014
  • September 2013
  • March 2013
  • February 2013
  • January 2013
  • November 2011
  • December 2010
  • October 2010
  • July 2010
  • June 2010
  • May 2010
  • October 2009
  • August 2009
  • July 2009
  • June 2009
  • May 2009
  • March 2009
  • February 2009
  • September 2008
  • July 2008
  • May 2008
  • March 2008
  • January 2008
  • December 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007

Categories

  • adobe
  • android
  • apple
  • beans
  • benchmark
  • challenge
  • chat
  • comparison
  • courio
  • eclipse
  • example
  • flex
  • framework
  • games
  • hacks
  • helios
  • html
  • hyperscala
  • ios
  • java
  • javafx
  • javascript
  • jcommon
  • jgn
  • jmc
  • jseamless
  • jug
  • kickstarter
  • learning
  • linux
  • log4j
  • logging
  • mac
  • media
  • mediacenter
  • methodology
  • mobile
  • mythtv
  • nabo
  • open-source
  • opengl
  • opinion
  • personal
  • playframework
  • pojo
  • programming
  • publicity
  • rant
  • raspberrypi
  • review
  • scala
  • scribe
  • script
  • sgine
  • social
  • sudoku
  • sun
  • swing
  • tutorial
  • Uncategorized
  • webframework
  • website
  • windows
  • xjava
© 2025 MattHicks.com | Powered by Minimalist Blog WordPress Theme