TalksBubbleImpl.mesa:
routines to insert distinctive markers around a character to indicate that it contains voice
 Ades, April 3, 1986 11:29:49 am PST
DIRECTORY
Rope USING [ROPE, Fetch, Length],
Basics USING [BYTE],
Imager USING [Color, Context, Move, SetColor, ShowChar, ShowRope, SetStrokeWidth, SetStrokeJoint, MaskStroke],
ImagerPath USING [PathProc],
ImagerColorDefs USING [ConstantColor],
ImagerColorPrivate USING [ColorFromStipple],
ImagerFont USING [Extents, FontBoundingBox, Width],
TextNode USING [Location],
NodeStyle USING [Ref, GetReal],
NodeStyleOps USING [OfStyle],
TEditFormatExtras USING [CharacterArtwork, CharacterArtworkRep, CharacterArtworkClass, CharacterArtworkClassRep, RegisterCharacterArtwork, GetFont],
TextEdit USING [FetchChar, CharSet, GetCharProp],
Vector2 USING [VEC];
TalksBubbleImpl: CEDAR PROGRAM IMPORTS Rope, Imager, ImagerColorPrivate, ImagerFont, NodeStyle, TEditFormatExtras, TextEdit = BEGIN
TalksBubbleDataRep: TYPE ~ RECORD [
letter: CHAR,
label: Rope.ROPE,
ascent: REAL,
descent: REAL,
width: REAL,
bearoff: REAL,
firstCharWidth: REAL
];
TalksBubblePaint: PROC [self: TEditFormatExtras.CharacterArtwork, context: Imager.Context] ~ {
data: REF TalksBubbleDataRep ~ NARROW[self.data];
DrawBubble: ImagerPath.PathProc = {
height: REAL ← data.ascent + data.descent;
moveTo[[data.bearoff, -data.descent+(height/4)]];
lineTo[[data.bearoff, -data.descent+(3*height/4)]];
arcTo[[data.bearoff+(data.width/2), -data.descent+height], [data.bearoff + data.width, -data.descent+(3*height/4)]];
lineTo[[data.bearoff+data.width, -data.descent+(height/4)]];
arcTo[[data.bearoff+(15*data.width/16), -data.descent+(height/16)], [data.bearoff + (3*data.width/4), -data.descent]];
lineTo[[data.bearoff+(data.width/4), -data.descent-(height/2)]];
lineTo[[data.bearoff+(data.width/2), -data.descent]];
lineTo[[data.bearoff+(data.width/4), -data.descent]];
arcTo[[data.bearoff+(data.width/16), -data.descent+(height/16)], [data.bearoff, -data.descent+(height/4)]];
};
DrawBox: ImagerPath.PathProc = {
moveTo[[data.bearoff+data.firstCharWidth, -data.descent]];
lineTo[[data.bearoff+data.firstCharWidth, data.ascent]];
lineTo[[data.bearoff+data.width, data.ascent]];
lineTo[[data.bearoff+data.width, -data.descent]];
lineTo[[data.bearoff+data.firstCharWidth, -data.descent]]
};
Imager.Move[context];
Imager.ShowChar[context, data.letter];
IF data.label = NIL
THEN
{ Imager.SetStrokeWidth[context, 1.0];
Imager.SetStrokeJoint[context, round];
Imager.MaskStroke[context, DrawBubble, TRUE]
}
ELSE
{ Imager.SetColor[context, textColor];
Imager.ShowRope[context, data.label];
Imager.SetStrokeWidth[context, 1.0];
Imager.SetStrokeJoint[context, round];
Imager.MaskStroke[context, DrawBox, TRUE]
}
};
TalksBubbleFormat: PROC [class: TEditFormatExtras.CharacterArtworkClass, loc: TextNode.Location, style: NodeStyle.Ref, kind: NodeStyleOps.OfStyle] RETURNS [TEditFormatExtras.CharacterArtwork] ~ {
charSet: TextEdit.CharSet ← TextEdit.FetchChar[loc.node, loc.where].charSet;
letter: CHAR ← TextEdit.FetchChar[loc.node, loc.where].char;
ascent: REAL ← NodeStyle.GetReal[style, backgroundAscent];
descent: REAL ← NodeStyle.GetReal[style, backgroundDescent];
width: REAL;
bearoff: REAL ← NodeStyle.GetReal[style, outlineboxBearoff];
label: Rope.ROPENARROW[TextEdit.GetCharProp[loc.node, loc.where, $voiceWindow], Rope.ROPE];
escapement: Vector2.VEC ← ImagerFont.Width[TEditFormatExtras.GetFont[style], [set: charSet, code: letter-'\000]];
firstCharWidth: REAL ← escapement.x;
IF label # NIL THEN FOR i: INT IN [0..label.Length) DO
escapement.x ← escapement.x + ImagerFont.Width[TEditFormatExtras.GetFont[style], [set: charSet, code: label.Fetch[i]-'\000]].x
ENDLOOP;
width ← escapement.x;
IF ascent+descent <= 0.0 THEN {
fontBoundingBox: ImagerFont.Extents ~ ImagerFont.FontBoundingBox[TEditFormatExtras.GetFont[style]];
ascent ← fontBoundingBox.ascent + bearoff;
descent ← fontBoundingBox.descent - bearoff;
};
{ data: REF TalksBubbleDataRep ~ NEW[TalksBubbleDataRep ← [
letter: letter,
label: label,
ascent: ascent,
descent: descent,
width: width,
bearoff: bearoff,
firstCharWidth: firstCharWidth
]];
extents: ImagerFont.Extents ← [leftExtent: -data.bearoff+2.0, rightExtent: data.bearoff+data.width+2.0, ascent: data.ascent+2.0, descent: (data.descent+data.ascent)/2];
RETURN [NEW[TEditFormatExtras.CharacterArtworkRep ← [paint: TalksBubblePaint, extents: extents, escapement: escapement, data: data]]]
}
};
talksBubbleClass: TEditFormatExtras.CharacterArtworkClass ~ NEW[TEditFormatExtras.CharacterArtworkClassRep ← [
name: $TalksBubble,
format: TalksBubbleFormat,
data: NIL
]];
textColor: ImagerColorDefs.ConstantColor ← ImagerColorPrivate.ColorFromStipple[7BDEH, paint];
TEditFormatExtras.RegisterCharacterArtwork[talksBubbleClass];
END.