Button border-radius transition in JavaFX/TornadoFX - javafx

I am trying to make my buttons animate on hover such as those of Discord.
I can change the background color and border radius, but not able to animate it smoothly.
I could only find examples of animating shapes, and not css properties.
Here is my css code for the button.
class NavigatorButtonViewCss: Stylesheet() {
companion object {
val face by cssclass()
val buttonBackgroundColor = c("#36393F")
val buttonHoverBackgroundColor = c("#7289DA")
val textColor = c("#C8C9CB")
}
init {
indicator {
prefWidth = 10.px
}
face {
prefWidth = 50.px
prefHeight = 50.px
backgroundColor += buttonBackgroundColor
backgroundRadius = multi(box(50.percent))
label {
textFill = textColor
}
and(hover) {
// I want this to be animated
backgroundColor += buttonHoverBackgroundColor
backgroundRadius = multi(box(35.percent))
}
}
}
}
My button now
What I want
Are there any ways to achieve this transition?
Thank you.

JavaFX does not allow you to create any kind of animation via CSS and as far as I can tell there's no way of applying a animation even with TornadoFX. (Note tough that I just started with TornadoFX today, so I may be wrong.)
The only way of getting an animation in via stylesheet would be to use set the skin to a custom skin implementing the corner animation. While you could make the skin provide CSS property controling the roundedness of the corners.
Usually you'd extend Button adding a property, but in this case I simply store it in the properties map.
Skin to assign to the button
package org.example
import com.sun.javafx.scene.control.skin.ButtonSkin
import javafx.beans.binding.Bindings
import javafx.beans.binding.DoubleBinding
import javafx.beans.property.DoubleProperty
import javafx.beans.property.SimpleDoubleProperty
import java.util.function.Function
import javafx.css.SimpleStyleableBooleanProperty
import javafx.css.CssMetaData
import javafx.css.StyleablePropertyFactory
import javafx.css.Styleable
import javafx.css.StyleableProperty
import javafx.scene.control.Button
import javafx.scene.shape.ArcTo
import javafx.scene.shape.ClosePath
import javafx.scene.shape.HLineTo
import javafx.scene.shape.MoveTo
import javafx.scene.shape.Path
import javafx.scene.shape.VLineTo
import tornadofx.*
class AnimatedButtonSkin(button: Button) : ButtonSkin(button) {
companion object {
#JvmField
val CSS_ROUNDED_KEY = "org.example.AnimatedButtonSkin.rounded"
#JvmField
val CSS_ROUNDED_METADATA: CssMetaData<Button, Boolean>
#JvmField
val FACTORY = StyleablePropertyFactory<Button>(javafx.scene.control.SkinBase.getClassCssMetaData())
init {
CSS_ROUNDED_METADATA = FACTORY.createBooleanCssMetaData(
"-fx-rounded",
object : Function<Button, StyleableProperty<kotlin.Boolean>> {
override fun apply(b: Button): StyleableProperty<Boolean> {
// property stored in properties to avoid extending button
val v = b.getProperties().get(CSS_ROUNDED_KEY)
return v as StyleableProperty<Boolean>
}
},
true
)
}
}
override fun dispose() {
// get rid of the property and the shape
val b = getSkinnable()
b.getProperties().remove(CSS_ROUNDED_KEY)
b.setShape(null)
super.dispose()
}
private fun createArc(
cornerSizeH: DoubleBinding,
cornerSizeV: DoubleBinding,
invertX: Boolean,
invertY: Boolean
): ArcTo {
return ArcTo().apply {
setAbsolute(false)
setSweepFlag(true)
radiusXProperty().bind(cornerSizeH)
radiusYProperty().bind(cornerSizeV)
xProperty().bind(if (invertX) cornerSizeH.negate() else cornerSizeH)
yProperty().bind(if (invertY) cornerSizeV.negate() else cornerSizeV)
}
}
override fun getCssMetaData(): List<CssMetaData<out Styleable, *>>? {
return FACTORY.getCssMetaData()
}
init {
val prop = SimpleStyleableBooleanProperty(CSS_ROUNDED_METADATA, true)
button.getProperties().put(CSS_ROUNDED_KEY, prop)
// relative part of width/height that is rounded
// size for single corner:
// 0 -> rectangular button
// 0.5 -> circular button
val cornerSize = SimpleDoubleProperty(.5)
val w = button.widthProperty()
val h = button.heightProperty()
// bindings for horizontal measures
val cornerHSize = w.multiply(cornerSize)
val doubleHCornerSize = cornerHSize.multiply(2.0);
// measures for vertical measures
val cornerVSize = h.multiply(cornerSize)
val doubleVCornerSize = cornerVSize.multiply(2.0);
// lower part of the top-left corner
val start = MoveTo().apply {
yProperty().bind(cornerSize);
}
// straight path of top
val top = HLineTo().apply {
setAbsolute(false)
xProperty().bind(w.subtract(doubleHCornerSize))
}
// straight part of the right
var right = VLineTo().apply {
setAbsolute(false)
yProperty().bind(h.subtract(doubleVCornerSize))
}
// straight part of the bottom
val bottom = HLineTo().apply {
setAbsolute(false)
xProperty().bind(top.xProperty().negate())
}
// assemble the parts
val shape = Path(
start,
createArc(cornerHSize, cornerVSize, false, true), top,
createArc(cornerHSize, cornerVSize, false, false), right,
createArc(cornerHSize, cornerVSize, true, false), bottom,
createArc(cornerHSize, cornerVSize, true, true), ClosePath()
)
button.shape = shape
// animate open/close on change of stylable property
prop.addListener({ _, _, new -> cornerSize.animate(endValue = if (new) .5 else .2, duration = .2.seconds) })
}
}
Style
class NavigatorButtonViewCss: Stylesheet() {
companion object {
val face by cssclass()
val rounded by cssproperty<Boolean>("-fx-rounded")
val buttonBackgroundColor = c("#36393F")
val buttonHoverBackgroundColor = c("#7289DA")
val textColor = c("#C8C9CB")
}
init {
indicator {
prefWidth = 10.px
}
face {
prefWidth = 50.px
prefHeight = 50.px
backgroundColor += buttonBackgroundColor
textFill = textColor
skin = AnimatedButtonSkin::class
and(hover) {
rounded.value = false // update corners
backgroundColor += buttonHoverBackgroundColor
}
}
}
}

Related

How to bind Node style (or styleClass) to a property?

Consider the following example:
class MainView : View("Example") {
val someBooleanProperty: SimpleBooleanProperty = SimpleBooleanProperty(true)
override val root = borderpane {
paddingAll = 20.0
center = button("Change bg color") {
action {
// let's assume that new someBooleanProperty value is updated
// from some API after button clicked
// so changing style of the borderpane in action block
// of the button is not the solution
someBooleanProperty.value = !someBooleanProperty.value
}
}
}
}
class Styles : Stylesheet() {
companion object {
val red by cssclass()
val green by cssclass()
}
init {
red { backgroundColor += Color.RED }
green { backgroundColor += Color.GREEN }
}
}
How can I dynamically change the background color of borderpane depending on someBooleanProperty (e.g RED when true and GREEN when false)? Is there possibility to bind CSS class to a property? Is there any solution to do that without using CSS (meaning inside style block etc)
If you want to toggle a class (add or remove a class based on a boolean property), you can use the Node.toggleClass(CssRule, ObservableValue<Boolean>) function.
val someBooleanProperty = SimpleBooleanProperty(true)
...
borderpane {
toggleClass(Styles.red, someBooleanProperty)
toggleClass(Styles.green, someBooleanProperty.not())
}
If, on the other hand, you want to bind to a changing class value, you can use the Node.bindClass(ObservableValue<CssRule>) function.
val someClassyProperty = SimpleObjectProperty(Styles.red)
...
borderpane {
bindClass(someClassyProperty)
}
You can then set the class to whatever you want.

What the easiest way to animate a Path as an object traverses it?

Consider a rectangle traversing a long, linear path. It would be useful to figure out where the shape had gone earlier in the animation. Displaying the entire path before the shape moves is not what I want. That is easily done by adding the path to the pane.
I want a trailing line behind the shape representing the path that the shape has traversed through so far. Does anyone know how to do this in Javafx? I am using Path and PathTransition to animate my object along a path.
There are various solutions. Depending on which one you choose decides your outcome.
You could use a Canvas and paint lines on it while a Node moves along the Path.
import javafx.animation.Animation;
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import javafx.util.Duration;
public class PathVisualization extends Application {
private static double SCENE_WIDTH = 400;
private static double SCENE_HEIGHT = 260;
Canvas canvas;
#Override
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
Path path = createPath();
canvas = new Canvas(SCENE_WIDTH,SCENE_HEIGHT);
root.getChildren().addAll(path, canvas);
primaryStage.setScene(new Scene(root, SCENE_WIDTH, SCENE_HEIGHT));
primaryStage.show();
Animation animation = createPathAnimation(path, Duration.seconds(5));
animation.play();
}
private Path createPath() {
Path path = new Path();
path.setStroke(Color.RED);
path.setStrokeWidth(10);
path.getElements().addAll(new MoveTo(20, 20), new CubicCurveTo(380, 0, 380, 120, 200, 120), new CubicCurveTo(0, 120, 0, 240, 380, 240), new LineTo(20,20));
return path;
}
private Animation createPathAnimation(Path path, Duration duration) {
GraphicsContext gc = canvas.getGraphicsContext2D();
// move a node along a path. we want its position
Circle pen = new Circle(0, 0, 4);
// create path transition
PathTransition pathTransition = new PathTransition( duration, path, pen);
pathTransition.currentTimeProperty().addListener( new ChangeListener<Duration>() {
Location oldLocation = null;
/**
* Draw a line from the old location to the new location
*/
#Override
public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
// skip starting at 0/0
if( oldValue == Duration.ZERO)
return;
// get current location
double x = pen.getTranslateX();
double y = pen.getTranslateY();
// initialize the location
if( oldLocation == null) {
oldLocation = new Location();
oldLocation.x = x;
oldLocation.y = y;
return;
}
// draw line
gc.setStroke(Color.BLUE);
gc.setFill(Color.YELLOW);
gc.setLineWidth(4);
gc.strokeLine(oldLocation.x, oldLocation.y, x, y);
// update old location with current one
oldLocation.x = x;
oldLocation.y = y;
}
});
return pathTransition;
}
public static class Location {
double x;
double y;
}
public static void main(String[] args) {
launch(args);
}
}
Here's a screenshot how it looks like. Red is the actual path, Blue is the path that is drawn on the Canvas:
Other solutions use e. g. a clip. However, if you choose the same Duration as I did above (i. e. 5 seconds) with that technique, you'll get gaps like this:
The solution with the line drawing has its drawbacks as well. If you choose 1 second, you'll see the line segments. A possibiliy to circumvent this would be to smooth the path yourself. But for that you'd have to get into splitting the path into segments and that's a bit math-y.
Slightly offtopic, but how to paint along the mouse coordinates may also be interesing for you to give you ideas.
Michael Bostock does a path animation by manipulating the stroke dash array and interpolating the stroke dash offset. He provides an example (of course) which you can view here.
The same approach can be taken in JavaFX. Here is a DrawPathTransition
(Kotlin) class I created which uses this technique:
class DrawPathTransition(val path: Path) : Transition() {
private val DEFAULT_DURATION = Duration.millis(400.0)
private val length = path.totalLength
var duration: Duration
get() = durationProperty.get()
set(value) {
durationProperty.set(value)
}
val durationProperty = SimpleObjectProperty(DEFAULT_DURATION)
init {
durationProperty.addListener({ _ -> cycleDuration = duration })
statusProperty().addListener({ _, _, status ->
when(status) {
Status.RUNNING -> path.strokeDashArray.addAll(length, length)
Status.STOPPED -> path.strokeDashArray.clear()
}
})
}
override fun interpolate(frac: Double) {
path.strokeDashOffset = length - length * frac
}
}
The tricky part here is getting the path's total length. See my answer to this question for how that can be accomplished.
You can then combine a PathTransition with the above DrawPathTransition of the same duration in a ParallelTransition to get what you desire.
Since this approach modifies strokeDashArray and strokeDashOffset it only works with solid lines, but what if we want to support dashed lines as well? Nadieh Bremer has an excellent article about this which can be reviewed here.
The DrawPathTransition (Kotlin) class provided below implements this technique. Note that this can create a rather large strokeDashArray during the transition.
class DrawPathTransition(val path: Path) : Transition() {
private val length = path.totalLength
private val stroked = path.strokeDashArray.isNotEmpty()
private val dashArray: List<Double> = if(stroked) ArrayList(path.strokeDashArray) else emptyList()
private val dashLength = dashArray.sum()
private val dashOffset = path.strokeDashOffset
var duration: Duration
get() = durationProperty.get()
set(value) {
durationProperty.set(value)
}
val durationProperty = SimpleObjectProperty(DEFAULT_DURATION)
init {
durationProperty.addListener({ _ -> cycleDuration = duration })
if(stroked) {
val n = (length / dashLength).toInt()
path.strokeDashArray.clear()
(1..n).forEach { path.strokeDashArray.addAll(dashArray) }
path.strokeDashArray.addAll(0.0, length)
statusProperty().addListener({ _, _, status ->
if(status == Animation.Status.STOPPED) {
path.strokeDashOffset = dashOffset
path.strokeDashArray.setAll(dashArray)
}
})
}
}
override fun interpolate(frac: Double) {
path.strokeDashOffset = length - length * frac
}
}
I wasn't completely happy with this approach though, as the stroke appears to "march" along the path as the path is drawn, which doesn't look great particularly with short durations. Rather I wanted it to appear as if the stroke was being "revealed" over time (so no stroke movement). The DrawPathTransition (Kotlin) class below implements my solution:
class DrawPathTransition(val path: Path) : Transition() {
private val length = path.totalLength
private val stroked = path.strokeDashArray.isNotEmpty()
private val dashArray: List<Double> = if(stroked) ArrayList(path.strokeDashArray) else emptyList()
private val dashSum = dashArray.sum()
private val dashOffset = path.strokeDashOffset
var duration: Duration
get() = durationProperty.get()
set(value) {
durationProperty.set(value)
}
val durationProperty = SimpleObjectProperty(DEFAULT_DURATION)
init {
durationProperty.addListener({ _ -> cycleDuration = duration })
if(stroked) {
statusProperty().addListener({ _, _, status ->
if(status == Animation.Status.STOPPED) {
path.strokeDashOffset = dashOffset
path.strokeDashArray.setAll(dashArray)
}
})
}
}
override fun interpolate(frac: Double) {
val l = length * frac
if(stroked) {
path.strokeDashOffset = l
val n = ceil(l / dashSum).toInt()
path.strokeDashArray.clear()
path.strokeDashArray.addAll(0.0, l)
(1..n).forEach { path.strokeDashArray.addAll(dashArray) }
path.strokeDashArray.addAll(0.0, length - l)
}
else path.strokeDashOffset = length - l
}
}

Changing the scrolling amount of a spark DataGrid when using the mouse wheel

Basically what the title says. I checked DataGridSkin, and it has a Scroller skin part, but I can't find any attribute for the scrolling amount. I might have missed it.
Can anybody please help me on this issue? Thanks.
I suggest the following. You can find part of scrolling logic in VScrollBar class in mouseWheelHandler method.
var nSteps:uint = Math.abs(delta);
...
for (var vStep:int = 0; vStep < nSteps; vStep++)
{
var vspDelta:Number = vp.getVerticalScrollPositionDelta(navigationUnit);
...
In my case delta equals 3. And grid is scrolled for 3 renderers heights. You can extend VScrollBar class. Then create custom ScrollerSkin (copy from original one) and set you vertical bar there instead of default one. Then create custom DataGrid skin (again copy from original one) and set your custom scroller skin for Scroller there.
In custom VScrollBar class override mouseWheelHandler method. Example:
package {
import flash.events.Event;
import flash.events.MouseEvent;
import mx.core.IInvalidating;
import mx.core.mx_internal;
import mx.events.FlexMouseEvent;
import spark.components.VScrollBar;
import spark.core.IViewport;
import spark.core.NavigationUnit;
import spark.utils.MouseEventUtil;
use namespace mx_internal;
public class MyVScrollBar extends VScrollBar {
public function MyVScrollBar() {
}
override mx_internal function mouseWheelHandler(event:MouseEvent):void {
const vp:IViewport = viewport;
if (event.isDefaultPrevented() || !vp || !vp.visible || !visible)
return;
var changingEvent:FlexMouseEvent = MouseEventUtil.createMouseWheelChangingEvent(event);
if (!dispatchEvent(changingEvent))
{
event.preventDefault();
return;
}
const delta:int = changingEvent.delta;
var nSteps:uint = 1; //number of renderers scrolled!
var navigationUnit:uint;
var scrollPositionChanged:Boolean;
navigationUnit = (delta < 0) ? NavigationUnit.DOWN : NavigationUnit.UP;
for (var vStep:int = 0; vStep < nSteps; vStep++)
{
var vspDelta:Number = vp.getVerticalScrollPositionDelta(navigationUnit);
if (!isNaN(vspDelta))
{
vp.verticalScrollPosition += vspDelta;
scrollPositionChanged = true;
if (vp is IInvalidating)
IInvalidating(vp).validateNow();
}
}
if (scrollPositionChanged)
dispatchEvent(new Event(Event.CHANGE));
event.preventDefault();
}
}
}
Set nSteps variable to number of renderers you want to be scrolled. So in this example grid is scrolled by only one item on wheel.
I found a solution: I used the idea described above, but instead of creating a class that extends VScrollBar, I added a MouseWheelChanging event listener to the scroll bar in the Scroller skin:
<fx:Component id="verticalScrollBarFactory">
<s:VScrollBar visible="false" mouseWheelChanging="outerDocument.vScrollBarWheelChanging(event)"/>
</fx:Component>
internal function vScrollBarWheelChanging(event:MouseEvent):void
{ event.delta/=Math.abs(event.delta); }
You can set event.delta to the desired scroll amount.

How come this image won't resize to fit the sprite?

I have this snippet of ActionScript code that is supposed to resize an image to fit the sprite: (as in rescale the image not just clip it)
package
{
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.events.Event;
public class Main extends Sprite
{
[Embed(source = 'img.png')]
private var TheImage:Class;
public static const TheImageWidth:Number = 1300;
public static const TheImageHeight:Number = 1300;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
var image:Bitmap = new TheImage();
addChild(image);
image.scaleX = width / TheImageWidth;
image.scaleY = height / TheImageHeight;
}
}
}
Why isn't it working? The image isn't getting the right dimensions.
try
image.image.content.width = TheImageWidth;
image.image.content.height = TheImageHeight;
I had to do the something like this for imagery. We had issue with using MapImage and the MapImageLayer using ArcGIS for Flex 2.5. It seems to be fix in 3.5, Anyways I had to put an image into a sprite and paint the image based on the sprites width. If you want to scale the image to the sprite's dimensions you will need to use the Matrix API and use the scale method. The values passed into this should be the sprite's width divided by the image's width assuming the image is bigger than your sprite. Do the same with the height. Here is the code I used to accomplish the task.
const point:MapPoint = geometry as MapPoint;
sprite.x = toScreenX(map, point.x);
sprite.y = toScreenY(map, point.y);
// grab left x point for upper left then for lower right
// actua
var bottomLeftX:Number = toScreenX(map, geomertyWrapper.extent.xmin);
var topRightX:Number = toScreenX(map, geomertyWrapper.extent.xmax);
var bottomLeftY:Number = toScreenY(map, geomertyWrapper.extent.ymin);
var topRightY:Number = toScreenY(map, geomertyWrapper.extent.ymax);
iconWidth = Math.abs(topRightX - bottomLeftX);
iconHeight = Math.abs(topRightY - bottomLeftY);
sprite.width = iconWidth;
sprite.height = iconHeight;
var scaleX:Number = iconWidth/bitmapData.width;
var scaleY:Number = iconHeight/bitmapData.height;
m_matrix.a = 1.0;
m_matrix.d = 1.0;
m_matrix.scale(scaleX, scaleY);
m_matrix.tx = -iconWidth / 2;
m_matrix.ty = -iconHeight / 2;
//Values needed for drawing AlertNotificationInstance
sprite.graphics.beginBitmapFill(m_bitmapData, m_matrix, false, false);
sprite.graphics.drawRect(m_matrix.tx, m_matrix.ty,
iconWidth,
iconHeight);
sprite.graphics.endFill();
Hope this helps.

Customize the alert box with two buttons

I need to customize my alert box with two button and both the button need to have different skins on them.
I was able to do it for one skin. But i am unable to figure out about the two skins.
My code that i have right now is below:
public function Messages():void
{
Alert.buttonWidth = 100;
Alert.yesLabel = "Accept";
Alert.noLabel = "Reject";
var alert:Alert = Alert.show("Accept video chat request from "+far_alias+"?", "Incoming Video Chat Request", Alert.YES | Alert.NO | Alert.NONMODAL, Sprite(Application.application), handleIncomingCallResponse, null, Alert.YES);
alert.data = cm;
alert.name = alert_name;
alert.styleName = "requestVideoAlert"
_alertDic[alert_name] = alert;
Alert.yesLabel = "Yes";
Alert.noLabel = "No";
}
And the Css code is below:
<mx:Style>
.requestVideoAlert
{
background-color: #000000;
back-color: #000000;
area-fill:#000000;
border-color:#000000;
dropShadowEnabled: false;
button-style-name: cancelbtn;
}
.cancelbtn
{
skin: Embed(source='assets/images/videoChat-cancel-button.png');
}
This give the same skin on both the buttons "Accept" and "Reject"
Can some one help me with this.
Thank you
Zee
I dont know if you already found an solution but if no and you use flex 3 I can help you.
This code should do what you need.
Changing buttons and even changing style for text on buttons.
'
protected function application1_creationCompleteHandler(event:FlexEvent):void
{
initStyles();
var myAlert:Alert = Alert.show("description", "title", Alert.YES | Alert.NO);
var arrOfButtons:Array = myAlert.mx_internal::alertForm.mx_internal::buttons;
var button1:Button = arrOfButtons[0];
var button2:Button = arrOfButtons[1];
// buttons filters
button1.styleName = 'buttonStyle1';
button2.styleName = 'buttonStyle2';
button1.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(event:Event):void
{
// you can change text on buttons as well
var btn:Button = event.target as Button;
var text:UITextField = btn.getChildAt(0) as UITextField;
text.filters = [ new GlowFilter(0x3946C0, 1, 4, 2, 8)];
}
/**
* set style of remove button for alert
**/
private function initButton1Style():void
{
var buttonStyle1:CSSStyleDeclaration = new CSSStyleDeclaration('buttonStyle1');
buttonStyle1.setStyle('fontWeight', "normal");
buttonStyle1.setStyle('fontColor', 0x000000);
buttonStyle1.setStyle('color', 0x000000);
buttonStyle1.setStyle("fillColors", [ 0xffffff, 0xF5A2A2, 0xF5A2A2, 0xffffff ]);
buttonStyle1.setStyle('fontSize', 10);
buttonStyle1.setStyle('themeColor', 0xff0000);
StyleManager.setStyleDeclaration(".buttonStyle1", buttonStyle1, true);
}
/**
* set style of buy button for alert
**/
private function initButton2Style():void
{
var buttonStyle2:CSSStyleDeclaration = new CSSStyleDeclaration('buttonStyle2');
buttonStyle2.setStyle('fontWeight', "normal");
buttonStyle2.setStyle('fontColor', 0x000000);
buttonStyle2.setStyle('color', 0x000000);
buttonStyle2.setStyle("fillColors", [ 0xffffff, 0xBAFFAB, 0xBAFFAB, 0xffffff ]);
buttonStyle2.setStyle('fontSize', 10);
buttonStyle2.setStyle('themeColor', 0x7CCB6C);
StyleManager.setStyleDeclaration(".buttonStyle2", buttonStyle2, true);
}
private function initStyles():void
{
initButton1Style();
initButton2Style();
}
]]>
</mx:Script>
'
Unfortunately, I don't think there's a simple way of doing this - the button styles are set in AlertForm by looping through all the buttons on the Alert and setting their stylename to buttonStyleName. In order to set seperate button styles, I think you'd have to extend both Alert (to use a custom AlertForm class) and AlertForm (to override styleChanged to assign seperate style names).
Try this:
package utils
{
import flash.events.EventDispatcher;
import mx.controls.Alert;
import mx.controls.Button;
import mx.core.mx_internal;
public class AlertUtility extends EventDispatcher
{
use namespace mx_internal;
public static function getYesNoAlert(title:String, message:String,closeFunction:Function):void{
var myAlert:Alert = Alert.show(message, title, Alert.YES | Alert.NO,null,closeFunction,null,Alert.NO);
var arrOfButtons:Array = myAlert.mx_internal::alertForm.mx_internal::buttons;
var button1:Button = arrOfButtons[0];
var button2:Button = arrOfButtons[1];
button1.styleName = 'alertBtnStyle1';
button2.styleName = 'alertBtnStyle2';
}
}
}
and in your style css:
mx|Button.alertBtnStyle1 {
skin: ClassReference("skins.myEmphasizedSkin");
/* desired style */
}
mx|Button.alertBtnStyle2 {
emphasizedSkin: ClassReference("skins.myButtonDefaultSkin");
/* desired style */
}

Resources