Skip to content

Commit

Permalink
Added ability to search for tags. Changed return value from richtext.…
Browse files Browse the repository at this point in the history
…create()
  • Loading branch information
britzl committed Feb 27, 2018
1 parent dd27145 commit be12da3
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 40 deletions.
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,12 @@ The RichText library will create gui text nodes representing the markup in the t
width = 400,
position = vmath.vector3(0, 0, 0),
parent = gui.get_node("parent"),
color = vmath.vector4(0.95, 0.95, 1.0, 1.0),
}

local text = "<size=3>RichText</size>Lorem <color=0,0.5,0,1>ipsum </color>dolor <color=red>sit </color><color=#ff00ffff>amet, </color><size=1.15><font=Nanum>consectetur </font></size>adipiscing elit. <b>Nunc </b>tincidunt <b><i>mattis</i> libero</b> <i>non viverra</i>. Nullam ornare accumsan rhoncus. Nunc placerat nibh a purus auctor, id scelerisque massa rutrum."
local text = "<size=3>RichText</size>Lorem <color=0,0.5,0,1>ipsum </color>dolor <color=red>sit </color><color=#ff00ffff>amet, </color><size=1.15><font=Nanum>consectetur </font></size>adipiscing elit. <b>Nunc </b>tincidunt <b><i>mattis</i> libero</b> <i>non viverra</i>.\n\nNullam ornare accumsan rhoncus.\n\nNunc placerat nibh a purus auctor, id scelerisque massa <size=2>rutrum.</size>"

self.nodes = richtext.create(text, "Roboto", settings)
richtext.create(text, "Roboto", settings)

This would result in the following output:

Expand All @@ -121,5 +122,15 @@ The `settings` table can contain the following values:
* `color` (vector4) - The default color of text. Will be white if not specified.

**RETURNS**
* `nodes` (table) - A table with all the gui text nodes used to create the text
* `words` (table) - A table with all the words that the text has been broken up into. Each word is represented by a table with keys such as `node`, `tags`, `text` etc
* `metrics` (table) - A table with text metrics. Contains the keys `width` and `height`.

### richtext.tagged(words, tag)
Get all words with a specific tag.

**PARAMETERS**
* `words` (table) - The words to search, as received by a call to `richtext.create()`.
* `tag` (string) - Name of the tag to search for. Pass `nil` to get all words without tags.

**RETURNS**
* `words` (table) - A table with all the words that matches the tag.
Binary file modified docs/example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion example/example.gui_script
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,15 @@ function init(self)
}

print(TEXT)
local nodes, metrics = richtext.create(TEXT, "Roboto", settings)
local words, metrics = richtext.create(TEXT, "Roboto", settings)

-- adjust background to cover text
gui.set_size(settings.parent, vmath.vector3(metrics.width, metrics.height, 0))

-- get all bold words and animate them
local bold_words = richtext.tagged(words, "b")
for _,bold_word in pairs(bold_words) do
local pos = gui.get_position(bold_word.node)
gui.animate(bold_word.node, gui.PROP_POSITION, pos + vmath.vector3(0, 4, 0), gui.EASING_INOUTQUAD, 4, ((pos.x * pos.y) % 10) / 10, nil, gui.PLAYBACK_LOOP_PINGPONG)
end
end
41 changes: 27 additions & 14 deletions richtext/parse.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ local color = require "richtext.color"
local M = {}

local function parse_tag(tag, params)
local settings = {}
local settings = { tags = { [tag] = true } }
if tag == "color" then
settings.color = color.parse(params)
elseif tag == "font" then
Expand All @@ -30,6 +30,9 @@ end

-- split a line into words
local function split_line(line, settings, words)
assert(line)
assert(settings)
assert(words)
local ws_start, trimmed_text, ws_end = line:match("^(%s*)(.-)(%s*)$")
if trimmed_text == "" then
add_word(ws_start .. ws_end, settings, words)
Expand All @@ -48,6 +51,9 @@ end
-- split text
-- split by lines first
local function split_text(text, settings, words)
assert(text)
assert(settings)
assert(words)
local added_linebreak = false
if text:sub(-1)~="\n" then
added_linebreak = true
Expand All @@ -66,9 +72,12 @@ local function split_text(text, settings, words)
end
end

local function find_tag(s)
-- find tag in text
-- return the tag, tag params and any text before and after the tag
local function find_tag(text)
assert(text)
-- find tag, end if no tag was found
local before_start_tag, tag, after_start_tag = s:match("(.-)(<[^/]%S->)(.*)")
local before_start_tag, tag, after_start_tag = text:match("(.-)(<[^/]%S->)(.*)")
if not before_start_tag or not tag or not after_start_tag then
return nil
end
Expand All @@ -87,35 +96,39 @@ local function find_tag(s)
end
end

function M.parse(s, parent_settings)
parent_settings = parent_settings or {}
function M.parse(text, word_settings)
assert(text)
assert(word_settings)
local all_words = {}
while true do
local before, tag, params, text, after = find_tag(s)
local before, tag, params, text_in_tag, after = find_tag(text)
-- no more tags? Split and add the entire string
if not tag then
split_text(s, parent_settings, all_words)
split_text(text, word_settings, all_words)
break
end

-- split and add text before the encountered tag
if before ~= "" then
split_text(before, parent_settings, all_words)
split_text(before, word_settings, all_words)
end

-- parse the tag and merge it with settings for the parent tag
-- parse the tag and merge settings
local tag_settings = parse_tag(tag, params)
for k,v in pairs(parent_settings) do
tag_settings[k] = v
for k,v in pairs(word_settings) do
tag_settings[k] = tag_settings[k] or v
end
for tag,_ in pairs(word_settings.tags or {}) do
tag_settings.tags[tag] = true
end

-- parse the text inside the tag and add the words
local inner_words = M.parse(text, tag_settings)
-- parse the text in the tag and add the words
local inner_words = M.parse(text_in_tag, tag_settings)
for _,word in ipairs(inner_words) do
all_words[#all_words + 1] = word
end

s = after
text = after
end
return all_words
end
Expand Down
51 changes: 29 additions & 22 deletions richtext/richtext.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ local function get_trailing_whitespace(text)
return text:match("^.-(%s*)$") or ""
end

local function get_font(word, settings)
local font_name = word.font or settings.font
local font_settings = settings.fonts[font_name]

local function get_font(word, fonts)
local font_settings = fonts[word.font]
local font = nil
if font_settings then
if word.bold and word.italic then
Expand All @@ -26,49 +26,43 @@ local function get_font(word, settings)
end
end
if not font then
font = settings.font
font = word.font
end
return font
end


function M.create(text, font, settings)
assert(text)
assert(font)
settings = settings or {}
settings.font = font
settings.fonts = settings.fonts or {}
if not settings.fonts[font] then
settings.fonts[font] = {
regular = hash(font)
}
end
settings.fonts[font] = settings.fonts[font] or { regular = hash(font) }
settings.color = settings.color or vmath.vector4(1)
settings.position = settings.position or vmath.vector3()

-- cache length of a single space, per font
local font_sizes = {}

local position = vmath.vector3(settings.position)

local words = parser.parse(text)
local word_settings = {
color = settings.color,
font = font,
size = 1
}
local words = parser.parse(text, word_settings)
local highest_word = 0
local text_metrics = {
width = 0,
height = 0,
}
local nodes = {}
local position = vmath.vector3(settings.position)
for _,word in pairs(words) do
-- assign defaults if needed
word.color = word.color or settings.color
word.font = word.font or settings.font
word.size = word.size or 1

-- get font to use based on word tags
local font = get_font(word, settings)
local font = get_font(word, settings.fonts)

-- create and configure text node
local node = gui.new_text_node(vmath.vector3(0), word.text)
nodes[#nodes + 1] = node
word.node = node
if settings.parent then
gui.set_parent(node, settings.parent)
end
Expand Down Expand Up @@ -122,9 +116,22 @@ function M.create(text, font, settings)

end

return nodes, text_metrics
return words, text_metrics
end


function M.tagged(words, tag)
local tagged = {}
for i=1,#words do
local word = words[i]
if not tag and not word.tags then
tagged[#tagged + 1] = word
elseif word.tags and word.tags[tag] then
tagged[#tagged + 1] = word
end
end
return tagged
end


return M

0 comments on commit be12da3

Please sign in to comment.