Summary[]
This code is based on the SweetBox code, and adds centered text, rollover, and press effects.
Code[]
import java.lang.*;
import javafx.ui.*;
import javafx.ui.canvas.*;
import javafx.ui.filter.*;
function lighter(c: Color, k: Number) =
Color
{
red: (1 - (1 - c.red) * k)
green: (1 - (1 - c.green) * k)
blue: (1 - (1 - c.blue) * k)
opacity: c.opacity
};
function darker(c: Color, k: Number) =
Color
{
red: c.red * k
green: c.green * k
blue: c.blue * k
opacity: c.opacity
};
public class SizeableText
extends Text
{
operation getBounds();
}
/**
Allows access to a protected variable in the parent class.
*/
operation SizeableText.getBounds()
{
return this.bounds;
}
class SweetButton
extends CompositeNode
{
public attribute text: String;
public attribute fontFace: Font;
public attribute fontColor: Color;
public attribute color: Color;
private attribute darkerColor: Color;
private attribute lighterColor: Color;
public attribute roundness: Number;
public attribute inset: Number;
public attribute x: Number;
public attribute y: Number;
public attribute w: Number;
public attribute h: Number;
private attribute _text: SizeableText;
public attribute haloColor: Color;
private attribute haloOffset: Number;
private attribute haloOpacity: Number;
private attribute storedHaloOpacity: Number;
private attribute maxHaloOpacity: Number;
public attribute onClickHaloColor: Color;
private attribute onClickHaloOpacity: Number;
public operation frame(x:Number, y: Number, w:Number, h:Number);
public operation centerText();
public operation getTextCenterY() : Number;
public operation getTextCenterX() : Number;
public operation onMouseEntered(e:CanvasMouseEvent);
public operation onMouseExited(e:CanvasMouseEvent);
public operation onMousePressed(e:CanvasMouseEvent);
public operation onMouseReleased(e:CanvasMouseEvent);
public operation onMouseClicked(e:CanvasMouseEvent);
private operation doMouseEntered(e:CanvasMouseEvent);
private operation doMouseExited(e:CanvasMouseEvent);
private operation doMousePressed(e:CanvasMouseEvent);
private operation doMouseReleased(e:CanvasMouseEvent);
private operation doMouseClicked(e:CanvasMouseEvent);
}
attribute SweetButton.text = "";
attribute SweetButton.inset = 1;
attribute SweetButton.roundness = 1;
attribute SweetButton.w = 200;
attribute SweetButton.h = 40;
attribute SweetButton.fontFace = Font { face: VERDANA, size: 15 };
attribute SweetButton.fontColor = white;
attribute SweetButton.color = black;
attribute SweetButton.darkerColor = darker( color, 0.5 );
attribute SweetButton.lighterColor = lighter( color, 0.8 );
attribute SweetButton.haloColor = new Color( 1, 1, 1, 0.25);
attribute SweetButton.haloOffset = 2;
attribute SweetButton.haloOpacity = 0;
attribute SweetButton.storedHaloOpacity = 0;
attribute SweetButton.onClickHaloColor = new Color( 1, 1, 1, 0.25);
attribute SweetButton.onClickHaloOpacity = 0;
attribute SweetButton._text = SizeableText
{
x: getTextCenterX()
y: getTextCenterY()
content: bind text
font: bind fontFace
fill: bind fontColor
};
trigger on SweetButton.color = newValue
{
darkerColor = darker( color, 0.5 );
lighterColor = lighter( color, 0.8 );
}
trigger on SweetButton.text = newValue
{
centerText();
}
trigger on SweetButton._text = newValue
{
centerText();
}
trigger on SweetButton.fontFace = newValue
{
centerText();
}
trigger on SweetButton.w = newValue
{
centerText();
}
trigger on SweetButton.h = newValue
{
centerText();
}
operation SweetButton.frame(x:Number, y: Number, w:Number, h:Number)
{
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
operation SweetButton.centerText()
{
_text.y = getTextCenterY();
_text.x = getTextCenterX();
}
operation SweetButton.getTextCenterY() : Number
{
var boxHeight = this.h;
var textY = ( ( boxHeight - _text.getBounds().height ) / 2 );
textY += this.y;
return textY;
}
operation SweetButton.getTextCenterX() : Number
{
var boxWidth = this.w;
var textX = ( ( boxWidth - _text.getBounds().width ) / 2 );
textX += this.x;
return textX;
}
operation SweetButton.doMouseEntered(e:CanvasMouseEvent)
{
if( onClickHaloOpacity == 0 )
{
haloOpacity = 1;
}
else
{
storedHaloOpacity = 1;
}
//forward this to any subclasses
onMouseEntered(e);
}
operation SweetButton.doMouseExited(e:CanvasMouseEvent)
{
haloOpacity = 0;
storedHaloOpacity = 0;
//forward this to any subclasses
onMouseExited(e);
}
operation SweetButton.doMousePressed(e:CanvasMouseEvent)
{
storedHaloOpacity = haloOpacity;
haloOpacity = 0;
onClickHaloOpacity = 1;
//forward this to any subclasses
onMousePressed(e);
}
operation SweetButton.doMouseReleased(e:CanvasMouseEvent)
{
onClickHaloOpacity = 0;
haloOpacity = storedHaloOpacity;
//forward this to any subclasses
onMouseReleased(e);
}
operation SweetButton.doMouseClicked(e:CanvasMouseEvent)
{
//forward this to any subclasses
onMouseClicked(e);
}
function SweetButton.composeNode() =
Group
{
content:
[
Group
{
filter: [GaussianBlur {radius: 2}]
content:
[
Rect
{
x: bind x
y: bind y
width: bind w
height: bind h
arcHeight: bind roundness * h
arcWidth: bind roundness * h
fill: bind LinearGradient
{
x1: 0, y1: 0, x2: 0, y2: 1
stops:
[
Stop { offset: 0.0 color: bind darkerColor },
Stop { offset: 1.0 color: bind color }
]
spreadMethod: PAD
}
onMouseEntered: operation(e:CanvasMouseEvent)
{
doMouseEntered(e);
}
onMouseExited: operation(e:CanvasMouseEvent)
{
doMouseExited(e);
}
onMousePressed: operation(e:CanvasMouseEvent)
{
doMousePressed(e);
}
onMouseReleased: operation(e:CanvasMouseEvent)
{
doMouseReleased(e);
}
onMouseClicked: operation(e:CanvasMouseEvent)
{
doMouseClicked(e);
}
},
Clip
{
shape: Rect
{
x: bind x+inset
y: bind y+inset
width: bind w-2*inset
height: bind (h-2*inset) /2
arcHeight: bind roundness * h /3
arcWidth: bind roundness * h /3
}
content: Rect
{
x: bind x+inset
y: bind y+inset
width: bind w-2*inset
height: bind h-2*inset
arcHeight: bind roundness * h
arcWidth: bind roundness * h
opacity: 0.8
fill: bind LinearGradient
{
x1: 0, y1: 0, x2: 0, y2: 0.5
stops:
[
Stop { offset: 0.0 color: white },
Stop { offset: 1.0 color: bind lighterColor }
]
spreadMethod: PAD
}
}
},
Rect
{
x: bind x + haloOffset
y: bind y + haloOffset
width: bind w
height: bind h
arcHeight: bind roundness * h
arcWidth: bind roundness * h
fill: bind haloColor
opacity: bind haloOpacity
},
Rect
{
x: bind x + haloOffset
y: bind y + haloOffset
width: bind w
height: bind h
arcHeight: bind roundness * h
arcWidth: bind roundness * h
stroke: bind onClickHaloColor
strokeWidth: 2
opacity: bind onClickHaloOpacity
},
]
},
_text
]
};
/*
This is an anoying hack. I can't figure out how to get the mouse events
I need to know that this button has been hovered over,pressed, etc
without eating the events that let me know it's been clicked. For
some reason, this subclass works fine.
*/
class ClickedButton
extends SweetButton
{
}
attribute ClickedButton.text = "I'm a button!";
attribute ClickedButton.x = 25;
attribute ClickedButton.y = 25;
attribute ClickedButton.fontFace = Font { face: VERDANA, size: 24 };
operation ClickedButton.onMouseClicked(e:CanvasMouseEvent)
{
System.out.println("Click!");
}
var sweetButton = ClickedButton;
var frame = Frame
{
title: "Sweet Button"
height: 500
width: 500
content: Canvas
{
background: new Color( 0.3, 0.3, 0.3, 1 )
content: sweetButton
}
centerOnScreen: true
visible: true
};
/*
Anoying hack #2: text can only be centered after the element is visible; adding
this to the on new trigger, etc, does nothing.
*/
sweetButton.centerText();
Note that this code is not designed to run in the JvaFX Tutorial window, but as a standalone app.
The event model is somewhat flawed, necessitating a subclass to detect mouse clicks.
The painting code also needs work; as it stands, text can only be entered after the component has been made visible.