Code Monkey home page Code Monkey logo

defold-richtext's Issues

Add support for overlapping/out of order tags.

Allow RichText to handle text like this:

<color=red><b>some text</color></b>

...where the tags are not closed in the same order that they are opened.

Or they could actually overlap, like this:
"<i>this is italic <b>bold italic : </i>bold regular</b> back to normal."

this is italic bold italic : bold regular back to normal.

I guess the parser would have to store the current styling state with the order of opened tags, so it could handle any tag that came next, rather than only searching for consecutive open-close pairs.


Feel free to close as "won't add".

Double newlines in conjunction with <size> create extra newline

Here's a minimal reproductible example:

local text = "<size=0.5>The brown fox jumps over the lazy dog. The brown fox jumps over the lazy dog.\n\nThe brown fox jumps over the lazy dog. The brown fox jumps over the lazy dog.</size>"
local words, metrics = richtext.create(text, "Roboto", settings)

And here's how it looks (notice the extra newline in the middle of the second paragraph):
Screenshot 2019-04-12 at 12 52 32

I need a way to prevent a line break with whatever's in front of the current token.

I have the following scenario:

I have a text bubble with some arbitrary text, which needs to have a small caret image prepended. Something like this:

Screenshot 2019-03-29 at 18 27 05

The caret must never be orphaned alone on a line. I tried adding <nobr> before the last word, but I'm left with situations like this, which are invalid:

Jennifer has <i>logged <nobr>in</i> <img=interludes:advance/></nobr>

Bold and linebreak compatibility

A linebreak seem to disappear when using the b tag.

Passing in this to richtext.create() gives correct results (there is one empty line between the lines):
Line 1\n\nLine 2

but this generates two lines without the empty line in between:
<b>Line 1</b>\n\nLine 2

Sprites being scaled when they shouldn't be?

What's happening here? There are two icons. One is displayed like how it should be, the second looks incorrect. I tried to reset the size but it did not seem to help.

local text = [[<img=icons:document/><size=1>Kalipi</size><br/><size=0.7>Well, hello there! He he he... ho ho ho...</size><br/><size=0.7>What do we have here?</size><size=1.0></size><img=icons:document/>]]		

2018-03-06 22_48_37-vn

Adding some size 1 text doesn't help either

2018-03-06 22_51_31-vn

Putting second on its own line also doesn't help

2018-03-06 22_52_36-vn

Anchor tag not correct work, if font is big size

Hello.
If font is a big size, then message "click text" work, when I click in up left corner. Only up left corner of word, but not whole word. It reproduce on font Roboto-Regular with 72 pt in example project.

How to reuse already created nodes?

for expample

rnode = richtext.create('<color=red>my text</color>')
.... somewhere deep in code ....
rnode.text += '<color=green>new text</color>'

The new 5.8.0 tag parser generates empty text words that can break layout

Stuff like this happens:

Screenshot 2019-09-08 at 13 44 22

Printing out the generated words table, I notice there's some empty text nodes, including one at the very end, which creates the extra newline (see words[7] and words[8] in the output below):

The text and options fed to richtext.create():

DEBUG:SCRIPT: "We'll be bringing in a subject. They're a trained actor, but should be fully in character for the duration. I'm to be considered invisible and inaudible by the actor, but I'll be guiding you along.<nobr> <img=t:advance/></nobr>",
dialogue,
{ --[[0x136f546e0]]
  combine_words = true,
  align = hash: [ALIGN_CENTER],
  fonts = { --[[0x12070b0e0]]
    timer = { --[[0x12070bd80]]
      regular = hash: [timer]
    },
    title = { --[[0x12070bbd0]]
      bold_italic = hash: [title_bolditalic],
      regular = hash: [title],
      italic = hash: [title_italic],
      bold = hash: [title_bold]
    },
    dialogue_bold = { --[[0x12070b6e0]]
      bold_italic = hash: [dialogue_italic],
      regular = hash: [dialogue_bold],
      italic = hash: [dialogue_bolditalic],
      bold = hash: [dialogue]
    },
    dialogue = { --[[0x12070b130]]
      bold_italic = hash: [dialogue_bolditalic],
      regular = hash: [dialogue],
      italic = hash: [dialogue_italic],
      bold = hash: [dialogue_bold]
    },
    document_serif = { --[[0x12070b670]]
      bold_italic = hash: [document_serif_bolditalic],
      regular = hash: [document_serif],
      italic = hash: [document_serif_italic],
      bold = hash: [document_serif_bold]
    },
    document = { --[[0x12070b7a0]]
      regular = hash: [document],
      italic = hash: [document_italic],
      bold = hash: [document_bold]
    },
    dialogue_small = { --[[0x12070b370]]
      bold_italic = hash: [dialogue_bolditalic_small],
      regular = hash: [dialogue_small],
      italic = hash: [dialogue_italic_small],
      bold = hash: [dialogue_bold_small]
    },
    dialogue_italic = { --[[0x12070b5b0]]
      bold_italic = hash: [dialogue_bold],
      regular = hash: [dialogue_italic],
      italic = hash: [dialogue],
      bold = hash: [dialogue_bolditalic]
    }
  },
  layers = { --[[0x1207049c0]]
    fonts = { --[[0x12066cd40]]
      hash: [timer] = hash: [tutor_timer],
      hash: [title_bolditalic] = hash: [tutor_title_bolditalic],
      hash: [document_serif_italic] = hash: [tutor_document_serif_italic],
      hash: [title_bold] = hash: [tutor_title_bold],
      hash: [title_italic] = hash: [tutor_title_italic],
      hash: [dialogue_bold_small] = hash: [tutor_dialogue_bold_small],
      hash: [dialogue_bolditalic_small] = hash: [tutor_dialogue_bolditalic_small],
      hash: [dialogue_italic_small] = hash: [tutor_dialogue_italic_small],
      hash: [dialogue_small] = hash: [tutor_dialogue_small],
      hash: [dialogue_italic] = hash: [tutor_dialogue_italic],
      hash: [dialogue] = hash: [tutor_dialogue],
      hash: [dialogue_bolditalic] = hash: [tutor_dialogue_bolditalic],
      hash: [dialogue_bold] = hash: [tutor_dialogue_bold],
      hash: [document] = hash: [tutor_document],
      hash: [document_italic] = hash: [tutor_document_italic],
      hash: [document_bold] = hash: [tutor_document_bold],
      hash: [document_serif] = hash: [tutor_document_serif],
      hash: [document_serif_bold] = hash: [tutor_document_serif_bold],
      hash: [title] = hash: [tutor_title],
      hash: [document_serif_bolditalic] = hash: [tutor_document_serif_bolditalic]
    },
    spinescenes = { } --[[0x136d608f0]],
    images = { } --[[0x136d608a0]]
  },
  parent = @(398.80001831055, 137.89999389648, 0),
  width = 1255
}

And the words table:

DEBUG:SCRIPT: 
{ --[[0x136f54830]]
  1 = { --[[0x136f51100]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    metrics = { --[[0x136e547b0]]
      width = 1156,
      max_descent = 13,
      max_ascent = 48,
      height = 61,
      total_width = 1167
    },
    text = "We'll be bringing in a subject. They're a trained ",
    tags = { } --[[0x136f555f0]],
    node = We'll be bringing in a subject. They're a trained @(-583.5, 0, 0),
    font = "dialogue",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  2 = { --[[0x136f38750]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    metrics = { --[[0x136de9360]]
      width = 1093,
      max_descent = 13,
      max_ascent = 48,
      height = 61,
      total_width = 1104
    },
    text = "actor, but should be fully in character for the ",
    tags = { ... } --[[0x136f555f0]],
    node = actor, but should be fully in character for the @(-552, -61, 0),
    font = "dialogue",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  3 = { --[[0x136f39d60]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    metrics = { --[[0x136da5680]]
      width = 1057,
      max_descent = 13,
      max_ascent = 48,
      height = 61,
      total_width = 1068
    },
    text = "duration. I'm to be considered invisible and ",
    tags = { ... } --[[0x136f555f0]],
    node = duration. I'm to be considered invisible and @(-534, -122, 0),
    font = "dialogue",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  4 = { --[[0x136f016b0]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    metrics = { --[[0x136d28390]]
      width = 1248,
      max_descent = 13,
      max_ascent = 48,
      height = 61,
      total_width = 1248
    },
    text = "inaudible by the actor, but I'll be guiding you along.",
    tags = { ... } --[[0x136f555f0]],
    node = inaudible by the actor, but I'll be guiding you along.@(-654.5, -183, 0),
    font = "dialogue",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  5 = { --[[0x136ed7d60]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    font = "dialogue",
    text = " ",
    nobr = true,
    node =  @(593.5, -183, 0),
    metrics = { --[[0x136d1c5c0]]
      width = 0,
      max_descent = 13,
      max_ascent = 48,
      height = 0,
      total_width = 11
    },
    tags = { --[[0x136ee3fa0]]
      nobr = true,
      img = "t:advance"
    },
    linebreak = false,
    tag = "nobr",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  6 = { --[[0x136ece3a0]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    font = "dialogue",
    text = "",
    nobr = true,
    image = { --[[0x136eda4d0]]
      anim = "advance",
      texture = "t"
    },
    node = box@(604.5, -183, 0),
    tags = { --[[0x136ee3fa0]]
      nobr = true,
      img = "t:advance"
    },
    metrics = { --[[0x136d01700]]
      width = 50,
      height = 50,
      total_width = 50
    },
    tag = "img",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  7 = { --[[0x136ec67c0]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    font = "dialogue",
    text = "",
    nobr = true,
    node = @(654.5, -183, 0),
    metrics = { --[[0x136d03b70]]
      width = 0,
      max_descent = 13,
      max_ascent = 48,
      height = 61,
      total_width = 0
    },
    tags = { --[[0x136ec5860]]
      nobr = true
    },
    linebreak = false,
    tag = "nobr",
    shadow = vmath.vector4(0, 0, 0, 0)
  },
  8 = { --[[0x136ebeab0]]
    outline = vmath.vector4(0, 0, 0, 0),
    size = 1,
    color = vmath.vector4(1, 1, 1, 1),
    font = "dialogue",
    text = "",
    node = @(0, -244, 0),
    tags = { } --[[0x136ec8680]],
    metrics = { --[[0x136d05070]]
      width = 0,
      max_descent = 13,
      max_as...
[Output truncated]

Defold-richtext does not set text node outline color

Defold-richtext does not set text node outline color. For runtime created nodes default outline color is black (for nodes created in editor this color is white). Because of this, text created by defold-richtext looks in some cases thinner then text in other text nodes (at least for bitmap fonts).

Weird, hard to repro bug

I don't know if RichText is at fault, but we've been having this bug happen once or twice a day on one of our expo machines at Gamescom. RichText is the main shared codepath between all the occurence points of this bug, so I said I'd share.

We haven't had it ever happen on any other PC.

The machine is a MacBookPro10,1, 8GB RAM, Mojave 10.14.5, NVIDIA GT650M, connected to an external display.

Nothing in the console.

IMG_20190822_144805282
IMG_20190822_144910659
IMG_20190822_144751540

Rescale images inline

Maybe add a <scale> tag? Or use the <size> tag? (this would be a breaking change)

Line padding setting tag?

Allow adjustment to padding between words? So negative y margin/padding could move lines closer together with less of a gap.

Adding extra waiting time for create_truncate_example()

It would be useful if it were somehow possible to add tags like <wait=1/> to be able to skip timer.delay repeats based on those kinds of tags. Maybe being able to check the next word/tag see if it's a wait tag and live edit it so it's at or below 0?

This would be useful to add a little time between line breaks / add suspense in text.

Add callback support for handling unknown tags

Allow the user to setup a callback function to run if the user wants to use tags which are not defined in the normal extension.

Example for a possible action tag would be

<action=particles:text_emitter>Make this pretty</action>

and

<action=sound:ding/>

The main thing the callback needs to be used for is defining how the data of the tag is handled so in the sound case the "sound:ding" would need to be handled when doing parse_tag.

local parse = require("richtext.parse")
parse.callback = function(tag, params, settings)
	if tag == "action" then
		local action_type, action_option = params:match("(.-):(.*)")
		settings.action = {
			type = action_type,
			option = action_option
		}
	end
end


.... in parse.lua below other tag definitions

	elseif M.callback then
		M.callback(tag, params, settings)
	end

Truncate to x many chars while ignoring tags

So <size=1>Lara</size><br/><size=0.7>Well, hello there!</size> could be truncated to

<size=1>Lara</size><br/><size=0.7>Well, h</size> for whatever function helper name could be.

This is so RPG style per char text showing can be easily done.

Vertical alignment setting

A vertical alignment setting so that components of different heights can be arranged by top/bottom/middle.

Current behaviour looks like this:
image

Desired behaviour would be a setting that could achieve all of these setups:
image

metrics should include img count

Reasoning is that if you use an img within a truncate based on the metrics.char_count you will likely cut off the final char as the actual truncated chars will be one short by the end of it.

Per character effects

A way to break words within special tags into individually characters and then be able to iterate over them to produce different effects.

Maybe something like
Taste the <char class="rainbow">RAINBOW!</char> or <char class="doom">don't</char>

Would return two char numeric tables with a node for each character within the char tag allowing the char tables to be stored and their nodes iterated over to produce different effects.

Multiline strings break, linebreaks of multiline strings should be ignored?

	local text = [[
	<size=1>Lara</size><br/>
	<size=0.7>Well, hello there!</size><br/>
	<size=0.7>What do we have here?</size><img=icons:waiting/>]]

Becomes

2018-03-01 21_56_28-vn

It should be

2018-03-01 21_57_28-vn

Which requires

local text = [[<size=1>Lara</size><br/><size=0.7>Well, hello there!</size><br/><size=0.7>What do we have here?</size><img=icons:waiting/>]]

It would be nice to be able to format in scripts with multiline strings so it's a little easier to manage.

Custom tags or classes

Add the ability to apply custom tags and then provide a function to return all nodes with a specific tag/class

Make it easy to inject color lists

I want to be able to insert a list of colors by name and then be able to easily use them in text. Color names like "abyssal_green" or "lotus_red" with associated color codes.

What I am doing currently is

local richtext_color = require "richtext.color"

local function richtext_insert_colors()
	richtext_color.COLORS["abyssal_green"] = richtext_color.parse_hex("#05f0bc") -- #14ffb9
	richtext_color.COLORS["lotus_red"] = richtext_color.parse_hex("#ff0946")
end

Maybe make it a richtext main function to add a color to the list by name.

richtext.add_color("name", "code") 

Where code is auto detected to be RGB/RGBA/hex/hexa, and if the color already existed it would silently replace it.

Combine words causes wrong metrics

The spacing between words that rich-text applies is not always consistent with the one that Defold applies. This causes lines of text to be more compact than the reported metrics and stuff like this happens, where a lot of extra space is being unused and the line is not well center-aligned:

Screenshot 2019-08-23 at 15 27 42

I think the correct approach for combine_words would be to attempt to combine the words during layout (preferably between getting word metrics and actually creating the node) and measure the whole combined string, not just words individually.

I can try to write a PR for that, as I would really want to have more accurate word spacing in Interrogation, both for improving readability and improving text density.

How to deal with exceeding the node limit?

I don't think that RichText should to handle this case explicitly, I'm just wondering if you have any ideas how to solve this for my project.

I'm dealing with quite a bit of text, generally filling the screen. I'm using the combine_words option, and most of the time, most of the text is plain, so there is no issue at all. But sometimes I will have text that definitely exceeds the 1024 max node limit per gui. I'm making a MUD client (an interface for online text-adventure games), so I'm not in control of the text that I get, only what I do with it. I know I need to split things between multiple GUI components, I'm just not sure what the best way to split it up is.

Some ways I've thought about:

  1. Always split my text into a preset number of large chunks (3-5?).
    • Simple, but not guaranteed to succeed (i.e would crash) in extreme cases.
  2. Always split the text into 1024-character pieces.
    • Still simple, should always work, but seems very inefficient for not being needed 90% of the time.
  3. Count tags and split it up dynamically?
    • Should maybe always work, but not so simple.
    • I don't think I could get an accurate node count without fully parsing the text an extra time, which seems dumb.
  4. Fork & modify RichText to split the "pipeline" into multiple function calls.
    • Should always work, sounds "clean", but requires probably non-trivial changes to RichText.
    • Something like:
      1. Parse.
      2. Combine words. (+ calculate line breaks, etc.?)
      3. Divide word list if there are more than 1024 nodes.
      4. Create nodes in however many GUIs are needed.

Justified text

Not really an issue, but a feature request. While it´s cool to have text left/center/right aligned, I miss being able to justify it or have a function to manage space between words.

I know, a simple autojustify may create the "space river" effect in the text but... Is there any chance the feature will be included in this cool addon of defold?

is_empty tags improperly being added to preceding words since 5.8.0

If I do

local last_word = richtext.truncate(words, length, {words = false})

then

pprint(last_word.tags)

for the text

text...,,,,,,,,,,,,,,,,,,,,,,,,,,,,<br/>

I will get

DEBUG:SCRIPT: 
{ --[[0000019E1A974C90]]
  br = true
}
DEBUG:SCRIPT: 
{ --[[0000019E1A974C90]]
  br = true
}
DEBUG:SCRIPT: 
{ --[[0000019E1A974C90]]
  br = true
}
etc.

Same is true for other is_empty like

wait tag

I added this tag to a custom version of RichText for my needs in a visual novel style project. It would probably be useful for others but I'm not sure if it should be a part of the main project. These are the main changes.

	elseif tag == "wait" then
		settings.wait = tonumber(params)
		if is_empty then
			-- empty tag, ie tag without content
			-- example <br/> and <img=texture:image/>
			local tag_settings = parse_tag(name, params)
			merge_tags(word_settings, tag_settings)
			local add_this_word = ""
			if tag_settings.wait ~= nil and tag_settings.wait > 0 then
				local wait_word = "\226\128\139"
				for i=1, tag_settings.wait do
					add_this_word = add_this_word .. wait_word
				end
			end
			add_word(add_this_word, word_settings, all_words)
		elseif not is_endtag then

Then the tag is used like <wait=30> which inserts 30 zero width characters. This is useful in visual novel projects where you have text appearing once character at a time and you want to add small delays between actions.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.