Update Rich text style params

This commit is contained in:
Insality 2023-08-05 19:01:06 +03:00
parent 5a0cf42e3d
commit 628723386e
8 changed files with 123 additions and 83 deletions

View File

@ -12,3 +12,4 @@ date,sha,version,build_size,build_time,platform,mode,is_cache_using,commits_coun
2023-07-05T20:19:22Z,d0062c2a78e618871ebb4c8ee66b1509b763f069,0.10.671,3292,12,x86_64-linux,headless,true,671 2023-07-05T20:19:22Z,d0062c2a78e618871ebb4c8ee66b1509b763f069,0.10.671,3292,12,x86_64-linux,headless,true,671
2023-07-13T18:37:07Z,1cbe57376397a8352bbafcc67de0b6f95ae37b35,0.10.682,2496,53,js-web,release,true,682 2023-07-13T18:37:07Z,1cbe57376397a8352bbafcc67de0b6f95ae37b35,0.10.682,2496,53,js-web,release,true,682
2023-07-13T19:31:47Z,ea185622702e6691275187741b1e2ee0c7f079ec,0.11.686,2452,51,js-web,release,true,686 2023-07-13T19:31:47Z,ea185622702e6691275187741b1e2ee0c7f079ec,0.11.686,2452,51,js-web,release,true,686
2023-07-15T12:56:18Z,556e1a9bae620e2df290b10287fa8d0f64d47293,0.11.690,2468,51,js-web,release,true,690

1 date sha version build_size build_time platform mode is_cache_using commits_count
12 2023-07-05T20:19:22Z d0062c2a78e618871ebb4c8ee66b1509b763f069 0.10.671 3292 12 x86_64-linux headless true 671
13 2023-07-13T18:37:07Z 1cbe57376397a8352bbafcc67de0b6f95ae37b35 0.10.682 2496 53 js-web release true 682
14 2023-07-13T19:31:47Z ea185622702e6691275187741b1e2ee0c7f079ec 0.11.686 2452 51 js-web release true 686
15 2023-07-15T12:56:18Z 556e1a9bae620e2df290b10287fa8d0f64d47293 0.11.690 2468 51 js-web release true 690

View File

@ -453,38 +453,52 @@ Hello! What a wonderful day for the new **Druid** update!
Alright, let's get straight to the point. Look at what I have for you! Alright, let's get straight to the point. Look at what I have for you!
**Druid Rich Text** has finally been released. The main difference from the existing **Bjorn's** Rich Text is that all visual parameters are customizable directly in the GUI. This allows you to integrate Rich Text more accurately and quickly. Additionally, this Rich Text aligns pixel perfect (well, almost) with regular GUI text node. **Druid Rich Text** has finally been released. The main difference from the existing **Bjorn's** Rich Text is that all visual parameters are customizable directly in the GUI. This allows you to integrate **Rich Text** more accurately and quickly. Additionally, this **Rich Text** aligns pixel perfect (well, [almost](https://github.com/defold/defold/issues/7197)) with regular GUI text node.
This version is the most basic one. Honestly, just wanna to publish current version for your and polish it later. This version is the most basic one. Honestly, just wanna to publish current version for your and polish it later. Read [RichText API here](https://insality.github.io/druid/modules/RichText.html)
Another addition is the ability to enable the "HTML mode" for the Button component. In this mode, the button's action occurs in the context of `user action`, allowing operations like "copy and paste text" "show the keyboard" and more. However, in this mode, the button only responds to regular clicks due to the technical implementation of it (so no double clicks or long taps). Another addition is the ability to enable the "HTML mode" for the Button component. In this mode, the button's action occurs in the context of `user action`, allowing operations like "copy and paste text" "show the keyboard" and more. However, in this mode, the button only responds to regular clicks due to the technical implementation of it (so no double clicks or long taps for this button).
The huge work was done on documentation. Now it's more clear and have more examples. All documentation now moved to the API section. The separate `componentd.md` file will be deleted soon as all documentation will be moved to the API section. The huge work was done on documentation. Now it's more clear and have more examples. All documentation now moved to the API section. The separate `componentd.md` manual will be deleted soon as all documentation will be moved to the API section.
The API section now filled with overview and usage examples. I've started with the basic modules, in future I will add more examples for all modules. The API section now filled with overview and usage examples. I've started with the basic modules, in future I will add more examples for all modules.
Also, I've added the Unit Tests. It's not cover all **Druid** code, but it's a good start! 🎉 Also, I've added the **Unit Tests**. It's not cover all **Druid** code, but it's a good start! 🎉
I have the feedback form for you, please fill it if you have a free minute. It will help me to improve **Druid** in future. %LINK HERE% I have the feedback form for you, please fill it if you have a free minute. It will help me to improve **Druid** in future. %LINK HERE%
Have a good day!
**Changelog 0.11.0** **Changelog 0.11.0**
--- ---
- **#191**: [RichText] Finally add Druid Rich Text custom component. Component is used to make formatted text in your's GUI. This Rich Text mostly adjusted visually in GUI and have almost pixel-perfect match with similar GUI text node - **#191**: [RichText] Finally add **Druid [Rich Text](https://insality.github.io/druid/modules/RichText.html)** custom component. Component is used to make formatted text in your's GUI. This Rich Text mostly adjusted visually in GUI and have almost pixel-perfect match with similar GUI text node
- **#39**: [System] Finally add Unit Tests. Yeah, it cover not all **Druid** code, but it's a good start! 🎉 - **#39**: [System] Finally add **Unit Tests**. Yeah, it cover not all **Druid** code, but it's a good start! 🎉
- **#219**: [System] UTF-8 performance optimization. Now Druid will try to use utf8 native extension over lua utf8 library if exists. - **#219**: [System] UTF-8 performance optimization. Now Druid will try to use *utf8* native extension over lua utf8 library if exists. If you wanna use native utf8, just [add the extension](https://github.com/d954mas/defold-utf8) in your `game.project` dependency.
- **#156**: [Button] Now button can work in HTML5 with html5.set_interaction_listener. - **#156**: [Button] Now button can work in HTML5 with `html5.set_interaction_listener`.
- The API is `button:set_html5_user_interaction(true)`. In HTML5 mode button have several restrictions. Basically, only the single tap event will work. - The API is `button:set_html5_user_interaction(true)`. In HTML5 mode button have several restrictions. Basically, only the single tap event will work.
- **#227**: Update current URL in HTML5 example - **#227**: Update current URL in HTML5 example
- Now if you will open the example from direct URL, it will be updated to the current URL. So now it's much easier to share the example link with each other. - Now if you will open the example from direct URL, it will be updated to the current URL in your browser. So now it's much easier to share the example link with each other.
- **#183**: Documentation about GUI in World Space - **#183**: Documentation about [GUI in World Space](https://forum.defold.com/t/how-to-gui-in-defold/73256#gui-in-world-coordinates-49)
- Also not only the GUI in World Space, but overall How to GUI in Defold article. - Also not only the GUI in World Space, but overall How to GUI in Defold article.
- **#234**: [BREAKING][Blocker] Now `blocker:set_enabled` and `blocker:is_enabled` affects only inner state of component. To consume input, the blocker component should be enabled and the node itself should be enabled. - **#234**: [BREAKING][Blocker] Now `blocker:set_enabled` and `blocker:is_enabled` affects only inner state of component. To consume input, the blocker component should be enabled and the node itself should be enabled.
- **#235**: [Drag] Fix Drag coordinates on streched screen - **#235**: [Drag] Fix Drag coordinates on streched screen.
- **#236**: [Hover] Fix nil return in `hover:on_input` - **#236**: [Hover] Fix nil return in `hover:on_input`.
- **#237**: [Layout] Add `layout:set_max_gui_upscale` function - **#237**: [Layout] Add `layout:set_max_gui_upscale` function.
- **#238**: [System] Add Helper documentation - This functions will scale down element, if current GUI scale is bigger than `max_gui_upscale` value. It can be useful for adapt mobile device to desktop screen.
- **#238**: [System] Add Helper documentation.
- [System] Now the documentation contains the **Druid** size. The current size as dependency is around **67KB**. It counted without extended components, which is not included by default in the build.
If you like **Druid**, please, consider to support me!
❤️ Support ❤️
Please support me if you like this project! It will help me keep engaged to update **Druid** and make it even better!
[![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/insality) [![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/insality) [![BuyMeACoffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/insality)
Thanks for my the supporters:
- [Defold Foundation](https://defold.com)
- [Ragetto](https://forum.defold.com/u/ragetto)

View File

@ -13,9 +13,6 @@ local utf8 = utf8 or utf8_lua
local M = {} local M = {}
M.ADJUST_STEPS = 20
M.ADJUST_SCALE_DELTA = 0.02
-- Trim spaces on string start -- Trim spaces on string start
local function ltrim(text) local function ltrim(text)
return text:match('^%s*(.*)') return text:match('^%s*(.*)')
@ -144,12 +141,13 @@ local function measure_node(word, settings, previous_word)
end end
--- Create rich text gui nodes from text -- Create rich text gui nodes from text
-- @param text The text to create rich text nodes from --- @param text string The text to create rich text nodes from
-- @param settings Optional settings table (refer to documentation for details) --- @param settings table Optional settings table (refer to documentation for details)
-- @return words --- @param style druid.rich_text.style
-- @return metrics --- @return words
function M.create(text, settings) --- @return metrics
function M.create(text, settings, style)
assert(text, "You must provide a text") assert(text, "You must provide a text")
-- default settings for a word -- default settings for a word
@ -178,12 +176,11 @@ function M.create(text, settings)
image_color = gui.get_color(settings.node_prefab), image_color = gui.get_color(settings.node_prefab),
default_animation = nil, default_animation = nil,
-- Tags -- Tags
anchor = nil,
br = nil, br = nil,
nobr = nil, nobr = nil,
} }
local parsed_words = parser.parse(text, word_params) local parsed_words = parser.parse(text, word_params, style)
local lines = M._split_on_lines(parsed_words, settings) local lines = M._split_on_lines(parsed_words, settings)
local lines_metrics = M._position_lines(lines, settings) local lines_metrics = M._position_lines(lines, settings)
M._update_nodes(lines, settings) M._update_nodes(lines, settings)
@ -450,7 +447,8 @@ end
---@param words druid.rich_text.word[] ---@param words druid.rich_text.word[]
---@param settings druid.rich_text.settings ---@param settings druid.rich_text.settings
---@param lines_metrics druid.rich_text.lines_metrics ---@param lines_metrics druid.rich_text.lines_metrics
function M.adjust_to_area(words, settings, lines_metrics) ---@param style druid.rich_text.style
function M.adjust_to_area(words, settings, lines_metrics, style)
local last_line_metrics = lines_metrics local last_line_metrics = lines_metrics
if not settings.is_multiline then if not settings.is_multiline then
@ -459,7 +457,7 @@ function M.adjust_to_area(words, settings, lines_metrics)
end end
else else
-- Multiline adjusting is very tricky stuff... -- Multiline adjusting is very tricky stuff...
-- It's do a lot of calculations, beware! -- It's doing a lot of calculations, beware!
if lines_metrics.text_width > settings.width or lines_metrics.text_height > settings.height then if lines_metrics.text_width > settings.width or lines_metrics.text_height > settings.height then
local scale_koef = math.sqrt(settings.height / lines_metrics.text_height) local scale_koef = math.sqrt(settings.height / lines_metrics.text_height)
if lines_metrics.text_width * scale_koef > settings.width then if lines_metrics.text_width * scale_koef > settings.width then
@ -469,9 +467,9 @@ function M.adjust_to_area(words, settings, lines_metrics)
local lines = M.apply_scale_without_update(words, settings, adjust_scale) local lines = M.apply_scale_without_update(words, settings, adjust_scale)
local is_fit = M.is_fit_info_area(lines, settings) local is_fit = M.is_fit_info_area(lines, settings)
local step = is_fit and M.ADJUST_SCALE_DELTA or -M.ADJUST_SCALE_DELTA local step = is_fit and style.ADJUST_SCALE_DELTA or -style.ADJUST_SCALE_DELTA
for i = 1, M.ADJUST_STEPS do for i = 1, style.ADJUST_STEPS do
-- Grow down to check if we fit -- Grow down to check if we fit
if step < 0 and is_fit then if step < 0 and is_fit then
last_line_metrics = M.set_text_scale(words, settings, adjust_scale) last_line_metrics = M.set_text_scale(words, settings, adjust_scale)
@ -487,7 +485,7 @@ function M.adjust_to_area(words, settings, lines_metrics)
lines = M.apply_scale_without_update(words, settings, adjust_scale) lines = M.apply_scale_without_update(words, settings, adjust_scale)
is_fit = M.is_fit_info_area(lines, settings) is_fit = M.is_fit_info_area(lines, settings)
if i == M.ADJUST_STEPS then if i == style.ADJUST_STEPS then
last_line_metrics = M.set_text_scale(words, settings, adjust_scale) last_line_metrics = M.set_text_scale(words, settings, adjust_scale)
end end
end end

View File

@ -3,44 +3,47 @@
-- Modified by: Insality -- Modified by: Insality
local M = {} local M = {}
local cache = {}
function M.parse_hex(hex) function M.parse_hex(hex)
if cache[hex] then
return cache[hex]
end
local r,g,b,a = hex:match("#?(%x%x)(%x%x)(%x%x)(%x?%x?)") local r,g,b,a = hex:match("#?(%x%x)(%x%x)(%x%x)(%x?%x?)")
if a == "" then a = "ff" end if a == "" then a = "ff" end
if r and g and b and a then if r and g and b and a then
return vmath.vector4( local color = vmath.vector4(
tonumber(r, 16) / 255, tonumber(r, 16) / 255,
tonumber(g, 16) / 255, tonumber(g, 16) / 255,
tonumber(b, 16) / 255, tonumber(b, 16) / 255,
tonumber(a, 16) / 255) tonumber(a, 16) / 255
)
cache[hex] = color
return color
end end
return nil return nil
end end
function M.parse_decimal(dec) function M.parse_decimal(dec)
if cache[dec] then
return cache[dec]
end
local r,g,b,a = dec:match("(%d*%.?%d*),(%d*%.?%d*),(%d*%.?%d*),(%d*%.?%d*)") local r,g,b,a = dec:match("(%d*%.?%d*),(%d*%.?%d*),(%d*%.?%d*),(%d*%.?%d*)")
if r and g and b and a then if r and g and b and a then
return vmath.vector4(tonumber(r), tonumber(g), tonumber(b), tonumber(a)) local color = vmath.vector4(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
cache[dec] = color
return color
end end
return nil return nil
end 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 = {}
function M.parse(c) function M.parse(c)
return M.COLORS[c] or M.parse_hex(c) or M.parse_decimal(c) return M.parse_hex(c) or M.parse_decimal(c)
end end

View File

@ -8,9 +8,9 @@ local utf8 = utf8 or utf8_lua
local M = {} local M = {}
local function parse_tag(tag, params) local function parse_tag(tag, params, style)
local settings = { tags = { [tag] = params }, tag = tag } local settings = { tags = { [tag] = params }, tag = tag }
if not tags.apply(tag, params, settings) then if not tags.apply(tag, params, settings, style) then
settings[tag] = params settings[tag] = params
end end
@ -110,8 +110,9 @@ end
--- Parse the text into individual words --- Parse the text into individual words
-- @param text The text to parse -- @param text The text to parse
-- @param default_settings Default settings for each word -- @param default_settings Default settings for each word
-- @param color_aliases Color aliases table
-- @return List of all words -- @return List of all words
function M.parse(text, default_settings) function M.parse(text, default_settings, style)
assert(text) assert(text)
assert(default_settings) assert(default_settings)
@ -151,12 +152,12 @@ function M.parse(text, default_settings)
if is_empty then if is_empty then
-- empty tag, ie tag without content -- empty tag, ie tag without content
-- example <br/> and <img=texture:image/> -- example <br/> and <img=texture:image/>
local empty_tag_settings = parse_tag(name, params) local empty_tag_settings = parse_tag(name, params, style)
merge_tags(empty_tag_settings, word_settings) merge_tags(empty_tag_settings, word_settings)
add_word("", empty_tag_settings, all_words) add_word("", empty_tag_settings, all_words)
elseif not is_endtag then elseif not is_endtag then
-- open tag - parse and add it -- open tag - parse and add it
local tag_settings = parse_tag(name, params) local tag_settings = parse_tag(name, params, style)
open_tags[#open_tags + 1] = tag_settings open_tags[#open_tags + 1] = tag_settings
else else
-- end tag - remove it from the list of open tags -- end tag - remove it from the list of open tags

View File

@ -5,17 +5,16 @@
local color = require("druid.custom.rich_text.module.rt_color") local color = require("druid.custom.rich_text.module.rt_color")
local M = {} local M = {}
local tags = {} local tags = {}
function M.apply(tag, params, settings) function M.apply(tag, params, settings, style)
local fn = tags[tag] local fn = tags[tag]
if not fn then if not fn then
return false return false
end end
fn(params, settings) fn(params, settings, style)
return true return true
end end
@ -44,17 +43,20 @@ end
-- Format: <color=[#]{HEX_VALUE}>{Text}</color> -- Format: <color=[#]{HEX_VALUE}>{Text}</color>
-- Format: <color={COLOR_NAME}>{Text}</color> -- Format: <color={COLOR_NAME}>{Text}</color>
-- Example: <color=FF0000>Rich Text</color> -- Example: <color=FF0000>Rich Text</color>
M.register("color", function(params, settings) M.register("color", function(params, settings, style)
params = style.COLORS[params] or params
settings.color = color.parse(params) settings.color = color.parse(params)
end) end)
M.register("shadow", function(params, settings) M.register("shadow", function(params, settings, style)
params = style.COLORS[params] or params
settings.shadow = color.parse(params) settings.shadow = color.parse(params)
end) end)
M.register("outline", function(params, settings) M.register("outline", function(params, settings, style)
params = style.COLORS[params] or params
settings.outline = color.parse(params) settings.outline = color.parse(params)
end) end)
@ -69,11 +71,6 @@ M.register("size", function(params, settings)
end) end)
M.register("a", function(params, settings)
settings.anchor = true
end)
-- Example: </br> -- Example: </br>
M.register("br", function(params, settings) M.register("br", function(params, settings)
settings.br = true settings.br = true

View File

@ -59,7 +59,6 @@
-- font: string, -- font: string,
-- image: druid.rich_text.image, -- image: druid.rich_text.image,
-- default_animation: string, -- default_animation: string,
-- anchor: number,
-- br: boolean, -- br: boolean,
-- nobr: boolean, -- nobr: boolean,
-- } -- }
@ -125,6 +124,21 @@ function RichText.init(self, template, nodes)
end end
--- Component style params.
-- You can override this component styles params in Druid styles table
-- or create your own style
-- @table style
-- @tfield[opt={}] table COLORS Rich Text color aliases
-- @tfield[opt=20] number ADJUST_STEPS Amount steps of attemps text adjust by height
-- @tfield[opt=0.02] number ADJUST_SCALE_DELTA Scale step on each height adjust step
function RichText.on_style_change(self, style)
self.style = {}
self.style.COLORS = style.COLORS or {}
self.style.ADJUST_STEPS = style.ADJUST_STEPS or 20
self.style.ADJUST_SCALE_DELTA = style.ADJUST_SCALE_DELTA or 0.02
end
--- Set text for Rich Text --- Set text for Rich Text
-- @tparam RichText self @{RichText} -- @tparam RichText self @{RichText}
-- @tparam string text The text to set -- @tparam string text The text to set
@ -174,10 +188,10 @@ end
-- <img=texture:image,size/> -- <img=texture:image,size/>
-- <img=texture:image,width,height/> -- <img=texture:image,width,height/>
function RichText.set_text(self, text) function RichText.set_text(self, text)
self:clean() self:clear()
local words, settings, line_metrics = rich_text.create(text, self._settings) local words, settings, line_metrics = rich_text.create(text, self._settings, self.style)
line_metrics = rich_text.adjust_to_area(words, settings, line_metrics) line_metrics = rich_text.adjust_to_area(words, settings, line_metrics, self.style)
self._words = words self._words = words
self._line_metrics = line_metrics self._line_metrics = line_metrics
@ -187,13 +201,22 @@ end
function RichText:on_remove() function RichText:on_remove()
self:clean() self:clear()
end end
--- Get all words, which has a passed tag --- Clear all created words.
function RichText:clear()
if self._words then
rich_text.remove(self._words)
self._words = nil
end
end
--- Get all words, which has a passed tag.
-- @tparam string tag -- @tparam string tag
-- @treturn table Words -- @treturn druid.rich_text.word[] words
function RichText:tagged(tag) function RichText:tagged(tag)
if not self._words then if not self._words then
return return
@ -204,12 +227,19 @@ end
--- Get all current words. --- Get all current words.
-- @treturn table Words -- @treturn table druid.rich_text.word[]
function RichText:get_words() function RichText:get_words()
return self._words return self._words
end end
--- Get current line metrics
--- @treturn druid.rich_text.lines_metrics
function RichText:get_line_metric()
return self._line_metrics
end
function RichText:_create_settings() function RichText:_create_settings()
local root_size = gui.get_size(self.root) local root_size = gui.get_size(self.root)
return { return {
@ -219,7 +249,7 @@ function RichText:_create_settings()
parent = self.root, parent = self.root,
width = root_size.x, width = root_size.x,
height = root_size.y, height = root_size.y,
combine_words = false, combine_words = false, -- disabled now
text_prefab = self.text_prefab, text_prefab = self.text_prefab,
node_prefab = self.icon_prefab, node_prefab = self.icon_prefab,
@ -231,7 +261,7 @@ function RichText:_create_settings()
is_multiline = gui.get_line_break(self.text_prefab), is_multiline = gui.get_line_break(self.text_prefab),
-- Image settings -- Image settings
image_pixel_grid_snap = false, image_pixel_grid_snap = false, -- disabled now
node_scale = gui.get_scale(self.icon_prefab), node_scale = gui.get_scale(self.icon_prefab),
image_scale = gui.get_scale(self.icon_prefab), image_scale = gui.get_scale(self.icon_prefab),
default_animation = gui.get_flipbook(self.icon_prefab), default_animation = gui.get_flipbook(self.icon_prefab),
@ -239,13 +269,4 @@ function RichText:_create_settings()
end end
--- Clear all created words.
function RichText:clean()
if self._words then
rich_text.remove(self._words)
self._words = nil
end
end
return RichText return RichText

View File

@ -1,5 +1,10 @@
-- Manual Annotations -- -- Manual Annotations --
---@class druid.rich_text.style
---@field COLORS table
---@field ADJUST_STEPS number
---@field ADJUST_SCALE_DELTA number
---@class druid.rich_text.metrics ---@class druid.rich_text.metrics
---@field width number ---@field width number
---@field height number ---@field height number