Skip to content

Commit

Permalink
inital commit, base project
Browse files Browse the repository at this point in the history
  • Loading branch information
KasperGam committed Dec 16, 2020
0 parents commit ca20663
Show file tree
Hide file tree
Showing 19 changed files with 585 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.DS_Store
.vscode
out
27 changes: 27 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
This project is based on the Twemoji project, Copywrite 2019 Twitter Inc,
to the brightscript language and scenegraph user interface. You can
find the origional project here: https://github.com/twitter/twemoji.
Special thanks to Twitter and all contributors to the Twemoji project
for making this possible.

Twemoji is licensed under the MIT License,
https://github.com/twitter/twemoji/blob/master/LICENSE.

Specific uses: The code to generate file names from emoji raw text
was used and adapted for brightscript, and can be found in origional
form here: https://github.com/twitter/twemoji/blob/master/scripts/build.js.

The Regular Expression for detecting Emojis is derived from the
emoji-regex project by Mathias Bynens, Copywrite Mathias Bynens. You can
find the emoji-regex project here:
https://github.com/mathiasbynens/emoji-regex.
Special thanks to @mathiasbynens and all contributors to the emoji-regex
project for making this possible.

emoji-regex is licensed under the MIT License,
https://github.com/mathiasbynens/emoji-regex/blob/master/LICENSE.

Specific uses: The regex found here:
https://github.com/mathiasbynens/emoji-regex/blob/master/es2015/RGI_Emoji.js
was modified to fit the schema of PCRE to be accepted by Roku's regex
component. PCRE documentation can be found here: http://www.pcre.org/.
19 changes: 19 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2020 Kasper Gammeltoft and other contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Emoji On Roku
This project is a port of the Twitter Emoji Project, [Twemoji](https://twemoji.twitter.com/).
The aim of this project is to provide Emoji support on Roku using scenegraph and brightscript.

## Installation

Download the zip containing the source, and unzip the source files.
Place the source and components directories inside your project, in the same
folder.

Alternativly, use component libraries to install the EmojiOnRoku project.
See the component library documentation here:
https://developer.roku.com/docs/references/scenegraph/control-nodes/componentlibrary.md.

Also check the releases page on this Github repository for the correct url for the component
library. Checkout the demo project for a working example.

## Usage

To use emoji in your project, create an `EmojiLabel` and set its `text` property to a string
containing emojis:
**Code:**
```brightscript
myEmojiLabel = createObject("roSGNode", "EmojiLabel")
myEmojiLabel.text = "Hello 👨🏻‍🦰! I am happy 😊 to see you. See emoji 👩🏽‍🦱 chars!"
myEmojilabel.translation=[200, 500]
m.top.appendChild(myEmojiLabel)
```

**Result**:

![example](docs/simpleExample.jpg)

You can also set the color, font, height, and other properties like you would on an ordinary
Scenegraph `Label` node. The emojis are shown at
72x72 size unless you specify the height or emojiSize
properties:
**Code:**
```brightscript
myEmojiLabel.height = 24
myEmojiLabel.color = &h00AAEEFF
```
**Result:**

![heightexample](docs/heightExample.jpg)

Finally, aligment can also be used, just as for normal Scenegraph `Label` nodes as well.
Here are all `horizAlign` and `vertAlign` combinations for the same `EmojiLabel`, who
has a width and height set to the fullscreen size.

Note that it is recommended to use `center` for `vertAlign` (the defualt), since this will align the emojis
relative to the text labels as well.
**Code:**
```brightscript
myEmojiLabel.width = 1920
myEmojiLabel.height = 1080
myEmojiLabel.horizAlign = "left" ' or "center" or "right"
myEmojiLabel.vertAlign = "top" ' or "center" or "bottom"
myEmojiLabel.color = &h00AAEEFF
```
**Result:**

![alignexample](/docs/alignments.jpg)

Note that it is recommended to set an explicit width on labels before using `"center"` or `"right"` for `horizAlign`.

When setting an explicit width, if the width of the label/emojis go over that width, the labels will use an ellipsis.
If the cutoff is in the middle of an emoji, however, the emoji will not be replaced by an ellipsis.

## License
Copyright 2020 Kasper Gammeltoft
Code licensed under the MIT License: http://opensource.org/licenses/MIT
232 changes: 232 additions & 0 deletions components/emojiLabel/EmojiLabel.brs
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
' Copyright Kasper Gammeltoft and other contributors. Licensed under MIT
' https://github.com/KasperGam/emojionroku/blob/master/LICENSE
sub init()
m.components = m.top.findNode("layout")
m.top.observeField("text", "setText")

m.top.observeField("color", "updateComponents")
m.top.observeField("font", "updateComponents")
m.top.observeField("horizAlign", "updateComponents")
m.top.observeField("vertAlign", "updateComponents")
m.top.observeField("width", "updateComponents")
m.top.observeField("height", "updateComponents")
m.top.observeField("emojiSize", "updateComponents")

setText()
end sub

' Will update the components if an interface field has changed.
function updateComponents()
' Only update components if we are actually rendering text
if m.top.text <> ""
width = m.top.width
height = m.top.height
hAlign = m.top.horizAlign
vAlign = m.top.vertAlign

' Set the layout group properties to reflect the horiz and vert
' aligments
m.components.horizAlignment = hAlign
m.components.vertAlignment = vAlign

' Set component properties
comps = getAllComponents()
for each comp in comps
comp.visible = true
if comp.subtype() = "Label"
comp.width = 0
comp.height = height
comp.vertAlign = vAlign
comp.color = m.top.color
if m.top.font <> Invalid
comp.font = m.top.font
end if
else
' For posters, use emojiSize first if set
if m.top.emojiSize > 0
comp.width = m.top.emojiSize
comp.height = m.top.emojiSize
' Then use height if set
else if height > 0
comp.width = height
comp.height = height
else
comp.width = 0
comp.height = 0
end if
end if
end for

' Check if we need to use ellipsis for this label
checkBoundingWidth()

boundingWidth = m.components.boundingRect().width
if width = 0 or boundingWidth > width
width = boundingWidth
end if

if m.top.emojiSize > height
height = m.top.emojiSize
end if
' Set proper translation for horizontal alignment
xTranslation = 0
yTranslation = 0
' For center, that is in the middle of the node
if hAlign = "center"
xTranslation = width / 2
' For right, that is the right edge of the node
else if hAlign = "right"
xTranslation = width
end if

' Set proper translation for vertical alignment
' For center, use the middle of the node
if vAlign = "center"
yTranslation = height / 2
' For bottom, use bottom edge of node
else if vAlign = "bottom"
yTranslation = height
end if

m.components.translation = [xTranslation, yTranslation]
end if
end function

' Convenience function to check if we need to truncate the label
function checkBoundingWidth()
curWidth = 0
width = m.top.width
' Reset previous ellipsis if present
existingEllipsis = m.components.findNode("ellipsis")
if existingEllipsis <> Invalid
m.components.removeChild(existingEllipsis)
end if

comps = getAllComponents()

for index = 0 to comps.count() - 1
comp = comps[index]
' If we are already over the width, then don't display any other components
if curWidth >= width
comp.visible = false
else
' See if this component extends beyond the available width
compWidth = comp.boundingRect().width
curWidth += compWidth

if curWidth > width and width > 0
diff = curWidth - width

' For labels, we might be able to have the label use proper ellipsis by itself
if comp.subType() = "Label"
newCompWidth = compWidth - diff
ellipsis = createLabel("…")
minWidth = ellipsis.boundingRect().width
' If the label is too short to have an ellipsis itself, insert one here
if newCompWidth <= minWidth
comp.visible = false
ellipsis.id = "ellipsis"
m.components.insertChild(ellipsis, index)
else
' Label will use ellipsis with explicit width set
comp.width = newCompWidth
end if
else
' Replace with ellipsis
comp.visible = false
ellipsis = createLabel("…")
ellipsis.id = "ellipsis"
m.components.insertChild(ellipsis, index)
end if
end if
end if
end for
end function

' Updates the entire label components with new text.
function setText()
labelText = m.top.text

resetComponents()
if labelText <> ""
' Check for emojis in this text
emojiRegex = createObject("roRegex", regex(), "m")
matches = emojiRegex.matchAll(labelText)

for each match in matches
matchText = match[0]
' Create the label representing all text before this match,
' if there is any
loc = labelText.instr(matchText)
if loc > 0
leftText = labelText.left(loc)
m.components.appendChild(createLabel(leftText))
end if

' Get the URI for this emoji and create the poster
pointURI = emojiPointName(matchText)
m.components.appendChild(createPoster(pointURI))

' Update the remaining text. Set to be text after this emoji.
labelText = labelText.mid(loc + matchText.len())
end for

' If we have text at the end after the last emoji match, create
' a label for that text.
if labelText <> ""
m.components.appendChild(createLabel(labelText))
end if
end if

' Update the components.
updateComponents()
end function

' Create a new label to display non-emoji text in the label.
function createLabel(withText as String)
label = createObject("roSGNode", "Label")
label.text = withText
label.color = m.top.color
label.vertAlign = m.top.vertAlign
label.height = m.top.height
if m.top.font <> Invalid
label.font = m.top.font
end if

return label
end function

' Create a new poster to show an emoji with.
function createPoster(uri as String)
poster = CreateObject("roSGNode", "Poster")
poster.uri = uri

' Use emoji size first if set
if m.top.emojiSize > 0
poster.width = m.top.emojiSize
poster.height = m.top.emojiSize
' Then use height if set
else if m.top.height > 0
poster.width = m.top.height
poster.height = m.top.height
end if

return poster
end function

' Returns all components for the current emoji label.
function getAllComponents()
components = []
for i = 0 to m.components.getChildCount() - 1
components.push(m.components.getChild(i))
end for

return components
end function

' Removes all child components to reset the layout group.
function resetComponents()
while m.components.getChildCount() > 0
m.components.removeChildIndex(0)
end while
end function
Loading

0 comments on commit ca20663

Please sign in to comment.