Planet JFX
Advertisement

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();


SweetButton

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.

Advertisement