import javafx.ui.*; import javafx.ui.canvas.*; import java.util.Date; public class Timer { private attribute elapsed: Number; public attribute minutes: Number; public attribute seconds: Number; public attribute hours: Number; public attribute running: Boolean; } attribute Timer.elapsed = bind if running then [1..60] dur 60000 linear while running continue if true else 0; trigger on Timer.elapsed = value { var now = new Date(); minutes = now.getMinutes(); seconds = now.getSeconds() + [.35,0.34 .. 0.0] dur 350 + (now.getTime() % 1000)/1000; hours = now.getHours(); } public class Clock extends CompositeNode { public attribute ticking: Boolean; } operation Clock.composeNode() { var t = Timer {running: bind ticking}; return Group { transform: [translate(5,5),scale(1,1)] var secs = bind t.seconds var mins = bind t.minutes + secs/60 var hrs = bind t.hours + mins/60 content: [ ImageView { transform: [] image: Image {url: "http://sellmic.com/lab/dev/jfx/clock/images/clock_face.png"} }, Group { var hourHand = ImageView { transform: bind rotate(hrs*30,255,245) image: Image {url: "http://sellmic.com/lab/dev/jfx/clock/images/hour_hand.png"} } var minuteHand = ImageView { transform: bind rotate(mins *6,255,245) image: Image {url: "http://sellmic.com/lab/dev/jfx/clock/images/minute_hand.png"} } var secondHand = ImageView { transform: bind rotate(t.seconds * 6,255,245) image: Image {url: "http://sellmic.com/lab/dev/jfx/clock/images/second_hand.png"} } content: [hourHand, minuteHand, secondHand] }, ImageView { transform: [] image: Image {url: "http://sellmic.com/lab/dev/jfx/clock/images/pin.png"} }] }; } Clock {ticking: true}