Skip to content

Commit

Permalink
Added support for combining words on a line to reduce node count
Browse files Browse the repository at this point in the history
Fixes #34
  • Loading branch information
britzl committed Jun 8, 2019
1 parent e2db60f commit 046ee3f
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -142,6 +142,7 @@ The `settings` table can contain the following values:
* `align` (hash) - One of `richtext.ALIGN_LEFT`, `richtext.ALIGN_CENTER` and `richtext.ALIGN_RIGHT`. Defaults to `richtext.ALIGN_LEFT`. Defines how the words of a line of text are positioned in relation the provided `position`.
* `line_spacing` (number) - Value to multiply line height with. Set to a value lower than 1.0 to reduce space between lines and a value higher than 1.0 to increase space between lines. Defaults to 1.0.
* `image_pixel_grid_snap` (boolean) - Set to true to position image on full pixels (positions rounded to nearest integer) to avoid effects of anti-aliasing. Defaults to false.
* `combine_words` (boolean) - Set to true to combine words with the same style on a line into a single node. This is useful for very long texts where the maximum number of nodes would exceed the limit set in the GUI. The disadvantage is that any per word operations will not work since the words have been combined.

The `fonts` table should have the following format:

Expand Down
6 changes: 6 additions & 0 deletions example/example.gui_script
@@ -1,6 +1,11 @@
local richtext = require "richtext.richtext"

local sherlock = [[At three o'clock precisely I was at Baker Street, but Holmes had not yet returned. The landlady informed me that he had left the house shortly after eight o'clock in the morning. I sat down beside the fire, however, with the intention of awaiting him, however long he might be. I was already deeply interested in his inquiry, for, though it was surrounded by none of the grim and strange features which were associated with the two crimes which I have already recorded, still, the nature of the case and the exalted station of his client gave it a character of its own. Indeed, apart from the nature of the investigation which my friend had on hand, there was something in his masterly grasp of a situation, and his keen, incisive reasoning, which made it a pleasure to me to study his system of work, and to follow the quick, subtle methods by which he disentangled the most inextricable mysteries. So accustomed was I to his invariable success that the very possibility of his failing had ceased to enter into my head. It was close upon four before the door opened, and a drunken-looking groom, ill-kempt and side-whiskered, with an inflamed face and disreputable clothes, walked into the room. Accustomed as I was to my friend's amazing powers in the use of disguises, I had to look three times before I was certain that it was indeed he. With a nod he vanished into the bedroom, whence he emerged in five minutes tweed-suited and respectable, as of old. Putting his hands into his pockets, he stretched out his legs in front of the fire and laughed heartily for some minutes. 'Well, really!' he cried, and then he choked and laughed again until he was obliged to lie back, limp and helpless, in the chair. 'What is it?' 'It's quite too funny. I am sure you could never guess how I employed my morning, or what I ended by doing.' 'I can't imagine. I suppose that you have been watching the habits, and perhaps the house, of Miss Irene Adler.' 'Quite so; but the sequel was rather unusual. I will tell you, however. I left the house a little after eight o'clock this morning in the character of a groom out of work. There is a wonderful sympathy and freemasonry among horsey men. Be one of them, and you will know all that there is to know. I soon found Briony Lodge. It is a bijou villa, with a garden at the back, but built out in front right up to the road, two stories. Chubb lock to the door. Large sitting-room on the right side, well furnished, with long windows almost to the floor, and those preposterous English window fasteners which a child could open. Behind there was nothing remarkable, save that the passage window could be reached from the top of the coach-house. I walked round it and examined it closely from every point of view, but without noting anything else of interest. 'I then lounged down the street and found, as I expected, that there was a mews in a lane which runs down by one wall of the garden. I lent the ostlers a hand in rubbing down their horses, and received in exchange twopence, a glass of half and half, two fills of shag tobacco, and as much information as I could desire about Miss Adler, to say nothing of half a dozen other people in the neighbourhood in whom I was not in the least interested, but whose biographies I was compelled to listen to.']]

local function create_long_text_example()
local settings = { position = vmath.vector3(10, 1100, 0), width = 600, combine_words = true }
richtext.create(sherlock, "Roboto-Regular", settings)
end

local function create_complex_example()
local settings = {
Expand Down Expand Up @@ -146,6 +151,7 @@ end

function init(self)
msg.post(".", "acquire_input_focus")
--create_long_text_example()
create_complex_example()
create_align_example()
create_truncate_example()
Expand Down
51 changes: 49 additions & 2 deletions richtext/richtext.lua
Expand Up @@ -11,6 +11,8 @@ M.ALIGN_RIGHT = hash("ALIGN_RIGHT")
local V3_ZERO = vmath.vector3(0)
local V3_ONE = vmath.vector3(1)

local TEXT_NODE_CACHE = {}

local id_counter = 0

local function new_id(prefix)
Expand Down Expand Up @@ -86,10 +88,46 @@ local function get_layer(word, layers)
return layers.fonts[gui.get_font(node)]
end

-- compare two words and check that they have the same size, color and font
local function compare_words(one, two)
return one ~= nil and two ~= nil and one.size == two.size and one.color == two.color and one.font == two.font
end


-- combine a list of words to reduce node count as much as possible
-- this will only combine text nodes
-- combined words will be removed from the list and their nodes added
-- to a node cache for reuse
-- @param words The words to combine
-- @param cache The cache to add combined words to
local function combine_words(words)
local previous_word = nil
local i = 1
while i <= #words do
word = words[i]
if previous_word and word.text and previous_word.text and compare_words(word, previous_word) then
local previous_metrics = previous_word.metrics
local current_metrics = word.metrics
previous_word.text = previous_word.text .. word.text
previous_metrics.width = previous_metrics.width + current_metrics.width
previous_metrics.height = math.max(previous_metrics.height, current_metrics.height)
gui.set_size(previous_word.node, vmath.vector3(previous_metrics.width, previous_metrics.height, 0))
gui.set_text(previous_word.node, previous_word.text)
table.remove(words, i)
TEXT_NODE_CACHE[#TEXT_NODE_CACHE + 1] = word.node
else
previous_word = word
i = i + 1
end
end
end

-- position all words according to the line alignment and line width
-- the list of words will be empty after this function is called
local function position_words(words, line_width, line_height, position, settings)
if settings.combine_words then
combine_words(words)
end
if settings.align == M.ALIGN_RIGHT then
position.x = position.x - line_width
elseif settings.align == M.ALIGN_CENTER then
Expand Down Expand Up @@ -197,8 +235,12 @@ end


local function create_text_node(word, font)
assert(font)
local node = gui.new_text_node(V3_ZERO, word.text)
local node = table.remove(TEXT_NODE_CACHE)
if node then
gui.set_text(node, word.text)
else
node = gui.new_text_node(V3_ZERO, word.text)
end
gui.set_id(node, new_id("textnode"))
gui.set_font(node, font)
gui.set_color(node, word.color)
Expand Down Expand Up @@ -250,6 +292,7 @@ function M.create(text, font, settings)
settings.position = settings.position or V3_ZERO
settings.line_spacing = settings.line_spacing or 1
settings.image_pixel_grid_snap = settings.image_pixel_grid_snap or false
settings.combine_words = settings.combine_words or false

-- default settings for a word
-- will be assigned to each word unless tags override the values
Expand Down Expand Up @@ -330,6 +373,10 @@ function M.create(text, font, settings)
text_metrics.height = text_metrics.height + line_height
end

while #TEXT_NODE_CACHE > 0 do
gui.delete_node(table.remove(TEXT_NODE_CACHE))
end

return words, text_metrics
end

Expand Down

0 comments on commit 046ee3f

Please sign in to comment.