Function Repository Resource:

SpeechBubble

Source Notebook

Decorate an expression with a speech bubble frame

Contributed by: Richard Hennigan (Wolfram Research)

ResourceFunction["SpeechBubble"][expr]

displays expr wrapped in a speech bubble frame.

ResourceFunction["SpeechBubble"][speaker,expr]

uses speaker as the source of the speech bubble.

ResourceFunction["SpeechBubble"][speaker,expr,offset]

specifies that the speech bubble should be shifted by the given amount.

ResourceFunction["SpeechBubble"][]

gives a nine-patch image that can be used to decorate other boxes as a speech bubble.

Details and Options

In ResourceFunction["SpeechBubble"][speaker,expr,offset], the value for offset can be:
{x,y}a distance from the top right of expr
Scaled[{x,y}]coordinates scaled from 0 to 1 with respect to the size of expr
Center etc.{Center,Center} etc.
Automatican offset automatically determined by the rendered edge of expr
x-coordinates can be numbers, Automatic, or Left, Center or Right.
y-coordinates can be numbers, Automatic, or Top, Center or Bottom.
ResourceFunction["SpeechBubble"][speaker,expr] is equivalent to ResourceFunction["SpeechBubble"][speaker,expr,Automatic].
The nine-patch image returned by ResourceFunction["SpeechBubble"][] is suitable for use as an Appearance setting for some types of boxes, such as Panel and Button.

Examples

Basic Examples (3) 

Display an expression styled as a speech bubble:

In[1]:=
ResourceFunction["SpeechBubble"]["Hello world!"]
Out[1]=

Include a speaker:

In[2]:=
ResourceFunction["SpeechBubble"][\!\(\*
Graphics3DBox[SphereBox[{0, 0, 0}],
ImageSize->{70., 70.},
SphericalRegion->True,
ViewAngle->0.5011114127587017,
ViewPoint->{1.3, -2.4, 2.},
ViewVertical->{0., 0., 1.}]\), "I'm a Sphere"]
Out[2]=

The speech can be any expression:

In[3]:=
ResourceFunction["SpeechBubble"][\!\(\*
NamespaceBox["LinguisticAssistant",
DynamicModuleBox[{Typeset`query$$ = "picture of a car", Typeset`boxes$$ = RowBox[{
TemplateBox[{"\"car\"", 
RowBox[{"Entity", "[", 
RowBox[{"\"Word\"", ",", "\"car\""}], "]"}], "\"Entity[\\\"Word\\\", \\\"car\\\"]\"", "\"word\""}, "Entity"], "[", 
TemplateBox[{"\"image\"", 
RowBox[{"EntityProperty", "[", 
RowBox[{"\"Word\"", ",", "\"Image\""}], "]"}], "\"EntityProperty[\\\"Word\\\", \\\"Image\\\"]\""}, "EntityProperty"], "]"}], Typeset`allassumptions$$ = {{"type" -> "MultiClash", "word" -> "", "template" -> "Assuming ${word1} is referring to ${desc1}. Use \"${word2}\" as ${desc2}.", "count" -> "2", "Values" -> {{"name" -> "WordData", "word" -> "the input", "desc" -> " referring to English words", "input" -> "*MC.%7E-_*WordData-"}, {"name" -> "Species", "word" -> "car", "desc" -> "a species specification", "input" -> "*MC.%7E-_*Species-"}}}}, Typeset`assumptions$$ = {}, Typeset`open$$ = {1}, Typeset`querystate$$ = {"Online" -> True, "Allowed" -> True, "mparse.jsp" -> 1.5259314, "Messages" -> {}}}, 
DynamicBox[ToBoxes[
AlphaIntegration`LinguisticAssistantBoxes["", 4, Automatic, 
Dynamic[Typeset`query$$], 
Dynamic[Typeset`boxes$$], 
Dynamic[Typeset`allassumptions$$], 
Dynamic[Typeset`assumptions$$], 
Dynamic[Typeset`open$$], 
Dynamic[Typeset`querystate$$]], StandardForm],
ImageSizeCache->{119.8, {9., 15.8}},
TrackedSymbols:>{Typeset`query$$, Typeset`boxes$$, Typeset`allassumptions$$, Typeset`assumptions$$, Typeset`open$$, Typeset`querystate$$}],
DynamicModuleValues:>{},
UndoTrackedVariables:>{Typeset`open$$}],
BaseStyle->{"Deploy"},
DeleteWithContents->True,
Editable->False,
SelectWithContents->True]\), Audio["ExampleData/car.mp3"]]
Out[3]=

Scope (4) 

Specify an offset for the speech frame:

In[4]:=
ResourceFunction["SpeechBubble"][
 Graphics[Disk[], ImageSize -> 50], "centered", Center]
Out[4]=
In[5]:=
ResourceFunction["SpeechBubble"][
 Graphics[Disk[], ImageSize -> 50], "left top", {Left, Top}]
Out[5]=
In[6]:=
ResourceFunction["SpeechBubble"][
 Graphics[Disk[], ImageSize -> 50], "right bottom", {Right, Bottom}]
Out[6]=
In[7]:=
ResourceFunction["SpeechBubble"][
 Graphics[Disk[], ImageSize -> 50], "left bottom", {Left, Bottom}]
Out[7]=

Use an automatic offset that pulls the speech frame towards the edge of visible content:

In[8]:=
ResourceFunction["SpeechBubble"][
 Graphics[Triangle[], ImageSize -> 50], "automatic edge", Automatic]
Out[8]=
In[9]:=
ResourceFunction["SpeechBubble"][
 Graphics[Rectangle[], ImageSize -> 50], "automatic edge", Automatic]
Out[9]=

Don't use any offset:

In[10]:=
ResourceFunction["SpeechBubble"][
 Graphics[Disk[], ImageSize -> 50], "no offset", None]
Out[10]=

Get a nine-patch image for the speech bubble:

In[11]:=
ninePatch = ResourceFunction["SpeechBubble"][]
Out[11]=

Use it as an Appearance for a Panel:

In[12]:=
Panel[Plot[Sin[x], {x, 0, 10}], Appearance -> ninePatch]
Out[12]=

Applications (3) 

Create a custom BirdSay:

In[13]:=
ResourceFunction["SpeechBubble"][Show[\!\(\*
NamespaceBox["LinguisticAssistant",
DynamicModuleBox[{Typeset`query$$ = "picture of a bird", Typeset`boxes$$ = RowBox[{
TemplateBox[{"\"birds\"", 
RowBox[{"Entity", "[", 
RowBox[{"\"Species\"", ",", "\"Class:Aves\""}], "]"}], "\"Entity[\\\"Species\\\", \\\"Class:Aves\\\"]\"", "\"species specification\""}, "Entity"], "[", 
TemplateBox[{"\"image\"", 
RowBox[{"EntityProperty", "[", 
RowBox[{"\"Species\"", ",", "\"Image\""}], "]"}], "\"EntityProperty[\\\"Species\\\", \\\"Image\\\"]\""}, "EntityProperty"], "]"}], Typeset`allassumptions$$ = {{"type" -> "MultiClash", "word" -> "", "template" -> "Assuming ${word1} is referring to ${desc1}. Use \"${word2}\" as ${desc2}. Use \"${word3}\" as ${desc3}.", "count" -> "3", "Values" -> {{"name" -> "Species", "word" -> "bird", "desc" -> "a species specification", "input" -> "*MC.%7E-_*Species-"}, {"name" -> "Person", "word" -> "a bird", "desc" -> "a person", "input" -> "*MC.%7E-_*Person-"}, {"name" -> "WordData", "word" -> "", "desc" -> " referring to English words", "input" -> "*MC.%7E-_*WordData-"}}}}, Typeset`assumptions$$ = {}, Typeset`open$$ = {1}, Typeset`querystate$$ = {"Online" -> True, "Allowed" -> True, "mparse.jsp" -> 3.4309153, "Messages" -> {}}}, 
DynamicBox[ToBoxes[
AlphaIntegration`LinguisticAssistantBoxes["", 4, Automatic, 
Dynamic[Typeset`query$$], 
Dynamic[Typeset`boxes$$], 
Dynamic[Typeset`allassumptions$$], 
Dynamic[Typeset`assumptions$$], 
Dynamic[Typeset`open$$], 
Dynamic[Typeset`querystate$$]], StandardForm],
ImageSizeCache->{124.8, {9., 15.8}},
TrackedSymbols:>{Typeset`query$$, Typeset`boxes$$, Typeset`allassumptions$$, Typeset`assumptions$$, Typeset`open$$, Typeset`querystate$$}],
DynamicModuleValues:>{},
UndoTrackedVariables:>{Typeset`open$$}],
BaseStyle->{"Deploy"},
DeleteWithContents->True,
Editable->False,
SelectWithContents->True]\), ImageSize -> 75], "I'm the new BirdSay", Scaled[.25]]
Out[13]=

Create an even better BirdSay:

In[14]:=
bird = Show[
   ResourceFunction["SVGImport"][
    "https://cultofthepartyparrot.com/assets/parrot.svg"], ImageSize -> 200];
ResourceFunction["SpeechBubble"][bird, Tooltip[
 "The \"S\" in SVG stands for smooth \n\n\n\t\t (probably)", "It does not."]]
Out[12]=

Create a modernized version of cowsay, the original inspiration for BirdSay:

In[15]:=
cowsay[expr_] := ResourceFunction["SpeechBubble"][\!\(\*
NamespaceBox["LinguisticAssistant",
DynamicModuleBox[{Typeset`query$$ = "picture of a cow", Typeset`boxes$$ = RowBox[{
TemplateBox[{"\"cow\"", 
RowBox[{"Entity", "[", 
RowBox[{"\"Species\"", ",", "\"Species:BosTaurus\""}], "]"}], "\"Entity[\\\"Species\\\", \\\"Species:BosTaurus\\\"]\"", "\"species specification\""}, "Entity"], "[", 
TemplateBox[{"\"image\"", 
RowBox[{"EntityProperty", "[", 
RowBox[{"\"Species\"", ",", "\"Image\""}], "]"}], "\"EntityProperty[\\\"Species\\\", \\\"Image\\\"]\""}, "EntityProperty"], "]"}], Typeset`allassumptions$$ = {{"type" -> "MultiClash", "word" -> "", "template" -> "Assuming ${word1} is referring to ${desc1}. Use \"${word2}\" as ${desc2}. Use \"${word3}\" as ${desc3}.", "count" -> "3", "Values" -> {{"name" -> "Species", "word" -> "cow", "desc" -> "a species specification", "input" -> "*MC.%7E-_*Species-"}, {"name" -> "Movie", "word" -> "cow", "desc" -> "a movie", "input" -> "*MC.%7E-_*Movie-"}, {"name" -> "WordData", "word" -> "", "desc" -> " referring to English words", "input" -> "*MC.%7E-_*WordData-"}}}, {"type" -> "SubCategory", "word" -> "cow", "template" -> "Assuming ${desc1}. Use ${desc2} instead", "count" -> "2", "Values" -> {{"name" -> "Species:BosTaurus", "desc" -> "cow",
             "input" -> "*DPClash.SpeciesE.cow-_*Species%3ABosTaurus-"}, {"name" -> "Genus:Bos", "desc" -> "oxen, cattle", "input" -> "*DPClash.SpeciesE.cow-_*Genus%3ABos-"}}}}, Typeset`assumptions$$ = {}, Typeset`open$$ = {1}, Typeset`querystate$$ = {"Online" -> True, "Allowed" -> True, "mparse.jsp" -> 1.6659631, "Messages" -> {}}}, 
DynamicBox[ToBoxes[
AlphaIntegration`LinguisticAssistantBoxes["", 4, Automatic, 
Dynamic[Typeset`query$$], 
Dynamic[Typeset`boxes$$], 
Dynamic[Typeset`allassumptions$$], 
Dynamic[Typeset`assumptions$$], 
Dynamic[Typeset`open$$], 
Dynamic[Typeset`querystate$$]], StandardForm],
ImageSizeCache->{125.8, {9., 15.8}},
TrackedSymbols:>{Typeset`query$$, Typeset`boxes$$, Typeset`allassumptions$$, Typeset`assumptions$$, Typeset`open$$, Typeset`querystate$$}],
DynamicModuleValues:>{},
UndoTrackedVariables:>{Typeset`open$$}],
BaseStyle->{"Deploy"},
DeleteWithContents->True,
Editable->False,
SelectWithContents->True]\), expr, {Scaled[.2], Scaled[.6]}];
In[16]:=
cowsay["moooo"]
Out[16]=

Recreate the Unix command fortune | cowsay:

In[17]:=
ResourceFunction["RandomFortune"][] // cowsay
Out[17]=

Properties and Relations (2) 

The speech bubble frame automatically resizes to fit content:

In[18]:=
ResourceFunction["SpeechBubble"][
 Column[{Slider[Dynamic[r], {-2 Pi, 2 Pi}], Rotate["The speech bubble frame automatically\nresizes to fit content", Dynamic[r]]}]]
Out[18]=

Offsets are specified with respect to the top-right corner:

In[19]:=
Manipulate[
 ResourceFunction["SpeechBubble"][
  Graphics[Circle[], ImageSize -> 100], offset, Scaled[offset]],
 {offset, {0, 0}, {1, 1}}
 ]
Out[19]=

Possible Issues (2) 

Only the content in the speech bubble is interactive:

In[20]:=
ResourceFunction["SpeechBubble"][Button["Can't click", Print[1]], Button["Can click", Print[2]]]
Out[20]=

Automatic offsets will not work very well for dynamic content:

In[21]:=
animation = ResourceFunction["SimpleListAnimate"][
   Table[Graphics[{GrayLevel[1 - x], Disk[]}, ImageSize -> 50, PlotRange -> 3], {x, 0, 1, .1}]];
In[22]:=
ResourceFunction[
 "SpeechBubble"][animation, "automatic edge detection fails"]
Out[22]=

In these cases, it is best to use a specific offset:

In[23]:=
ResourceFunction["SpeechBubble"][animation, "close enough", Scaled[1/3]]
Out[23]=

Version History

  • 1.0.0 – 24 July 2020

Related Resources

License Information