Thursday, November 15, 2012

Creating EasyTooltip class: Part 14

In this tutorial we'll fix a problem related to tooltip sizing, and then expand the styling capabilities.

To fix this sizing problem, go to TooltipCursor and add an if..statement in each while loop in the updateSize() function. The if...statement is contained in another if...statement, which already exists in the while loop. It basically checks if width is less than minWidth, and sets it to minWidth if so:

if (w < style.minWidth) {
w = style.minWidth;
}

Full function:

private function updateSize():void {
if (style.maxWidth == 0) style.maxWidth = parentW;
if (style.maxHeight == 0) style.maxHeight = parentH;
txt.wordWrap = true;
w = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
h = txt.textHeight + 4 + style.textPadding.top + style.textPadding.bottom;
if (w > style.maxWidth) w = style.maxWidth;
if (h > style.maxHeight) h = style.maxHeight;
if (w < style.minWidth) w = style.minWidth;
if (h < style.minHeight) h = style.minHeight;

// calculate best direction
if (this.y <= h-style.offsetY) { 
direction = "bottom"; 
actual_y = -style.offsetY;
} else
if (this.y > h-style.offsetY) { 
direction = "top";
actual_y = -h + style.offsetY;
}

if (this.x <= parentW * 0.5) { 
direction += "right"; 
actual_x = - style.offsetX;
} else
if (this.x > parentW * 0.5) { 
direction += "left";
actual_x = -w + style.offsetX;
}

// If sticks out on the right:
if(direction == "topright" || direction == "bottomright"){
// if tooltip sticks out of border - calculate shortened width to fit the content
if (this.x + actual_x + w > parentW) {
txt.wordWrap = true;
w = parentW - this.x - actual_x;
h = txt.textHeight + 4 + style.textPadding.top + style.textPadding.bottom;
if (w > style.maxWidth) w = style.maxWidth;
if (h > style.maxHeight) h = style.maxHeight;
if (w < style.minWidth) {
w = style.minWidth;
this.x = parentW - w + style.offsetX;
}
if (h < style.minHeight) h = style.minHeight;
} else {
// if doesn't stick out, keep adding 1 pixel to the width until as much text is seen as possible
while (this.x + actual_x + w < parentW && w < style.maxWidth && w >= style.minWidth) {
w += 1;
// try turning off wordwrap and see if it still fits (as one line)
txt.wordWrap = false;
if (w > txt.textWidth + 4 + style.textPadding.left + style.textPadding.right && w >= style.minWidth && w < style.maxWidth) {
w = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
if (w < style.minWidth) {
w = style.minWidth;
}
break;
}
txt.wordWrap = true;
}
}
}

// If sticks out on the left:
if(direction == "topleft" || direction == "bottomleft"){
if (this.x + actual_x <= 0) {
txt.wordWrap = true;
actual_x = -this.x;
w = this.x + style.offsetX;
h = txt.textHeight + 4 + style.textPadding.top + style.textPadding.bottom;
if (w > style.maxWidth) w = style.maxWidth;
if (h > style.maxHeight) h = style.maxHeight;
if (w < style.minWidth) w = style.minWidth;
if (h < style.minHeight) h = style.minHeight;
} else {
// if doesn't stick out, keep adding 1 pixel to the width (and substracting it from actual_x) until as much text is seen as possible
while (this.x + actual_x > 0 && w < style.maxWidth && w >= style.minWidth) {
actual_x -= 1;
w += 1;
// try turning off wordwrap and see if it still fits (as one line)
txt.wordWrap = false;
if (w > txt.textWidth + 4 + style.textPadding.left + style.textPadding.right && w >= style.minWidth && w <= style.maxWidth) {
w = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
if (w < style.minWidth) {
w = style.minWidth;
}
break;
}
txt.wordWrap = true; 
}
}
}

}

Now we'll improve the ability to stylize the tooltips. In the end, the user should be able to easily skin the tooltip, as he would design a rectangle using AS3 drawing api. It will be possible to fill the tooltip box with a solid color, gradient or bitmap fill. Same with strokes - they can be solid, gradient or bitmap colored.

Create 2 new actionscript classes in the EasyTooltip directory. Give them names SolidColorRect and SolidColorStroke. These classes (as well as the whole skinning system) is very similar to the one I created in my AdvAlert library.

These classes allow the user to easily and quickly declare some skinning values that will later be used by the code to draw the tooltip.

SolidColorRect.as code:

package com.kircode.EasyTooltip 
{
/**
 * ...
 * @author Kirill Poletaev
 */
public class SolidColorRect
{
public var _backgroundColor:uint;
public var _radius:Array;
public var _alpha:Number;

/**
 * Create a rectangle filled with solid color.
 * @parambackgroundColor Background color fill of the box.
 * @paramcornerRadius Radius of the corners of the box - top left, top right, bottom left, bottom right.
 * @paramalpha Alpha channel of the background fill.
 */

public function SolidColorRect(backgroundColor:uint, cornerRadius:Array, alpha:Number) 
{
if (cornerRadius == null) cornerRadius = [0, 0, 0, 0];
_backgroundColor = backgroundColor;
_radius = cornerRadius;
_alpha = alpha
}

}

}

And SolidColorStroke.as code:

package com.kircode.EasyTooltip 
{
/**
 * ...
 * @author Kirill Poletaev
 */
public class SolidColorStroke
{
public var _lineThickness:Number;
public var _lineColor:uint;
public var _lineAlpha:Number;

/**
 * Create a solid colored line stroke for rectangles.
 * @paramlineThickness Thickness of the stroke.
 * @paramlineColor Color of the stroke.
 * @paramlineAlpha Alpha channel of the stroke.
 */

public function SolidColorStroke(lineThickness:Number, lineColor:uint, lineAlpha:Number) 
{
_lineThickness = lineThickness;
_lineColor = lineColor;
_lineAlpha = lineAlpha;
}

}

}

Go to TooltipStyle. Remove the background related variables, add 2 typeless variables backgroundRect and backgroundStroke.

public var backgroundRect:*;
public var backgroundStroke:*;

Set their values to new SolidColorRect and SolidColorStroke values:

backgroundRect = new SolidColorRect(0x222222, [10, 10, 10, 10], 1);
backgroundStroke = new SolidColorStroke(1, 0x0, 1);

Full TooltipStyle class:

package com.kircode.EasyTooltip 
{
import flash.text.TextFormat;
/**
 * Used for visually styling tooltips.
 * @author Kirill Poletaev
 */
public class TooltipStyle 
{
public var backgroundRect:*;
public var backgroundStroke:*;
public var textAlpha:Number;
public var textFormat:TextFormat;
public var textPadding:TextPadding;
public var offsetX:int;
public var offsetY:int;
public var maxWidth:int;
public var minWidth:int;
public var maxHeight:int;
public var minHeight:int;

public function TooltipStyle() 
{
backgroundRect = new SolidColorRect(0x222222, [10, 10, 10, 10], 1);
backgroundStroke = new SolidColorStroke(1, 0x0, 1);
textAlpha = 1;
textFormat = new TextFormat("Arial", 17, 0xffffff);
textPadding = new TextPadding(5, 5, 5, 5);
offsetX = 0;
offsetY = 0;
maxWidth = 0;
minWidth = 0;
maxHeight = 0;
minHeight = 0;
}

}

}

Now return to TooltipCursor and find the updateDisplay() function. Delete all the lines that draw the background. Add 2 if..statements, that check if backgroundStroke is a SolidColorStroke instance and if backgroundRect is a SolidColorRect instance. Draw the background according to the provided values:

private function updateDisplay():void {
background.graphics.clear();

if (style.backgroundStroke is SolidColorStroke) {
background.graphics.lineStyle(style.backgroundStroke._lineThickness, style.backgroundStroke._lineColor, style.backgroundStroke._lineAlpha);
}

if (style.backgroundRect is SolidColorRect) {
background.graphics.beginFill(style.backgroundRect._backgroundColor, style.backgroundRect._alpha);
background.graphics.drawRoundRectComplex(actual_x, actual_y, w, h, style.backgroundRect._radius[0], style.backgroundRect._radius[1], style.backgroundRect._radius[2], style.backgroundRect._radius[3]);
background.graphics.endFill();
}

txt.width = w - style.textPadding.left - style.textPadding.right;
txt.height = h - style.textPadding.top - style.textPadding.bottom;
txt.x = actual_x + style.textPadding.left;
txt.y = actual_y + style.textPadding.top;
}

Full TooltipCursor class code so far:

package com.kircode.EasyTooltip 
{
import flash.display.MovieClip;
import flash.display.Shape;
import flash.text.TextField;
import flash.text.TextFormat;
/**
 * The cursor that follows the mouse (if not set otherwise) and displays each tooltip.
 * @author Kirill Poletaev
 */
public class TooltipCursor extends MovieClip
{

public var txt:TextField;
private var style:TooltipStyle;
private var background:Shape;
private var w:int;
private var h:int;
private var actual_x:int;
private var actual_y:int;
private var parentW:int;
private var parentH:int;
private var direction:String = "";

public function TooltipCursor(pW:int, pH:int) 
{
parentW = pW;
parentH = pH;
txt = new TextField();
txt.multiline = true;
background = new Shape();
addChild(background);
addChild(txt);
txt.width = 1;
txt.height = 1;
}

/**
 * Set style of the tooltips.
 * @paramst TooltipStyle object to apply.
 */

public function setStyle(st:TooltipStyle):void {
style = st;
}

/**
 * Change the text value of the tooltip.
 * @parammessage The new text value.
 */

public function setText(message:String):void {
txt.text = message;
txt.setTextFormat(style.textFormat);
updateSize();
updateDisplay();
}

private function updateDisplay():void {
background.graphics.clear();

if (style.backgroundStroke is SolidColorStroke) {
background.graphics.lineStyle(style.backgroundStroke._lineThickness, style.backgroundStroke._lineColor, style.backgroundStroke._lineAlpha);
}

if (style.backgroundRect is SolidColorRect) {
background.graphics.beginFill(style.backgroundRect._backgroundColor, style.backgroundRect._alpha);
background.graphics.drawRoundRectComplex(actual_x, actual_y, w, h, style.backgroundRect._radius[0], style.backgroundRect._radius[1], style.backgroundRect._radius[2], style.backgroundRect._radius[3]);
background.graphics.endFill();
}

txt.width = w - style.textPadding.left - style.textPadding.right;
txt.height = h - style.textPadding.top - style.textPadding.bottom;
txt.x = actual_x + style.textPadding.left;
txt.y = actual_y + style.textPadding.top;
}

private function updateSize():void {
if (style.maxWidth == 0) style.maxWidth = parentW;
if (style.maxHeight == 0) style.maxHeight = parentH;
txt.wordWrap = true;
w = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
h = txt.textHeight + 4 + style.textPadding.top + style.textPadding.bottom;
if (w > style.maxWidth) w = style.maxWidth;
if (h > style.maxHeight) h = style.maxHeight;
if (w < style.minWidth) w = style.minWidth;
if (h < style.minHeight) h = style.minHeight;

// calculate best direction
if (this.y <= h-style.offsetY) { 
direction = "bottom"; 
actual_y = -style.offsetY;
} else
if (this.y > h-style.offsetY) { 
direction = "top";
actual_y = -h + style.offsetY;
}

if (this.x <= parentW * 0.5) { 
direction += "right"; 
actual_x = - style.offsetX;
} else
if (this.x > parentW * 0.5) { 
direction += "left";
actual_x = -w + style.offsetX;
}

// If sticks out on the right:
if(direction == "topright" || direction == "bottomright"){
// if tooltip sticks out of border - calculate shortened width to fit the content
if (this.x + actual_x + w > parentW) {
txt.wordWrap = true;
w = parentW - this.x - actual_x;
h = txt.textHeight + 4 + style.textPadding.top + style.textPadding.bottom;
if (w > style.maxWidth) w = style.maxWidth;
if (h > style.maxHeight) h = style.maxHeight;
if (w < style.minWidth) {
w = style.minWidth;
this.x = parentW - w + style.offsetX;
}
if (h < style.minHeight) h = style.minHeight;
} else {
// if doesn't stick out, keep adding 1 pixel to the width until as much text is seen as possible
while (this.x + actual_x + w < parentW && w < style.maxWidth && w >= style.minWidth) {
w += 1;
// try turning off wordwrap and see if it still fits (as one line)
txt.wordWrap = false;
if (w > txt.textWidth + 4 + style.textPadding.left + style.textPadding.right && w >= style.minWidth && w < style.maxWidth) {
w = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
if (w < style.minWidth) {
w = style.minWidth;
}
break;
}
txt.wordWrap = true;
}
}
}

// If sticks out on the left:
if(direction == "topleft" || direction == "bottomleft"){
if (this.x + actual_x <= 0) {
txt.wordWrap = true;
actual_x = -this.x;
w = this.x + style.offsetX;
h = txt.textHeight + 4 + style.textPadding.top + style.textPadding.bottom;
if (w > style.maxWidth) w = style.maxWidth;
if (h > style.maxHeight) h = style.maxHeight;
if (w < style.minWidth) w = style.minWidth;
if (h < style.minHeight) h = style.minHeight;
} else {
// if doesn't stick out, keep adding 1 pixel to the width (and substracting it from actual_x) until as much text is seen as possible
while (this.x + actual_x > 0 && w < style.maxWidth && w >= style.minWidth) {
actual_x -= 1;
w += 1;
// try turning off wordwrap and see if it still fits (as one line)
txt.wordWrap = false;
if (w > txt.textWidth + 4 + style.textPadding.left + style.textPadding.right && w >= style.minWidth && w <= style.maxWidth) {
w = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
if (w < style.minWidth) {
w = style.minWidth;
}
break;
}
txt.wordWrap = true; 
}
}
}

}

}

}

Thanks for reading!

Here is an example showing how to use the new features:

var myStyle:TooltipStyle = new TooltipStyle();
myStyle.minWidth = 240;
myStyle.maxWidth = 0;
myStyle.offsetX = -10;
myStyle.offsetY = -10;

tooltip = new EasyTooltip(stage, stage.stageWidth, stage.stageHeight, 0);
tooltip.setStyle(myStyle);
tooltip.addListener(object1, "Test test test test test test test test test test test test test test test");


No comments:

Post a Comment