mirror of
https://github.com/Insality/druid
synced 2025-06-27 10:27:48 +02:00
Initial commit with rich text
This commit is contained in:
parent
999789c1c8
commit
fae7e4afa4
199
druid/custom/rich_text/rich_text.gui
Normal file
199
druid/custom/rich_text/rich_text.gui
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
script: ""
|
||||||
|
fonts {
|
||||||
|
name: "game"
|
||||||
|
font: "/example/assets/fonts/game.font"
|
||||||
|
}
|
||||||
|
textures {
|
||||||
|
name: "kenney"
|
||||||
|
texture: "/example/assets/images/kenney.atlas"
|
||||||
|
}
|
||||||
|
background_color {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 0.0
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
size {
|
||||||
|
x: 300.0
|
||||||
|
y: 200.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_BOX
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
texture: ""
|
||||||
|
id: "root"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
slice9 {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 0.0
|
||||||
|
}
|
||||||
|
clipping_mode: CLIPPING_MODE_NONE
|
||||||
|
clipping_visible: true
|
||||||
|
clipping_inverted: false
|
||||||
|
alpha: 1.0
|
||||||
|
template_node_child: false
|
||||||
|
size_mode: SIZE_MODE_MANUAL
|
||||||
|
custom_type: 0
|
||||||
|
enabled: true
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
size {
|
||||||
|
x: 200.0
|
||||||
|
y: 100.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_TEXT
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
text: "Text"
|
||||||
|
font: "game"
|
||||||
|
id: "text_prefab"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
outline {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
shadow {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
line_break: false
|
||||||
|
parent: "root"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
alpha: 1.0
|
||||||
|
outline_alpha: 1.0
|
||||||
|
shadow_alpha: 0.0
|
||||||
|
template_node_child: false
|
||||||
|
text_leading: 1.0
|
||||||
|
text_tracking: 0.0
|
||||||
|
custom_type: 0
|
||||||
|
enabled: true
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 77.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
size {
|
||||||
|
x: 36.0
|
||||||
|
y: 36.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_BOX
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
texture: "kenney/slider_move"
|
||||||
|
id: "icon_prefab"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
parent: "root"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
slice9 {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 0.0
|
||||||
|
}
|
||||||
|
clipping_mode: CLIPPING_MODE_NONE
|
||||||
|
clipping_visible: true
|
||||||
|
clipping_inverted: false
|
||||||
|
alpha: 1.0
|
||||||
|
template_node_child: false
|
||||||
|
size_mode: SIZE_MODE_AUTO
|
||||||
|
custom_type: 0
|
||||||
|
enabled: true
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
material: "/builtins/materials/gui.material"
|
||||||
|
adjust_reference: ADJUST_REFERENCE_PARENT
|
||||||
|
max_nodes: 512
|
85
druid/custom/rich_text/rich_text.lua
Normal file
85
druid/custom/rich_text/rich_text.lua
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
local component = require("druid.component")
|
||||||
|
local rich_text = require("druid.custom.rich_text.rich_text.richtext")
|
||||||
|
|
||||||
|
local RichText = component.create("rich_text")
|
||||||
|
|
||||||
|
local SCHEME = {
|
||||||
|
ROOT = "root",
|
||||||
|
TEXT_PREFAB = "text_prefab",
|
||||||
|
ICON_PREFAB = "icon_prefab"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local ALIGN_MAP = {
|
||||||
|
[gui.PIVOT_CENTER] = { rich_text.ALIGN_CENTER, rich_text.VALIGN_MIDDLE },
|
||||||
|
[gui.PIVOT_N] = { rich_text.ALIGN_CENTER, rich_text.VALIGN_TOP },
|
||||||
|
[gui.PIVOT_S] = { rich_text.ALIGN_CENTER, rich_text.VALIGN_BOTTOM },
|
||||||
|
[gui.PIVOT_NE] = { rich_text.ALIGN_RIGHT, rich_text.VALIGN_TOP },
|
||||||
|
[gui.PIVOT_E] = { rich_text.ALIGN_RIGHT, rich_text.VALIGN_MIDDLE },
|
||||||
|
[gui.PIVOT_SE] = { rich_text.ALIGN_RIGHT, rich_text.VALIGN_BOTTOM },
|
||||||
|
[gui.PIVOT_SW] = { rich_text.ALIGN_LEFT, rich_text.VALIGN_BOTTOM },
|
||||||
|
[gui.PIVOT_W] = { rich_text.ALIGN_LEFT, rich_text.VALIGN_MIDDLE },
|
||||||
|
[gui.PIVOT_NW] = { rich_text.ALIGN_LEFT, rich_text.VALIGN_TOP },
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function RichText:init(template, nodes)
|
||||||
|
self:set_template(template)
|
||||||
|
self:set_nodes(nodes)
|
||||||
|
self.root = self:get_node(SCHEME.ROOT)
|
||||||
|
self.druid = self:get_druid()
|
||||||
|
|
||||||
|
self.text_prefab = self:get_node(SCHEME.TEXT_PREFAB)
|
||||||
|
self.icon_prefab = self:get_node(SCHEME.ICON_PREFAB)
|
||||||
|
|
||||||
|
gui.set_enabled(self.text_prefab, false)
|
||||||
|
gui.set_enabled(self.icon_prefab, false)
|
||||||
|
|
||||||
|
self._text_font = gui.get_font(self.text_prefab)
|
||||||
|
self._settings = self:_get_settings()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function RichText:set_text(text)
|
||||||
|
self:_clean_words()
|
||||||
|
pprint(self._settings)
|
||||||
|
local words, metrics = rich_text.create(text, self._text_font, self._settings)
|
||||||
|
|
||||||
|
self._words = words
|
||||||
|
self._metrics = metrics
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function RichText:on_remove()
|
||||||
|
self:_clean_words()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function RichText:_get_settings()
|
||||||
|
local root_size = gui.get_size(self.root)
|
||||||
|
local anchor = gui.get_pivot(self.root)
|
||||||
|
pprint(ALIGN_MAP[anchor])
|
||||||
|
return {
|
||||||
|
width = root_size.x,
|
||||||
|
parent = self.root,
|
||||||
|
color = gui.get_color(self.text_prefab),
|
||||||
|
shadow = gui.get_shadow(self.text_prefab),
|
||||||
|
outline = gui.get_outline(self.text_prefab),
|
||||||
|
align = ALIGN_MAP[anchor][1],
|
||||||
|
valign = ALIGN_MAP[anchor][2],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function RichText:_clean_words()
|
||||||
|
if not self._words then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
rich_text.remove(self._words)
|
||||||
|
self._words = nil
|
||||||
|
self._metrics = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return RichText
|
70
druid/custom/rich_text/rich_text/color.lua
Normal file
70
druid/custom/rich_text/rich_text/color.lua
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
-- Source: https://github.com/britzl/defold-richtext version 5.19.0
|
||||||
|
-- Author: Britzl
|
||||||
|
-- Modified by: Insality
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.parse_hex(hex)
|
||||||
|
local r,g,b,a = hex:match("#?(%x%x)(%x%x)(%x%x)(%x?%x?)")
|
||||||
|
if a == "" then a = "ff" end
|
||||||
|
if r and g and b and a then
|
||||||
|
return vmath.vector4(
|
||||||
|
tonumber(r, 16) / 255,
|
||||||
|
tonumber(g, 16) / 255,
|
||||||
|
tonumber(b, 16) / 255,
|
||||||
|
tonumber(a, 16) / 255)
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function M.parse_decimal(dec)
|
||||||
|
local r,g,b,a = dec:match("(%d*%.?%d*),(%d*%.?%d*),(%d*%.?%d*),(%d*%.?%d*)")
|
||||||
|
if r and g and b and a then
|
||||||
|
return vmath.vector4(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function M.add(name, color)
|
||||||
|
if type(color) == "string" then
|
||||||
|
color = M.parse_hex(color) or M.parse_decimal(color)
|
||||||
|
end
|
||||||
|
assert(type(color) == "userdata" and color.x and color.y and color.z and color.w, "Unable to add color")
|
||||||
|
M.COLORS[name] = color
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
M.COLORS = {
|
||||||
|
aqua = M.parse_hex("#00ffffff"),
|
||||||
|
black = M.parse_hex("#000000ff"),
|
||||||
|
blue = M.parse_hex("#0000ffff"),
|
||||||
|
brown = M.parse_hex("#a52a2aff"),
|
||||||
|
cyan = M.parse_hex("#00ffffff"),
|
||||||
|
darkblue = M.parse_hex("#0000a0ff"),
|
||||||
|
fuchsia = M.parse_hex("#ff00ffff"),
|
||||||
|
green = M.parse_hex("#008000ff"),
|
||||||
|
grey = M.parse_hex("#808080ff"),
|
||||||
|
lightblue = M.parse_hex("#add8e6ff"),
|
||||||
|
lime = M.parse_hex("#00ff00ff"),
|
||||||
|
magenta = M.parse_hex("#ff00ffff"),
|
||||||
|
maroon = M.parse_hex("#800000ff"),
|
||||||
|
navy = M.parse_hex("#000080ff"),
|
||||||
|
olive = M.parse_hex("#808000ff"),
|
||||||
|
orange = M.parse_hex("#ffa500ff"),
|
||||||
|
purple = M.parse_hex("#800080ff"),
|
||||||
|
red = M.parse_hex("#ff0000ff"),
|
||||||
|
silver = M.parse_hex("#c0c0c0ff"),
|
||||||
|
teal = M.parse_hex("#008080ff"),
|
||||||
|
white = M.parse_hex("#ffffffff"),
|
||||||
|
yellow = M.parse_hex("#ffff00ff"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function M.parse(c)
|
||||||
|
return M.COLORS[c] or M.parse_hex(c) or M.parse_decimal(c)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return M
|
203
druid/custom/rich_text/rich_text/parse.lua
Executable file
203
druid/custom/rich_text/rich_text/parse.lua
Executable file
@ -0,0 +1,203 @@
|
|||||||
|
-- Source: https://github.com/britzl/defold-richtext version 5.19.0
|
||||||
|
-- Author: Britzl
|
||||||
|
-- Modified by: Insality
|
||||||
|
|
||||||
|
local utf8 = require("druid.system.utf8")
|
||||||
|
local tags = require("druid.custom.rich_text.rich_text.tags")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local function parse_tag(tag, params)
|
||||||
|
local settings = { tags = { [tag] = params }, tag = tag }
|
||||||
|
if not tags.apply(tag, params, settings) then
|
||||||
|
settings[tag] = params
|
||||||
|
end
|
||||||
|
|
||||||
|
return settings
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- add a single word to the list of words
|
||||||
|
local function add_word(text, settings, words)
|
||||||
|
-- handle HTML entities
|
||||||
|
text = text:gsub("<", "<"):gsub(">", ">"):gsub(" ", " ")
|
||||||
|
|
||||||
|
local data = { text = text }
|
||||||
|
for k,v in pairs(settings) do
|
||||||
|
data[k] = v
|
||||||
|
end
|
||||||
|
words[#words + 1] = data
|
||||||
|
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)
|
||||||
|
else
|
||||||
|
local wi = #words
|
||||||
|
for word in trimmed_text:gmatch("%S+") do
|
||||||
|
add_word(word .. " ", settings, words)
|
||||||
|
end
|
||||||
|
local first = words[wi + 1]
|
||||||
|
first.text = ws_start .. first.text
|
||||||
|
local last = words[#words]
|
||||||
|
last.text = utf8.sub(last.text, 1, utf8.len(last.text) - 1) .. ws_end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- split text
|
||||||
|
-- split by lines first
|
||||||
|
local function split_text(text, settings, words)
|
||||||
|
assert(text)
|
||||||
|
assert(settings)
|
||||||
|
assert(words)
|
||||||
|
-- special treatment of empty text with a linebreak <br/>
|
||||||
|
if text == "" and settings.linebreak then
|
||||||
|
add_word(text, settings, words)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- we don't want to deal with \r\n, remove all \r
|
||||||
|
text = text:gsub("\r", "")
|
||||||
|
|
||||||
|
-- the Lua pattern expects the text to have a linebreak at the end
|
||||||
|
local added_linebreak = false
|
||||||
|
if text:sub(-1)~="\n" then
|
||||||
|
added_linebreak = true
|
||||||
|
text = text .. "\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- split into lines
|
||||||
|
for line in text:gmatch("(.-)\n") do
|
||||||
|
split_line(line, settings, words)
|
||||||
|
-- flag last word of a line as having a linebreak
|
||||||
|
local last = words[#words]
|
||||||
|
last.linebreak = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- remove the last linebreak if we manually added it above
|
||||||
|
if added_linebreak then
|
||||||
|
local last = words[#words]
|
||||||
|
last.linebreak = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Merge one tag into another
|
||||||
|
local function merge_tags(dst, src)
|
||||||
|
for k,v in pairs(src) do
|
||||||
|
if k ~= "tags" then
|
||||||
|
dst[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for tag,params in pairs(src.tags or {}) do
|
||||||
|
dst.tags[tag] = (params == "") and true or params
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Parse the text into individual words
|
||||||
|
-- @param text The text to parse
|
||||||
|
-- @param default_settings Default settings for each word
|
||||||
|
-- @return List of all words
|
||||||
|
function M.parse(text, default_settings)
|
||||||
|
assert(text)
|
||||||
|
assert(default_settings)
|
||||||
|
|
||||||
|
text = text:gsub("&zwsp;", "<zwsp>\226\128\139</zwsp>")
|
||||||
|
local all_words = {}
|
||||||
|
local open_tags = {}
|
||||||
|
while true do
|
||||||
|
-- merge list of word settings from defaults and all open tags
|
||||||
|
local word_settings = { tags = {}}
|
||||||
|
merge_tags(word_settings, default_settings)
|
||||||
|
for _,open_tag in ipairs(open_tags) do
|
||||||
|
merge_tags(word_settings, open_tag)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- find next tag, with the text before and after the tag
|
||||||
|
local before_tag, tag, after_tag = text:match("(.-)(</?%S->)(.*)")
|
||||||
|
|
||||||
|
-- no more tags, split and add rest of the text
|
||||||
|
if not before_tag or not tag or not after_tag then
|
||||||
|
if text ~= "" then
|
||||||
|
split_text(text, word_settings, all_words)
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- split and add text before the encountered tag
|
||||||
|
if before_tag ~= "" then
|
||||||
|
split_text(before_tag, word_settings, all_words)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- parse the tag, split into name and optional parameters
|
||||||
|
local endtag, name, params, empty = tag:match("<(/?)(%a+)=?(%S-)(/?)>")
|
||||||
|
|
||||||
|
local is_endtag = endtag == "/"
|
||||||
|
local is_empty = empty == "/"
|
||||||
|
if is_empty then
|
||||||
|
-- empty tag, ie tag without content
|
||||||
|
-- example <br/> and <img=texture:image/>
|
||||||
|
local empty_tag_settings = parse_tag(name, params)
|
||||||
|
merge_tags(empty_tag_settings, word_settings)
|
||||||
|
add_word("", empty_tag_settings, all_words)
|
||||||
|
elseif not is_endtag then
|
||||||
|
if name == "repeat" then
|
||||||
|
local text_to_repeat = after_tag:match("(.-)</repeat>")
|
||||||
|
local repetitions = tonumber(params)
|
||||||
|
if repetitions > 1 then
|
||||||
|
after_tag = text_to_repeat:rep(repetitions - 1) .. after_tag
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- open tag - parse and add it
|
||||||
|
local tag_settings = parse_tag(name, params)
|
||||||
|
open_tags[#open_tags + 1] = tag_settings
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if name ~= "repeat" then
|
||||||
|
-- end tag - remove it from the list of open tags
|
||||||
|
local found = false
|
||||||
|
for i=#open_tags,1,-1 do
|
||||||
|
if open_tags[i].tag == name then
|
||||||
|
table.remove(open_tags, i)
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not found then print(("Found end tag '%s' without matching start tag"):format(name)) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if name == "p" then
|
||||||
|
local last_word = all_words[#all_words]
|
||||||
|
if last_word then
|
||||||
|
if not is_endtag then
|
||||||
|
last_word.linebreak = true
|
||||||
|
end
|
||||||
|
if is_endtag or is_empty then
|
||||||
|
last_word.paragraph_end = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- parse text after the tag on the next iteration
|
||||||
|
text = after_tag
|
||||||
|
end
|
||||||
|
return all_words
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the length of a text, excluding any tags (except image and spine tags)
|
||||||
|
function M.length(text)
|
||||||
|
return utf8.len(text:gsub("<img.-/>", " "):gsub("<spine.-/>", " "):gsub("<.->", ""))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return M
|
700
druid/custom/rich_text/rich_text/richtext.lua
Executable file
700
druid/custom/rich_text/rich_text/richtext.lua
Executable file
@ -0,0 +1,700 @@
|
|||||||
|
-- Source: https://github.com/britzl/defold-richtext version 5.19.0
|
||||||
|
-- Author: Britzl
|
||||||
|
-- Modified by: Insality
|
||||||
|
|
||||||
|
local parser = require("druid.custom.rich_text.rich_text.parse")
|
||||||
|
local utf8 = require("druid.system.utf8")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
M.ALIGN_CENTER = hash("ALIGN_CENTER")
|
||||||
|
M.ALIGN_LEFT = hash("ALIGN_LEFT")
|
||||||
|
M.ALIGN_RIGHT = hash("ALIGN_RIGHT")
|
||||||
|
M.ALIGN_JUSTIFY = hash("ALIGN_JUSTIFY")
|
||||||
|
|
||||||
|
M.VALIGN_TOP = hash("VALIGN_TOP")
|
||||||
|
M.VALIGN_MIDDLE = hash("VALIGN_MIDDLE")
|
||||||
|
M.VALIGN_BOTTOM = hash("VALIGN_BOTTOM")
|
||||||
|
|
||||||
|
|
||||||
|
local V4_ZERO = vmath.vector4(0)
|
||||||
|
local V4_ONE = vmath.vector4(1)
|
||||||
|
local V3_ZERO = vmath.vector3(0)
|
||||||
|
local V3_ONE = vmath.vector3(1)
|
||||||
|
|
||||||
|
local id_counter = 0
|
||||||
|
|
||||||
|
local function new_id(prefix)
|
||||||
|
id_counter = id_counter + 1
|
||||||
|
return hash((prefix or "") .. tostring(id_counter))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function round(v)
|
||||||
|
if type(v) == "number" then
|
||||||
|
return math.floor(v + 0.5)
|
||||||
|
else
|
||||||
|
return vmath.vector3(math.floor(v.x + 0.5), math.floor(v.y + 0.5), math.floor(v.z + 0.5))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function deepcopy(orig)
|
||||||
|
local orig_type = type(orig)
|
||||||
|
local copy
|
||||||
|
if orig_type == 'table' then
|
||||||
|
copy = {}
|
||||||
|
for orig_key, orig_value in next, orig, nil do
|
||||||
|
copy[deepcopy(orig_key)] = deepcopy(orig_value)
|
||||||
|
end
|
||||||
|
else -- number, string, boolean, etc
|
||||||
|
copy = orig
|
||||||
|
end
|
||||||
|
return copy
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
font = font_settings.bold_italic
|
||||||
|
end
|
||||||
|
if not font and word.bold then
|
||||||
|
font = font_settings.bold
|
||||||
|
end
|
||||||
|
if not font and word.italic then
|
||||||
|
font = font_settings.italic
|
||||||
|
end
|
||||||
|
if not font then
|
||||||
|
font = font_settings.regular
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not font then
|
||||||
|
font = word.font
|
||||||
|
end
|
||||||
|
return font
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function get_layer(word, layers)
|
||||||
|
local node = word.node
|
||||||
|
if word.image then
|
||||||
|
return layers.images[gui.get_texture(node)]
|
||||||
|
elseif word.spine then
|
||||||
|
return layers.spinescenes[gui.get_spine_scene(node)]
|
||||||
|
end
|
||||||
|
return layers.fonts[gui.get_font(node)]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- compare two words and check that they have the same size, color, font and tags
|
||||||
|
local function compare_words(one, two)
|
||||||
|
if one == nil
|
||||||
|
or two == nil
|
||||||
|
or one.size ~= two.size
|
||||||
|
or one.color ~= two.color
|
||||||
|
or one.shadow ~= two.shadow
|
||||||
|
or one.outline ~= two.outline
|
||||||
|
or one.font ~= two.font then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local one_tags, two_tags = one.tags, two.tags
|
||||||
|
if one_tags == two_tags then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
if one_tags == nil or two_tags == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
for k, v in pairs(one_tags) do
|
||||||
|
if two_tags[k] ~= v then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for k, v in pairs(two_tags) do
|
||||||
|
if one_tags[k] ~= v then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
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.align == M.ALIGN_RIGHT then
|
||||||
|
position.x = position.x - line_width
|
||||||
|
elseif settings.align == M.ALIGN_CENTER then
|
||||||
|
position.x = position.x - line_width / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
local spacing = 0
|
||||||
|
if settings.align == M.ALIGN_JUSTIFY then
|
||||||
|
local words_width = 0
|
||||||
|
local word_count = 0
|
||||||
|
for i=1,#words do
|
||||||
|
local word = words[i]
|
||||||
|
if word.metrics.total_width > 0 then
|
||||||
|
words_width = words_width + word.metrics.total_width
|
||||||
|
word_count = word_count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if word_count > 1 then
|
||||||
|
spacing = (settings.width - words_width) / (word_count - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i=1,#words do
|
||||||
|
local word = words[i]
|
||||||
|
-- align spine animations to bottom of line since
|
||||||
|
-- spine animations ignore pivot (always PIVOT_S)
|
||||||
|
if word.spine then
|
||||||
|
position.y = position.y - line_height
|
||||||
|
gui.set_position(word.node, position)
|
||||||
|
position.y = position.y + line_height
|
||||||
|
elseif word.image and settings.image_pixel_grid_snap then
|
||||||
|
gui.set_position(word.node, round(position))
|
||||||
|
else
|
||||||
|
gui.set_position(word.node, position)
|
||||||
|
end
|
||||||
|
position.x = position.x + word.metrics.total_width + spacing
|
||||||
|
words[i] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the length of a text ignoring any tags except image tags
|
||||||
|
-- which are treated as having a length of 1
|
||||||
|
-- @param text String with text or a list of words (from richtext.create)
|
||||||
|
-- @return Length of text
|
||||||
|
function M.length(text)
|
||||||
|
assert(text)
|
||||||
|
if type(text) == "string" then
|
||||||
|
return parser.length(text)
|
||||||
|
else
|
||||||
|
local count = 0
|
||||||
|
for i=1,#text do
|
||||||
|
local word = text[i]
|
||||||
|
local is_text_node = not word.image and not word.spine
|
||||||
|
count = count + (is_text_node and utf8.len(word.text) or 1)
|
||||||
|
end
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local size_vector = vmath.vector3()
|
||||||
|
local function create_box_node(word)
|
||||||
|
local node = gui.new_box_node(V3_ZERO, V3_ZERO)
|
||||||
|
local word_image = word.image
|
||||||
|
local image_width = word_image.width
|
||||||
|
local image_height = word_image.height
|
||||||
|
gui.set_id(node, new_id("box"))
|
||||||
|
if image_width then
|
||||||
|
gui.set_size_mode(node, gui.SIZE_MODE_MANUAL)
|
||||||
|
size_vector.x = image_width
|
||||||
|
size_vector.y = image_height
|
||||||
|
size_vector.z = 0
|
||||||
|
gui.set_size(node, size_vector)
|
||||||
|
else
|
||||||
|
gui.set_size_mode(node, gui.SIZE_MODE_AUTO)
|
||||||
|
end
|
||||||
|
gui.set_texture(node, word.image.texture)
|
||||||
|
local word_size = word.size
|
||||||
|
size_vector.x = word_size
|
||||||
|
size_vector.y = word_size
|
||||||
|
size_vector.z = word_size
|
||||||
|
gui.set_scale(node, size_vector)
|
||||||
|
|
||||||
|
gui.play_flipbook(node, hash(word.image.anim))
|
||||||
|
|
||||||
|
-- get metrics of node based on image size
|
||||||
|
local size = gui.get_size(node)
|
||||||
|
local metrics = {}
|
||||||
|
metrics.total_width = size.x * word.size
|
||||||
|
metrics.width = size.x * word.size
|
||||||
|
metrics.height = size.y * word.size
|
||||||
|
return node, metrics
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function create_spine_node(word)
|
||||||
|
local node = gui.new_spine_node(V3_ZERO, word.spine.scene)
|
||||||
|
gui.set_id(node, new_id("spine"))
|
||||||
|
gui.set_size_mode(node, gui.SIZE_MODE_AUTO)
|
||||||
|
gui.set_scale(node, vmath.vector3(word.size))
|
||||||
|
gui.play_spine_anim(node, word.spine.anim, gui.PLAYBACK_LOOP_FORWARD)
|
||||||
|
|
||||||
|
local size = gui.get_size(node)
|
||||||
|
local metrics = {}
|
||||||
|
metrics.total_width = size.x
|
||||||
|
metrics.width = size.x
|
||||||
|
metrics.height = size.y
|
||||||
|
return node, metrics
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function get_text_metrics(word, font, text)
|
||||||
|
text = text or word.text
|
||||||
|
font = font or word.font
|
||||||
|
|
||||||
|
local metrics
|
||||||
|
if utf8.len(text) == 0 then
|
||||||
|
metrics = gui.get_text_metrics(font, "|")
|
||||||
|
metrics.width = 0
|
||||||
|
metrics.total_width = 0
|
||||||
|
metrics.height = metrics.height * word.size
|
||||||
|
else
|
||||||
|
metrics = gui.get_text_metrics(font, text)
|
||||||
|
metrics.width = metrics.width * word.size
|
||||||
|
metrics.height = metrics.height * word.size
|
||||||
|
metrics.total_width = metrics.width
|
||||||
|
end
|
||||||
|
return metrics
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function create_text_node(word, font, metrics)
|
||||||
|
local node = gui.new_text_node(V3_ZERO, word.text)
|
||||||
|
gui.set_id(node, new_id("textnode"))
|
||||||
|
gui.set_font(node, font)
|
||||||
|
gui.set_color(node, word.color)
|
||||||
|
if word.shadow then gui.set_shadow(node, word.shadow) end
|
||||||
|
if word.outline then gui.set_outline(node, word.outline) end
|
||||||
|
gui.set_scale(node, V3_ONE * word.size)
|
||||||
|
|
||||||
|
metrics = metrics or get_text_metrics(word, font)
|
||||||
|
gui.set_size_mode(node, gui.SIZE_MODE_MANUAL)
|
||||||
|
gui.set_size(node, vmath.vector3(metrics.width, metrics.height, 0))
|
||||||
|
return node, metrics
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function combine_node(previous_word, word, metrics)
|
||||||
|
local text = previous_word.text .. word.text
|
||||||
|
previous_word.text = text
|
||||||
|
previous_word.metrics = metrics
|
||||||
|
gui.set_size(previous_word.node, vmath.vector3(metrics.width, metrics.height, 0))
|
||||||
|
gui.set_text(previous_word.node, text)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function create_node(word, parent, font, node, metrics)
|
||||||
|
if word.image then
|
||||||
|
if not node then
|
||||||
|
node, metrics = create_box_node(word)
|
||||||
|
end
|
||||||
|
elseif word.spine then
|
||||||
|
if not node then
|
||||||
|
node, metrics = create_spine_node(word)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
node, metrics = create_text_node(word, font, metrics)
|
||||||
|
end
|
||||||
|
gui.set_parent(node, parent)
|
||||||
|
gui.set_inherit_alpha(node, true)
|
||||||
|
return node, metrics
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function measure_node(word, font, previous_word)
|
||||||
|
local node, metrics, combined_metrics
|
||||||
|
if word.image then
|
||||||
|
node, metrics = create_box_node(word)
|
||||||
|
elseif word.spine then
|
||||||
|
node, metrics = create_spine_node(word)
|
||||||
|
else
|
||||||
|
metrics = get_text_metrics(word, font)
|
||||||
|
if previous_word then
|
||||||
|
combined_metrics = get_text_metrics(word, font, previous_word.text .. word.text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return metrics, combined_metrics, node
|
||||||
|
end
|
||||||
|
|
||||||
|
local function split_word(word, font, max_width)
|
||||||
|
local one = deepcopy(word)
|
||||||
|
local two = deepcopy(word)
|
||||||
|
local text = word.text
|
||||||
|
local metrics = get_text_metrics(one, font)
|
||||||
|
local char_count = utf8.len(text)
|
||||||
|
local split_index = math.floor(char_count * (max_width / metrics.total_width))
|
||||||
|
local rest = ""
|
||||||
|
while split_index > 1 do
|
||||||
|
one.text = utf8.sub(text, 1, split_index)
|
||||||
|
one.linebreak = true
|
||||||
|
metrics = get_text_metrics(one, font)
|
||||||
|
if metrics.width <= max_width then
|
||||||
|
rest = utf8.sub(text, split_index + 1)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
split_index = split_index - 1
|
||||||
|
end
|
||||||
|
two.text = rest
|
||||||
|
return one, two
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Create rich text gui nodes from text
|
||||||
|
-- @param text The text to create rich text nodes from
|
||||||
|
-- @param font The default font
|
||||||
|
-- @param settings Optional settings table (refer to documentation for details)
|
||||||
|
-- @return words
|
||||||
|
-- @return metrics
|
||||||
|
function M.create(text, font, settings)
|
||||||
|
assert(text, "You must provide a text")
|
||||||
|
assert(font, "You must provide a font")
|
||||||
|
settings = settings or {}
|
||||||
|
settings.align = settings.align or M.ALIGN_LEFT
|
||||||
|
settings.valign = settings.valign or M.VALIGN_TOP
|
||||||
|
settings.size = settings.size or 1
|
||||||
|
settings.fonts = settings.fonts or {}
|
||||||
|
settings.fonts[font] = settings.fonts[font] or { regular = hash(font) }
|
||||||
|
settings.layers = settings.layers or {}
|
||||||
|
settings.layers.fonts = settings.layers.fonts or {}
|
||||||
|
settings.layers.images = settings.layers.images or {}
|
||||||
|
settings.layers.spinescenes = settings.layers.spinescenes or {}
|
||||||
|
settings.color = settings.color or V4_ONE
|
||||||
|
settings.shadow = settings.shadow or V4_ZERO
|
||||||
|
settings.outline = settings.outline or V4_ZERO
|
||||||
|
settings.position = settings.position or V3_ZERO
|
||||||
|
settings.line_spacing = settings.line_spacing or 1
|
||||||
|
settings.paragraph_spacing = settings.paragraph_spacing or 0.5
|
||||||
|
settings.image_pixel_grid_snap = settings.image_pixel_grid_snap or false
|
||||||
|
settings.combine_words = settings.combine_words or false
|
||||||
|
if settings.align == M.ALIGN_JUSTIFY and not settings.width then
|
||||||
|
error("Width must be specified if text should be justified")
|
||||||
|
end
|
||||||
|
|
||||||
|
local line_increment_before = 0
|
||||||
|
local line_increment_after = 1
|
||||||
|
local pivot = gui.PIVOT_NW
|
||||||
|
if settings.valign == M.VALIGN_MIDDLE then
|
||||||
|
line_increment_before = 0.5
|
||||||
|
line_increment_after = 0.5
|
||||||
|
pivot = gui.PIVOT_W
|
||||||
|
elseif settings.valign == M.VALIGN_BOTTOM then
|
||||||
|
line_increment_before = 1
|
||||||
|
line_increment_after = 0
|
||||||
|
pivot = gui.PIVOT_SW
|
||||||
|
end
|
||||||
|
|
||||||
|
-- default settings for a word
|
||||||
|
-- will be assigned to each word unless tags override the values
|
||||||
|
local word_settings = {
|
||||||
|
color = settings.color,
|
||||||
|
shadow = settings.shadow,
|
||||||
|
outline = settings.outline,
|
||||||
|
font = font,
|
||||||
|
size = settings.size
|
||||||
|
}
|
||||||
|
local words = parser.parse(text, word_settings)
|
||||||
|
local text_metrics = {
|
||||||
|
width = 0,
|
||||||
|
height = 0,
|
||||||
|
char_count = 0,
|
||||||
|
img_count = 0,
|
||||||
|
spine_count = 0,
|
||||||
|
}
|
||||||
|
local line_words = {}
|
||||||
|
local line_width = 0
|
||||||
|
local line_height = 0
|
||||||
|
local paragraph_spacing = 0
|
||||||
|
local position = vmath.vector3(settings.position)
|
||||||
|
local word_count = #words
|
||||||
|
local i = 1
|
||||||
|
repeat
|
||||||
|
local word = words[i]
|
||||||
|
if word.image then
|
||||||
|
text_metrics.img_count = text_metrics.img_count + 1
|
||||||
|
elseif word.spine then
|
||||||
|
text_metrics.spine_count = text_metrics.spine_count + 1
|
||||||
|
else
|
||||||
|
text_metrics.char_count = text_metrics.char_count + parser.length(word.text)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get font to use based on word tags
|
||||||
|
local font_for_word = get_font(word, settings.fonts)
|
||||||
|
|
||||||
|
-- get the previous word, so we can combine
|
||||||
|
local previous_word
|
||||||
|
if settings.combine_words then
|
||||||
|
previous_word = line_words[#line_words]
|
||||||
|
if not compare_words(previous_word, word) then
|
||||||
|
previous_word = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get metrics first, without creating the node (if possible)
|
||||||
|
local word_metrics, combined_metrics, node = measure_node(word, font_for_word, previous_word)
|
||||||
|
local should_create_node = true
|
||||||
|
|
||||||
|
-- check if the line overflows due to this word
|
||||||
|
local overflow = false
|
||||||
|
if settings.width then
|
||||||
|
if combined_metrics then
|
||||||
|
overflow = (line_width - previous_word.metrics.total_width + combined_metrics.width) > settings.width
|
||||||
|
else
|
||||||
|
overflow = (line_width + word_metrics.width) > settings.width
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if we overflow and the word is longer than a full line we
|
||||||
|
-- split the word and add the first part to the current line
|
||||||
|
if overflow and word.text and word_metrics.width > settings.width then
|
||||||
|
local remaining_width = settings.width - line_width
|
||||||
|
local one, two = split_word(word, font_for_word, remaining_width)
|
||||||
|
word_metrics, combined_metrics, node = measure_node(one, font_for_word, previous_word)
|
||||||
|
words[i] = one
|
||||||
|
word = one
|
||||||
|
table.insert(words, i + 1, two)
|
||||||
|
word_count = word_count + 1
|
||||||
|
overflow = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if overflow and not word.nobr then
|
||||||
|
-- overflow, position the words that fit on the line
|
||||||
|
text_metrics.height = text_metrics.height + (line_height * line_increment_before * settings.line_spacing)
|
||||||
|
position.x = settings.position.x
|
||||||
|
position.y = settings.position.y - text_metrics.height
|
||||||
|
position_words(line_words, line_width, line_height, position, settings)
|
||||||
|
|
||||||
|
-- add the word that didn't fit to the next line instead
|
||||||
|
line_words[#line_words + 1] = word
|
||||||
|
|
||||||
|
-- update text metrics
|
||||||
|
text_metrics.width = math.max(text_metrics.width, line_width)
|
||||||
|
text_metrics.height = text_metrics.height + (line_height * line_increment_after * settings.line_spacing) + paragraph_spacing
|
||||||
|
line_width = word_metrics.total_width
|
||||||
|
line_height = word_metrics.height
|
||||||
|
paragraph_spacing = 0
|
||||||
|
else
|
||||||
|
-- the word fits on the line, add it and update text metrics
|
||||||
|
if combined_metrics then
|
||||||
|
line_width = line_width - previous_word.metrics.total_width + combined_metrics.total_width
|
||||||
|
line_height = math.max(line_height, combined_metrics.height)
|
||||||
|
combine_node(previous_word, word, combined_metrics)
|
||||||
|
should_create_node = false
|
||||||
|
else
|
||||||
|
line_width = line_width + word_metrics.total_width
|
||||||
|
line_height = math.max(line_height, word_metrics.height)
|
||||||
|
line_words[#line_words + 1] = word
|
||||||
|
end
|
||||||
|
text_metrics.width = math.max(text_metrics.width, line_width)
|
||||||
|
end
|
||||||
|
|
||||||
|
if should_create_node then
|
||||||
|
word.node, word.metrics = create_node(word, settings.parent, font_for_word, node, word_metrics)
|
||||||
|
gui.set_pivot(word.node, pivot)
|
||||||
|
|
||||||
|
-- assign layer
|
||||||
|
local layer = get_layer(word, settings.layers)
|
||||||
|
if layer then
|
||||||
|
gui.set_layer(word.node, layer)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- queue this word for deletion
|
||||||
|
word.delete = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if word.paragraph_end then
|
||||||
|
local paragraph = word.paragraph
|
||||||
|
if paragraph then
|
||||||
|
paragraph_spacing = math.max(
|
||||||
|
paragraph_spacing,
|
||||||
|
line_height * (paragraph == true and settings.paragraph_spacing or paragraph)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle line break
|
||||||
|
if word.linebreak then
|
||||||
|
-- position all words on the line up until the linebreak
|
||||||
|
text_metrics.height = text_metrics.height + (line_height * line_increment_before * settings.line_spacing)
|
||||||
|
position.x = settings.position.x
|
||||||
|
position.y = settings.position.y - text_metrics.height
|
||||||
|
position_words(line_words, line_width, line_height, position, settings)
|
||||||
|
|
||||||
|
-- update text metrics
|
||||||
|
text_metrics.height = text_metrics.height + (line_height * line_increment_after * settings.line_spacing) + paragraph_spacing
|
||||||
|
line_height = word_metrics.height
|
||||||
|
line_width = 0
|
||||||
|
paragraph_spacing = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
until i > word_count
|
||||||
|
|
||||||
|
-- position remaining words
|
||||||
|
if #line_words > 0 then
|
||||||
|
text_metrics.height = text_metrics.height + (line_height * line_increment_before * settings.line_spacing)
|
||||||
|
position.x = settings.position.x
|
||||||
|
position.y = settings.position.y - text_metrics.height
|
||||||
|
position_words(line_words, line_width, line_height, position, settings)
|
||||||
|
text_metrics.height = text_metrics.height + (line_height * line_increment_after * settings.line_spacing)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- compact words table
|
||||||
|
local j = 1
|
||||||
|
for i = 1, word_count do
|
||||||
|
local word = words[i]
|
||||||
|
if not word.delete then
|
||||||
|
words[j] = word
|
||||||
|
j = j + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i = j, word_count do
|
||||||
|
words[i] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return words, text_metrics
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Detected click/touch events on words with an anchor tag
|
||||||
|
-- These words act as "hyperlinks" and will generate a message when clicked
|
||||||
|
-- @param words Words to search for anchor tags
|
||||||
|
-- @param action The action table from on_input
|
||||||
|
-- @return true if a word was clicked, otherwise false
|
||||||
|
function M.on_click(words, action)
|
||||||
|
for i=1,#words do
|
||||||
|
local word = words[i]
|
||||||
|
if word.anchor and gui.pick_node(word.node, action.x, action.y) then
|
||||||
|
if word.tags and word.tags.a then
|
||||||
|
local message = {
|
||||||
|
node_id = gui.get_id(word.node),
|
||||||
|
text = word.text,
|
||||||
|
x = action.x, y = action.y,
|
||||||
|
screen_x = action.screen_x, screen_y = action.screen_y
|
||||||
|
}
|
||||||
|
msg.post("#", word.tags.a, message)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get all words with a specific tag
|
||||||
|
-- @param words The words to search (as received from richtext.create)
|
||||||
|
-- @param tag The tag to search for. Nil to search for words without a tag
|
||||||
|
-- @return Words matching the tag
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
--- Truncate a set of words such that only a specific number of characters
|
||||||
|
-- and images are visible
|
||||||
|
-- @param words List of words to truncate
|
||||||
|
-- @param length Maximum number of characters to show
|
||||||
|
-- @param options Optional table with truncate options. Available options are: words
|
||||||
|
-- @return Last visible word
|
||||||
|
function M.truncate(words, length, options)
|
||||||
|
assert(words)
|
||||||
|
assert(length)
|
||||||
|
local last_visible_word = nil
|
||||||
|
if options and options.words then
|
||||||
|
for i=1, #words do
|
||||||
|
local word = words[i]
|
||||||
|
local visible = i <= length
|
||||||
|
if visible then
|
||||||
|
last_visible_word = word
|
||||||
|
end
|
||||||
|
gui.set_enabled(word.node, visible)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local count = 0
|
||||||
|
for i=1, #words do
|
||||||
|
local word = words[i]
|
||||||
|
local is_text_node = not word.image and not word.spine
|
||||||
|
local word_length = is_text_node and utf8.len(word.text) or 1
|
||||||
|
local visible = count < length
|
||||||
|
if visible then
|
||||||
|
last_visible_word = word
|
||||||
|
end
|
||||||
|
gui.set_enabled(word.node, visible)
|
||||||
|
if count < length and is_text_node then
|
||||||
|
local text = word.text
|
||||||
|
-- partial word?
|
||||||
|
if count + word_length > length then
|
||||||
|
-- remove overflowing characters from word
|
||||||
|
local overflow = (count + word_length) - length
|
||||||
|
text = utf8.sub(word.text, 1, word_length - overflow)
|
||||||
|
end
|
||||||
|
gui.set_text(word.node, text)
|
||||||
|
word.metrics = get_text_metrics(word, word.font, text)
|
||||||
|
end
|
||||||
|
count = count + word_length
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return last_visible_word
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Split a word into it's characters
|
||||||
|
-- @param word The word to split
|
||||||
|
-- @return The individual characters
|
||||||
|
function M.characters(word)
|
||||||
|
assert(word)
|
||||||
|
|
||||||
|
local parent = gui.get_parent(word.node)
|
||||||
|
local font = gui.get_font(word.node)
|
||||||
|
local layer = gui.get_layer(word.node)
|
||||||
|
local pivot = gui.get_pivot(word.node)
|
||||||
|
|
||||||
|
local word_length = utf8.len(word.text)
|
||||||
|
|
||||||
|
-- exit early if word is a single character or empty
|
||||||
|
if word_length <= 1 then
|
||||||
|
local char = deepcopy(word)
|
||||||
|
char.node, char.metrics = create_node(char, parent, font)
|
||||||
|
gui.set_pivot(char.node, pivot)
|
||||||
|
gui.set_position(char.node, gui.get_position(word.node))
|
||||||
|
gui.set_layer(char.node, layer)
|
||||||
|
return { char }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- split word into characters
|
||||||
|
local chars = {}
|
||||||
|
local position = gui.get_position(word.node)
|
||||||
|
local position_x = position.x
|
||||||
|
|
||||||
|
for i = 1, word_length do
|
||||||
|
local char = deepcopy(word)
|
||||||
|
chars[#chars + 1] = char
|
||||||
|
char.text = utf8.sub(word.text, i, i)
|
||||||
|
char.node, char.metrics = create_node(char, parent, font)
|
||||||
|
gui.set_layer(char.node, layer)
|
||||||
|
gui.set_pivot(char.node, pivot)
|
||||||
|
|
||||||
|
local sub_metrics = get_text_metrics(word, font, utf8.sub(word.text, 1, i))
|
||||||
|
position.x = position_x + sub_metrics.width - char.metrics.width
|
||||||
|
gui.set_position(char.node, position)
|
||||||
|
end
|
||||||
|
|
||||||
|
return chars
|
||||||
|
end
|
||||||
|
|
||||||
|
---Removes the gui nodes created by rich text
|
||||||
|
function M.remove(words)
|
||||||
|
assert(words)
|
||||||
|
|
||||||
|
local num = #words
|
||||||
|
for i=1,num do
|
||||||
|
gui.delete_node(words[i].node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return M
|
127
druid/custom/rich_text/rich_text/tags.lua
Normal file
127
druid/custom/rich_text/rich_text/tags.lua
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
-- Source: https://github.com/britzl/defold-richtext version 5.19.0
|
||||||
|
-- Author: Britzl
|
||||||
|
-- Modified by: Insality
|
||||||
|
|
||||||
|
local color = require("druid.custom.rich_text.rich_text.color")
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local tags = {}
|
||||||
|
|
||||||
|
|
||||||
|
function M.apply(tag, params, settings)
|
||||||
|
local fn = tags[tag]
|
||||||
|
if not fn then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
fn(params, settings)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function M.register(tag, fn)
|
||||||
|
assert(tag, "You must provide a tag")
|
||||||
|
assert(fn, "You must provide a tag function")
|
||||||
|
tags[tag] = fn
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
M.register("color", function(params, settings)
|
||||||
|
settings.color = color.parse(params)
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
M.register("shadow", function(params, settings)
|
||||||
|
settings.shadow = color.parse(params)
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
M.register("outline", function(params, settings)
|
||||||
|
settings.outline = color.parse(params)
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
M.register("font", function(params, settings)
|
||||||
|
settings.font = params
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
M.register("size", function(params, settings)
|
||||||
|
settings.size = tonumber(params)
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
M.register("b", function(params, settings)
|
||||||
|
settings.bold = true
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
M.register("i", function(params, settings)
|
||||||
|
settings.italic = true
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
M.register("a", function(params, settings)
|
||||||
|
settings.anchor = true
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
M.register("br", function(params, settings)
|
||||||
|
settings.linebreak = true
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
M.register("nobr", function(params, settings)
|
||||||
|
settings.nobr = true
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
-- Split string at first occurrence of token
|
||||||
|
-- If the token doesn't exist the whole string is returned
|
||||||
|
-- @param s The string to split
|
||||||
|
-- @param token The token to split string on
|
||||||
|
-- @return before The string before the token or the whole string if token doesn't exist
|
||||||
|
-- @return after The string after the token or nul
|
||||||
|
local function split(s, token)
|
||||||
|
if not s then return nil, nil end
|
||||||
|
local before, after = s:match("(.-)" .. token .. "(.*)")
|
||||||
|
before = before or s
|
||||||
|
return before, after
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
M.register("img", function(params, settings)
|
||||||
|
local texture_and_anim, params = split(params, ",")
|
||||||
|
local width, height
|
||||||
|
width, params = split(params, ",")
|
||||||
|
height = split(params, ",")
|
||||||
|
local texture, anim = split(texture_and_anim, ":")
|
||||||
|
|
||||||
|
width = width and tonumber(width)
|
||||||
|
height = height and tonumber(height) or width
|
||||||
|
|
||||||
|
settings.image = {
|
||||||
|
texture = texture,
|
||||||
|
anim = anim,
|
||||||
|
width = width,
|
||||||
|
height = height
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
M.register("spine", function(params, settings)
|
||||||
|
local scene, anim = params:match("(.-):(.*)")
|
||||||
|
settings.spine = {
|
||||||
|
scene = scene,
|
||||||
|
anim = anim
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
M.register("p", function(params, settings)
|
||||||
|
settings.paragraph = tonumber(params) or true
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
return M
|
@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
local const = require("druid.const")
|
|
||||||
local Event = require("druid.event")
|
local Event = require("druid.event")
|
||||||
local settings = require("druid.system.settings")
|
local settings = require("druid.system.settings")
|
||||||
local component = require("druid.component")
|
local component = require("druid.component")
|
||||||
|
@ -16,6 +16,8 @@ embedded_instances {
|
|||||||
" z: 0.0\n"
|
" z: 0.0\n"
|
||||||
" w: 1.0\n"
|
" w: 1.0\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
""
|
""
|
||||||
position {
|
position {
|
||||||
@ -51,6 +53,8 @@ embedded_instances {
|
|||||||
" z: 0.0\n"
|
" z: 0.0\n"
|
||||||
" w: 1.0\n"
|
" w: 1.0\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
""
|
""
|
||||||
position {
|
position {
|
||||||
@ -139,6 +143,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -202,6 +208,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -265,6 +273,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -328,6 +338,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -391,6 +403,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -454,6 +468,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -517,6 +533,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -580,6 +598,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -643,6 +663,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -706,6 +728,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -769,6 +793,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -832,6 +858,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -895,6 +923,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -958,6 +988,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1021,6 +1053,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1084,6 +1118,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1147,6 +1183,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1210,6 +1248,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1273,6 +1313,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1336,6 +1378,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1399,6 +1443,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1462,6 +1508,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1525,6 +1573,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1588,6 +1638,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1651,6 +1703,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1714,6 +1768,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1777,6 +1833,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1840,6 +1898,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1903,6 +1963,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -1966,6 +2028,8 @@ embedded_instances {
|
|||||||
" value: \"true\"\n"
|
" value: \"true\"\n"
|
||||||
" type: PROPERTY_TYPE_BOOLEAN\n"
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"embedded_components {\n"
|
"embedded_components {\n"
|
||||||
" id: \"collectionfactory\"\n"
|
" id: \"collectionfactory\"\n"
|
||||||
@ -2003,3 +2067,68 @@ embedded_instances {
|
|||||||
z: 1.0
|
z: 1.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
embedded_instances {
|
||||||
|
id: "custom_rich_text"
|
||||||
|
data: "components {\n"
|
||||||
|
" id: \"screen_factory\"\n"
|
||||||
|
" component: \"/monarch/screen_factory.script\"\n"
|
||||||
|
" position {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" }\n"
|
||||||
|
" rotation {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" w: 1.0\n"
|
||||||
|
" }\n"
|
||||||
|
" properties {\n"
|
||||||
|
" id: \"screen_id\"\n"
|
||||||
|
" value: \"custom_rich_text\"\n"
|
||||||
|
" type: PROPERTY_TYPE_HASH\n"
|
||||||
|
" }\n"
|
||||||
|
" properties {\n"
|
||||||
|
" id: \"popup\"\n"
|
||||||
|
" value: \"true\"\n"
|
||||||
|
" type: PROPERTY_TYPE_BOOLEAN\n"
|
||||||
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n"
|
||||||
|
"embedded_components {\n"
|
||||||
|
" id: \"collectionfactory\"\n"
|
||||||
|
" type: \"collectionfactory\"\n"
|
||||||
|
" data: \"prototype: \\\"/example/examples/custom/rich_text/rich_text.collection\\\"\\n"
|
||||||
|
"load_dynamically: false\\n"
|
||||||
|
"\"\n"
|
||||||
|
" position {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" }\n"
|
||||||
|
" rotation {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" w: 1.0\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n"
|
||||||
|
""
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale3 {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -168,6 +168,7 @@ local function init_lobby(self)
|
|||||||
self.lobby_grid:add(get_title(self, "Custom components"))
|
self.lobby_grid:add(get_title(self, "Custom components"))
|
||||||
self.lobby_grid:add(get_button(self, "Rich Input", "custom_rich_input", "/custom/rich_input/rich_input.gui_script"))
|
self.lobby_grid:add(get_button(self, "Rich Input", "custom_rich_input", "/custom/rich_input/rich_input.gui_script"))
|
||||||
self.lobby_grid:add(get_button(self, "Pin Knob", "custom_pin_knob", "/custom/pin_knob/pin_knob.gui_script"))
|
self.lobby_grid:add(get_button(self, "Pin Knob", "custom_pin_knob", "/custom/pin_knob/pin_knob.gui_script"))
|
||||||
|
self.lobby_grid:add(get_button(self, "Rich Text", "custom_rich_text", "/custom/rich_text/rich_text.gui_script"))
|
||||||
|
|
||||||
self.lobby_grid:add(get_title(self, "System"))
|
self.lobby_grid:add(get_title(self, "System"))
|
||||||
self.lobby_grid:add(get_button_disabled(self, "Styles"))
|
self.lobby_grid:add(get_button_disabled(self, "Styles"))
|
||||||
|
39
example/examples/custom/rich_text/rich_text.collection
Normal file
39
example/examples/custom/rich_text/rich_text.collection
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
name: "rich_text"
|
||||||
|
scale_along_z: 0
|
||||||
|
embedded_instances {
|
||||||
|
id: "go"
|
||||||
|
data: "components {\n"
|
||||||
|
" id: \"rich_text\"\n"
|
||||||
|
" component: \"/example/examples/custom/rich_text/rich_text.gui\"\n"
|
||||||
|
" position {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" }\n"
|
||||||
|
" rotation {\n"
|
||||||
|
" x: 0.0\n"
|
||||||
|
" y: 0.0\n"
|
||||||
|
" z: 0.0\n"
|
||||||
|
" w: 1.0\n"
|
||||||
|
" }\n"
|
||||||
|
" property_decls {\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n"
|
||||||
|
""
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale3 {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
}
|
||||||
|
}
|
308
example/examples/custom/rich_text/rich_text.gui
Normal file
308
example/examples/custom/rich_text/rich_text.gui
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
script: "/example/examples/custom/rich_text/rich_text.gui_script"
|
||||||
|
fonts {
|
||||||
|
name: "game"
|
||||||
|
font: "/example/assets/fonts/game.font"
|
||||||
|
}
|
||||||
|
textures {
|
||||||
|
name: "kenney"
|
||||||
|
texture: "/example/assets/images/kenney.atlas"
|
||||||
|
}
|
||||||
|
background_color {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 0.0
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 300.0
|
||||||
|
y: 415.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
size {
|
||||||
|
x: 600.0
|
||||||
|
y: 830.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_BOX
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
texture: "kenney/empty"
|
||||||
|
id: "root"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
slice9 {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 0.0
|
||||||
|
}
|
||||||
|
clipping_mode: CLIPPING_MODE_NONE
|
||||||
|
clipping_visible: true
|
||||||
|
clipping_inverted: false
|
||||||
|
alpha: 1.0
|
||||||
|
template_node_child: false
|
||||||
|
size_mode: SIZE_MODE_MANUAL
|
||||||
|
custom_type: 0
|
||||||
|
enabled: true
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
size {
|
||||||
|
x: 200.0
|
||||||
|
y: 100.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_TEMPLATE
|
||||||
|
id: "rich_text"
|
||||||
|
parent: "root"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
alpha: 1.0
|
||||||
|
template: "/druid/custom/rich_text/rich_text.gui"
|
||||||
|
template_node_child: false
|
||||||
|
custom_type: 0
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
size {
|
||||||
|
x: 300.0
|
||||||
|
y: 200.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 0.4
|
||||||
|
y: 0.6
|
||||||
|
z: 0.6
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_BOX
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
texture: ""
|
||||||
|
id: "rich_text/root"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_E
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
parent: "rich_text"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
slice9 {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 0.0
|
||||||
|
}
|
||||||
|
clipping_mode: CLIPPING_MODE_NONE
|
||||||
|
clipping_visible: true
|
||||||
|
clipping_inverted: false
|
||||||
|
alpha: 1.0
|
||||||
|
overridden_fields: 5
|
||||||
|
overridden_fields: 14
|
||||||
|
overridden_fields: 46
|
||||||
|
template_node_child: true
|
||||||
|
size_mode: SIZE_MODE_MANUAL
|
||||||
|
custom_type: 0
|
||||||
|
enabled: true
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
size {
|
||||||
|
x: 200.0
|
||||||
|
y: 100.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_TEXT
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
text: "Text"
|
||||||
|
font: "game"
|
||||||
|
id: "rich_text/text_prefab"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
outline {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
shadow {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
line_break: false
|
||||||
|
parent: "rich_text/root"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
alpha: 1.0
|
||||||
|
outline_alpha: 1.0
|
||||||
|
shadow_alpha: 0.0
|
||||||
|
template_node_child: true
|
||||||
|
text_leading: 1.0
|
||||||
|
text_tracking: 0.0
|
||||||
|
custom_type: 0
|
||||||
|
enabled: true
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
position {
|
||||||
|
x: 77.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
rotation {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
scale {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
size {
|
||||||
|
x: 36.0
|
||||||
|
y: 36.0
|
||||||
|
z: 0.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
color {
|
||||||
|
x: 1.0
|
||||||
|
y: 1.0
|
||||||
|
z: 1.0
|
||||||
|
w: 1.0
|
||||||
|
}
|
||||||
|
type: TYPE_BOX
|
||||||
|
blend_mode: BLEND_MODE_ALPHA
|
||||||
|
texture: "kenney/slider_move"
|
||||||
|
id: "rich_text/icon_prefab"
|
||||||
|
xanchor: XANCHOR_NONE
|
||||||
|
yanchor: YANCHOR_NONE
|
||||||
|
pivot: PIVOT_CENTER
|
||||||
|
adjust_mode: ADJUST_MODE_FIT
|
||||||
|
parent: "rich_text/root"
|
||||||
|
layer: ""
|
||||||
|
inherit_alpha: true
|
||||||
|
slice9 {
|
||||||
|
x: 0.0
|
||||||
|
y: 0.0
|
||||||
|
z: 0.0
|
||||||
|
w: 0.0
|
||||||
|
}
|
||||||
|
clipping_mode: CLIPPING_MODE_NONE
|
||||||
|
clipping_visible: true
|
||||||
|
clipping_inverted: false
|
||||||
|
alpha: 1.0
|
||||||
|
template_node_child: true
|
||||||
|
size_mode: SIZE_MODE_AUTO
|
||||||
|
custom_type: 0
|
||||||
|
enabled: true
|
||||||
|
visible: true
|
||||||
|
}
|
||||||
|
layers {
|
||||||
|
name: "image"
|
||||||
|
}
|
||||||
|
layers {
|
||||||
|
name: "text"
|
||||||
|
}
|
||||||
|
material: "/builtins/materials/gui.material"
|
||||||
|
adjust_reference: ADJUST_REFERENCE_PARENT
|
||||||
|
max_nodes: 512
|
Loading…
x
Reference in New Issue
Block a user