Thursday, November 8, 2012

Creating EasyTooltip class: Part 7

Today we'll begin working on auto-sizing of our tooltips.

Firstly go to EasyTooltip and add 2 more parameters to receive - the width and height of the parent.

Before that, declare 2 variables - parW and parH:

private var parW:int;
private var parH:int;

And then, as said above, receive the parameters and apply their values to parW and parH. Moreover, pass the two varaibles to the TooltipCursor object's constructor.

/**
 * Create a Tooltip manager. Needed for creating and managing tooltips.
 * @paramparent Reference to the parent of tooltips - the container, which will contain the objects that will be rolled over.
 * @paramparentWidth Width of the parent.
 * @paramparentHeight Height of the parent.
 */

public function EasyTooltip(parent:DisplayObjectContainer, parentWidth:int, parentHeight:int) 
{
par = parent;
listeners = [];
parW = parentWidth;
parH = parentHeight;
cursor = new TooltipCursor(parentWidth, parentHeight);
cursor.mouseChildren = false;
cursor.mouseEnabled = false;
parent.addChild(cursor);
cursor.addEventListener(Event.ENTER_FRAME, onFrame);
setStyle(new TooltipStyle());
trace("Tooltip manager created!");
}

In TooltipCursor declare 2 new varaibles - parentW and parentH:

private var parentW:int;
private var parentH:int;

Receive and set the values in the constructor:

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

Now go to updateSize() function.

The first thing we'll do is calculate the width and height of the tooltip. The width consists of the text width and the left/right text paddings. It is also necessary to add 4 pixels to the width so that the last letter doesn't get cut (some kind of a bug I guess). Similar with the height:

w = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
h = txt.textHeight + 4 + style.textPadding.top + style.textPadding.bottom;

The next 2 lines remain unchanged:

actual_x = 0 + style.offsetX;
actual_y = -h + style.offsetY;

Now here is where it starts to get tricky.

First we check if the tooltip is wide enough to cross the parent's boundaries. This is done with an if...statement:

// if tooltip sticks out of border - calculate shorted width to fit the content
if (this.x + actual_x + w > parentW) {

Then we set text field's wordWrap property to true, so that we can break the text into multiple lines:

txt.wordWrap = true;

Calculate the new width by using all the available space from the current mouse coordinate to the left.

w = parentW - this.x - actual_x;

Then calculate the height the same way we did before:

h = txt.textHeight + 4 + style.textPadding.top + style.textPadding.bottom;

All the code above is executed if the width exceeds boundaries. If it doesn't however, we add a while() loop that keeps adding 4 pixels to the width, until the width exceeds the borders.

In the while loop(), after adding the 4 pixels, we also add another check - set wordWrap of the text to false and see if the whole text fits in the visible area. If it does, that means the whole text is fit as one line in the tooltip. Then we can break the while loop and keep that width. Otherwise, set wordWrap back to true and continue:

}else {
// if doesn't stick out, keep adding 4 pixels to the width until as much text is seen as possible
while (this.x + actual_x + w < parentW) {
w += 4;
// 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 = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
break;
}
txt.wordWrap = true;
}

}

Full function:

private function updateSize():void {
w = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
h = txt.textHeight + 4 + style.textPadding.top + style.textPadding.bottom;
actual_x = 0 + style.offsetX;
actual_y = -h + style.offsetY;

// if tooltip sticks out of border - calculate shorted 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;
}else {
// if doesn't stick out, keep adding 4 pixels to the width until as much text is seen as possible
while (this.x + actual_x + w < parentW) {
w += 4;
// 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 = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
break;
}
txt.wordWrap = true;
}

}
}

And full code now:

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;

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

/**
 * 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();
background.graphics.beginFill(style.backgroundColor, style.backgroundAlpha);
background.graphics.drawRect(actual_x, actual_y, w, h);
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 {
w = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
h = txt.textHeight + 4 + style.textPadding.top + style.textPadding.bottom;
actual_x = 0 + style.offsetX;
actual_y = -h + style.offsetY;

// if tooltip sticks out of border - calculate shorted 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;
}else {
// if doesn't stick out, keep adding 4 pixels to the width until as much text is seen as possible
while (this.x + actual_x + w < parentW) {
w += 4;
// 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 = txt.textWidth + 4 + style.textPadding.left + style.textPadding.right;
break;
}
txt.wordWrap = true;
}

}
}

}

}

I made an example with 2 visual objects that invoke tooltips:

tooltip = new EasyTooltip(stage, stage.stageWidth, stage.stageHeight);
tooltip.addListener(object1, "This is an object.");
tooltip.addListener(object2, "This is a piece of a longer text. It should fit in the visible area.");


Thanks for reading!

No comments:

Post a Comment