diff --git a/druid/base/text.lua b/druid/base/text.lua index 8981ccc..e4bdf4c 100755 --- a/druid/base/text.lua +++ b/druid/base/text.lua @@ -83,10 +83,12 @@ local function update_text_area_size(self) scale_modifier = math.min(scale_modifier, self.start_scale.x) if self:is_multiline() then - local max_text_area_square = max_width * max_height - local cur_text_area_square = metrics.height * metrics.width * self.start_scale.x - scale_modifier = self.start_scale.x * math.sqrt(max_text_area_square / cur_text_area_square) - scale_modifier = math.min(scale_modifier, self.start_scale.x) + local scale_modifier_by_height = math.sqrt(max_height / metrics.height) + scale_modifier = math.min(self.start_scale.y, scale_modifier_by_height) + + if metrics.width * scale_modifier > max_width then + scale_modifier = math.min(max_width / metrics.width, self.start_scale.x) + end end if self._minimal_scale then diff --git a/druid/custom/rich_text/rich_text.lua b/druid/custom/rich_text/rich_text.lua index 5ba247b..5756cd4 100644 --- a/druid/custom/rich_text/rich_text.lua +++ b/druid/custom/rich_text/rich_text.lua @@ -12,14 +12,14 @@ local SCHEME = { 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 }, + [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 }, } @@ -28,6 +28,7 @@ function RichText:init(template, nodes) self:set_nodes(nodes) self.root = self:get_node(SCHEME.ROOT) self.druid = self:get_druid() + self.root_size = gui.get_size(self.root) self.text_prefab = self:get_node(SCHEME.TEXT_PREFAB) self.icon_prefab = self:get_node(SCHEME.ICON_PREFAB) @@ -42,11 +43,44 @@ end function RichText:set_text(text) self:_clean_words() - pprint(self._settings) + local is_already_adjusted = self._settings.adjust_scale ~= 1 + + -- Make text singleline if prefab without line break + local is_multiline = gui.get_line_break(self.text_prefab) + if not is_multiline then + text = string.format("%s", text) + end + local words, metrics = rich_text.create(text, self._text_font, self._settings) self._words = words self._metrics = metrics + + for _, word in ipairs(words) do + print(word.text) + end + + if not is_multiline then + local scale_koef = self.root_size.x / self._metrics.width + self._settings.adjust_scale = math.min(scale_koef, 1) + else + local scale_koef = math.sqrt(self.root_size.y / self._metrics.height) + if self._metrics.width * scale_koef > self.root_size.x then + scale_koef = math.sqrt(self.root_size.x / self._metrics.width) + end + self._settings.adjust_scale = math.min(scale_koef, 1) + end + + if not is_already_adjusted and self._settings.adjust_scale < 1 then + print("Again set text with adjusted scale", self._settings.adjust_scale) + self:set_text(text) + return + end + + -- Align vertically, different behaviour from rich text + self:_align_vertically() + + pprint(self._metrics) end @@ -56,17 +90,23 @@ end function RichText:_get_settings() - local root_size = gui.get_size(self.root) local anchor = gui.get_pivot(self.root) - pprint(ALIGN_MAP[anchor]) + local align = ALIGN_MAP[anchor][1] + local valign = ALIGN_MAP[anchor][2] + return { - width = root_size.x, + width = self.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], + text_scale = gui.get_scale(self.text_prefab), + default_texture = gui.get_texture(self.icon_prefab), + default_anim = gui.get_flipbook(self.icon_prefab), + combine_words = true, + adjust_scale = 1, + align = align, + valign = valign, } end @@ -82,4 +122,21 @@ function RichText:_clean_words() end +function RichText:_align_vertically() + local text_height = self._metrics.height + local offset = 0 + if self._settings.valign == rich_text.VALIGN_MIDDLE then + offset = text_height * 0.5 + end + if self._settings.valign == rich_text.VALIGN_BOTTOM then + offset = text_height + end + + for _, word in ipairs(self._words) do + word.position.y = word.position.y + offset + gui.set_position(word.node, word.position) + end +end + + return RichText diff --git a/druid/custom/rich_text/rich_text/parse.lua b/druid/custom/rich_text/rich_text/parse.lua index 2c74f95..a68d581 100755 --- a/druid/custom/rich_text/rich_text/parse.lua +++ b/druid/custom/rich_text/rich_text/parse.lua @@ -115,7 +115,7 @@ function M.parse(text, default_settings) local open_tags = {} while true do -- merge list of word settings from defaults and all open tags - local word_settings = { 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) diff --git a/druid/custom/rich_text/rich_text/richtext.lua b/druid/custom/rich_text/rich_text/richtext.lua index 03d21fc..89058bf 100755 --- a/druid/custom/rich_text/rich_text/richtext.lua +++ b/druid/custom/rich_text/rich_text/richtext.lua @@ -157,6 +157,7 @@ local function position_words(words, line_width, line_height, position, settings else gui.set_position(word.node, position) end + word.position = vmath.vector3(position) position.x = position.x + word.metrics.total_width + spacing words[i] = nil end @@ -188,32 +189,34 @@ 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")) + gui.set_size_mode(node, gui.SIZE_MODE_AUTO) + gui.set_texture(node, word.image.texture or word.default_texture) + gui.play_flipbook(node, hash(word.image.anim or word.default_anim)) + if image_width then + local image_size = gui.get_size(node) gui.set_size_mode(node, gui.SIZE_MODE_MANUAL) size_vector.x = image_width - size_vector.y = image_height + -- Use height or autoscale to keep aspect ratio + size_vector.y = word_image.height or ((image_size.y / image_size.x) * image_width) 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 + + local word_size = word.size * word.adjust_scale 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)) + word.scale = vmath.vector3(size_vector) + gui.set_scale(node, word.scale) -- 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 + metrics.total_width = size.x * word.size * word.adjust_scale + metrics.width = size.x * word.size * word.adjust_scale + metrics.height = size.y * word.size * word.adjust_scale return node, metrics end @@ -222,7 +225,8 @@ 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)) + word.scale = vmath.vector3(word.size) + gui.set_scale(node, word.scale) gui.play_spine_anim(node, word.spine.anim, gui.PLAYBACK_LOOP_FORWARD) local size = gui.get_size(node) @@ -243,11 +247,11 @@ local function get_text_metrics(word, font, text) metrics = gui.get_text_metrics(font, "|") metrics.width = 0 metrics.total_width = 0 - metrics.height = metrics.height * word.size + metrics.height = metrics.height * word.size * word.text_scale.y * word.adjust_scale else metrics = gui.get_text_metrics(font, text) - metrics.width = metrics.width * word.size - metrics.height = metrics.height * word.size + metrics.width = metrics.width * word.size * word.text_scale.x * word.adjust_scale + metrics.height = metrics.height * word.size * word.text_scale.y * word.adjust_scale metrics.total_width = metrics.width end return metrics @@ -261,7 +265,8 @@ local function create_text_node(word, font, metrics) 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) + word.scale = word.text_scale * word.size * word.adjust_scale + gui.set_scale(node, word.scale) metrics = metrics or get_text_metrics(word, font) gui.set_size_mode(node, gui.SIZE_MODE_MANUAL) @@ -312,6 +317,7 @@ local function measure_node(word, font, previous_word) return metrics, combined_metrics, node end + local function split_word(word, font, max_width) local one = deepcopy(word) local two = deepcopy(word) @@ -320,7 +326,7 @@ local function split_word(word, font, max_width) 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 + while split_index >= 1 do one.text = utf8.sub(text, 1, split_index) one.linebreak = true metrics = get_text_metrics(one, font) @@ -362,6 +368,9 @@ function M.create(text, font, settings) 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 + settings.text_scale = settings.text_scale or V3_ONE + settings.default_texture = settings.default_texture or nil + settings.default_anim = settings.default_anim or nil if settings.align == M.ALIGN_JUSTIFY and not settings.width then error("Width must be specified if text should be justified") end @@ -386,7 +395,14 @@ function M.create(text, font, settings) shadow = settings.shadow, outline = settings.outline, font = font, - size = settings.size + size = settings.size, + -- Autofill properties + text_scale = settings.text_scale, + adjust_scale = settings.adjust_scale, -- scale for content adjust to fit into root size + position = nil, + scale = nil, + default_texture = nil, -- for image only + default_anim = nil, -- for image only } local words = parser.parse(text, word_settings) local text_metrics = { @@ -406,6 +422,8 @@ function M.create(text, font, settings) repeat local word = words[i] if word.image then + word.default_texture = settings.default_texture + word.default_anim = settings.default_anim text_metrics.img_count = text_metrics.img_count + 1 elseif word.spine then text_metrics.spine_count = text_metrics.spine_count + 1 @@ -680,7 +698,8 @@ function M.characters(word) 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) + char.position = vmath.vector3(position) + gui.set_position(char.node, char.position) end return chars diff --git a/druid/custom/rich_text/rich_text/tags.lua b/druid/custom/rich_text/rich_text/tags.lua index ded8372..eba23a0 100644 --- a/druid/custom/rich_text/rich_text/tags.lua +++ b/druid/custom/rich_text/rich_text/tags.lua @@ -97,9 +97,13 @@ M.register("img", function(params, settings) width, params = split(params, ",") height = split(params, ",") local texture, anim = split(texture_and_anim, ":") + if not anim then + anim = texture + texture = nil + end width = width and tonumber(width) - height = height and tonumber(height) or width + height = height and tonumber(height) settings.image = { texture = texture, diff --git a/example/examples/custom/rich_text/rich_text.gui b/example/examples/custom/rich_text/rich_text.gui index 3c2643b..4e70236 100644 --- a/example/examples/custom/rich_text/rich_text.gui +++ b/example/examples/custom/rich_text/rich_text.gui @@ -84,8 +84,8 @@ nodes { w: 1.0 } scale { - x: 1.0 - y: 1.0 + x: 1.3 + y: 1.3 z: 1.0 w: 1.0 } @@ -126,8 +126,8 @@ nodes { w: 1.0 } scale { - x: 1.0 - y: 1.0 + x: 1.2 + y: 1.2 z: 1.0 w: 1.0 } @@ -149,7 +149,7 @@ nodes { id: "rich_text/root" xanchor: XANCHOR_NONE yanchor: YANCHOR_NONE - pivot: PIVOT_E + pivot: PIVOT_N adjust_mode: ADJUST_MODE_FIT parent: "rich_text" layer: "" @@ -164,6 +164,7 @@ nodes { clipping_visible: true clipping_inverted: false alpha: 1.0 + overridden_fields: 3 overridden_fields: 5 overridden_fields: 14 overridden_fields: 46 @@ -187,8 +188,8 @@ nodes { w: 1.0 } scale { - x: 1.0 - y: 1.0 + x: 0.8 + y: 0.8 z: 1.0 w: 1.0 } @@ -225,13 +226,15 @@ nodes { w: 1.0 } adjust_mode: ADJUST_MODE_FIT - line_break: false + line_break: true parent: "rich_text/root" layer: "" inherit_alpha: true alpha: 1.0 outline_alpha: 1.0 shadow_alpha: 0.0 + overridden_fields: 3 + overridden_fields: 18 template_node_child: true text_leading: 1.0 text_tracking: 0.0 @@ -297,6 +300,64 @@ nodes { 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: 150.0 + y: 4.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: "middle_line" + 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: 0.56 + template_node_child: false + size_mode: SIZE_MODE_MANUAL + custom_type: 0 + enabled: true + visible: true +} layers { name: "image" } diff --git a/example/examples/custom/rich_text/rich_text.gui_script b/example/examples/custom/rich_text/rich_text.gui_script new file mode 100644 index 0000000..c6f6234 --- /dev/null +++ b/example/examples/custom/rich_text/rich_text.gui_script @@ -0,0 +1,34 @@ +local druid = require("druid.druid") + +local RichText = require("druid.custom.rich_text.rich_text") + + +function init(self) + self.druid = druid.new(self) + + self.rich_text = self.druid:new(RichText, "rich_text") + -- self.rich_text:set_text("Lorem long text with differrent placeholder or just text without any sense here to check multiline without long words") + -- self.rich_text:set_text("Lorem long text with differrent placeholder or just text without any sense here to check multiline without long wordswordwordwrodwrodwrodswrodword he") + self.rich_text:set_text("Some text with image in the middle") + self.rich_text:set_text("Some text with image in the middle") +end + + +function final(self) + self.druid:final() +end + + +function update(self, dt) + self.druid:update(dt) +end + + +function on_message(self, message_id, message, sender) + self.druid:on_message(message_id, message, sender) +end + + +function on_input(self, action_id, action) + return self.druid:on_input(action_id, action) +end