diff --git a/gui/vector_emoji_rich_text/README.md b/gui/vector_emoji_rich_text/README.md new file mode 100644 index 00000000..ca5a4372 --- /dev/null +++ b/gui/vector_emoji_rich_text/README.md @@ -0,0 +1,46 @@ +--- +title: Vector Fonts, Rich Text and Emoji +brief: This example shows how to use vector fonts, Rich Text effects and full-color emoji. +author_ids: + - defold-foundation +scripts: hello.gui_script +thumbnail: thumbnail.webp +tags: font, gui, animation +--- + +This example shows how to combine vector fonts, Rich Text effects and full-color emoji in a single Defold GUI text node. + +The text displays a text and emojis with example Rich Text tags for changing size, color, or gradients. +An emoji changes every second. +The entire text gently zooms in and out using sine easing. + +## Setup + +The scene contains just one game object, `go`, with a GUI component: + +- The GUI contains one text node named `text`, positioned in the center. +- The GUI uses the accompanying GUI script `hello.gui_script` to set and animate the text. +- The GUI has a vector font registered with the name `hello`. +- The example includes `Twemoji-COLRv1.ttf` for full-color emoji. + +![setup](setup.png) + +It's also important to enable full text layout system and Rich Text in the custom App Manifest: + +![appmanifest](appmanifest.png) + +## How it works + +1. In `init()` get the `hello` font resource +2. Add the COLRv1 emoji font with `font.add_font()` +3. Prewarm the glyphs with `font.prewarm_text()`. +2. When prewarming completes, `on_prewarmed()` sets the Rich Text string on the `text` node and starts a looping zoom animation with `gui.animate()` and `gui.EASING_INOUTSINE`. +3. In `update()` we change the emoji every second, cycling through rocket, smile, wink and thumbs up. + +The Rich Text markup uses ``, ``, ``, `` and `` to style and animate portions of the text without creating extra nodes. +Read more about Rich Text tags in [the Rich Text markup manual](https://defold.com/manuals/font-richtext/). + +Credits +Inter font files: SIL Open Font License 1.1. +Emoji glyphs: derived from Twemoji Mozilla, CC BY 4.0 +Source links in `licenses/`. diff --git a/gui/vector_emoji_rich_text/appmanifest.appmanifest b/gui/vector_emoji_rich_text/appmanifest.appmanifest new file mode 100644 index 00000000..8063896d --- /dev/null +++ b/gui/vector_emoji_rich_text/appmanifest.appmanifest @@ -0,0 +1,73 @@ +platforms: + arm64-ios: + context: + excludeLibs: [font, physics, LinearMath, BulletDynamics, BulletCollision, script_bullet3d, box2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, harfbuzz, sheenbidi, unibreak, skribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] + arm64_sim-ios: + context: + excludeLibs: [font, physics, LinearMath, BulletDynamics, BulletCollision, script_bullet3d, box2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, harfbuzz, sheenbidi, unibreak, skribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] + armv7-android: + context: + excludeLibs: [font, physics, LinearMath, BulletDynamics, BulletCollision, script_bullet3d, box2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, harfbuzz, sheenbidi, unibreak, skribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] + arm64-android: + context: + excludeLibs: [font, physics, LinearMath, BulletDynamics, BulletCollision, script_bullet3d, box2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, harfbuzz, sheenbidi, unibreak, skribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] + x86_64-android: + context: + excludeLibs: [font, physics, LinearMath, BulletDynamics, BulletCollision, script_bullet3d, box2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, harfbuzz, sheenbidi, unibreak, skribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] + arm64-osx: + context: + excludeLibs: [font, physics, LinearMath, BulletDynamics, BulletCollision, script_bullet3d, box2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, harfbuzz, sheenbidi, unibreak, skribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] + x86_64-osx: + context: + excludeLibs: [font, physics, LinearMath, BulletDynamics, BulletCollision, script_bullet3d, box2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, harfbuzz, sheenbidi, unibreak, skribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] + x86_64-linux: + context: + excludeLibs: [font, physics, LinearMath, BulletDynamics, BulletCollision, script_bullet3d, box2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, harfbuzz, sheenbidi, unibreak, skribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] + arm64-linux: + context: + excludeLibs: [font, physics, LinearMath, BulletDynamics, BulletCollision, script_bullet3d, box2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, harfbuzz, sheenbidi, unibreak, skribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] + x86_64-win32: + context: + excludeLibs: [font, physics, libLinearMath, libBulletDynamics, libBulletCollision, script_bullet3d, libbox2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, libharfbuzz, libsheenbidi, libunibreak, libskribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] + wasm-web: + context: + excludeLibs: [font, physics, LinearMath, BulletDynamics, BulletCollision, script_bullet3d, box2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, harfbuzz, sheenbidi, unibreak, skribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] + wasm_pthread-web: + context: + excludeLibs: [font, physics, LinearMath, BulletDynamics, BulletCollision, script_bullet3d, box2d_defold, script_box2d_defold, gamesys_model, gamesys_rig, rig] + excludeSymbols: [ScriptBullet3DExt, ScriptBox2DExt, ScriptModelExt] + symbols: [] + libs: [font_skribidi, harfbuzz, sheenbidi, unibreak, skribidi, physics_null, gamesys_rig_null, gamesys_model_null, rig_null] diff --git a/gui/vector_emoji_rich_text/appmanifest.png b/gui/vector_emoji_rich_text/appmanifest.png new file mode 100644 index 00000000..49d62d7d Binary files /dev/null and b/gui/vector_emoji_rich_text/appmanifest.png differ diff --git a/gui/vector_emoji_rich_text/fonts/Inter-ExtraBold.otf b/gui/vector_emoji_rich_text/fonts/Inter-ExtraBold.otf new file mode 100644 index 00000000..b8ee32a3 Binary files /dev/null and b/gui/vector_emoji_rich_text/fonts/Inter-ExtraBold.otf differ diff --git a/gui/vector_emoji_rich_text/fonts/Twemoji-COLRv1.ttf b/gui/vector_emoji_rich_text/fonts/Twemoji-COLRv1.ttf new file mode 100644 index 00000000..91324bfc Binary files /dev/null and b/gui/vector_emoji_rich_text/fonts/Twemoji-COLRv1.ttf differ diff --git a/gui/vector_emoji_rich_text/fonts/hello.font b/gui/vector_emoji_rich_text/fonts/hello.font new file mode 100644 index 00000000..ceaca61c --- /dev/null +++ b/gui/vector_emoji_rich_text/fonts/hello.font @@ -0,0 +1,20 @@ +font: "/fonts/Inter-ExtraBold.otf" +material: "/builtins/fonts/font-vector.material" +characters: " !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" +styles { + name: "default" +} +styles { + name: "link" + markup: "
    " +} +styles { + name: "link:hover" + markup: "" +} +styles { + name: "link:active" + markup: "" +} +vector_font_mode: VECTOR_FONT_MODE_VECTOR +runtime: true diff --git a/gui/vector_emoji_rich_text/game.project b/gui/vector_emoji_rich_text/game.project new file mode 100644 index 00000000..bce7b36a --- /dev/null +++ b/gui/vector_emoji_rich_text/game.project @@ -0,0 +1,22 @@ +[project] +title = Vector Font Rich Text and Emoji +version = 1.0 +custom_resources = /fonts/Twemoji-COLRv1.ttf + +[bootstrap] +main_collection = /main/main.collectionc + +[display] +width = 720 +height = 720 +high_dpi = 1 + +[input] +game_binding = /input/game.input_bindingc + +[shader] +exclude_gles_sm100 = 1 + +[native_extension] +app_manifest = /appmanifest.appmanifest + diff --git a/gui/vector_emoji_rich_text/input/game.input_binding b/gui/vector_emoji_rich_text/input/game.input_binding new file mode 100644 index 00000000..e69de29b diff --git a/gui/vector_emoji_rich_text/licenses/INTER-LICENSE-NOTE.txt b/gui/vector_emoji_rich_text/licenses/INTER-LICENSE-NOTE.txt new file mode 100644 index 00000000..4ec823b8 --- /dev/null +++ b/gui/vector_emoji_rich_text/licenses/INTER-LICENSE-NOTE.txt @@ -0,0 +1,4 @@ +Inter font files are distributed under the SIL Open Font License 1.1. +Copyright (c) Rasmus Andersson and the Inter project contributors. +The official license text: https://openfontlicense.org/open-font-license-official-text/ +Source: https://github.com/rsms/inter diff --git a/gui/vector_emoji_rich_text/licenses/TWEMOJI-ATTRIBUTION.txt b/gui/vector_emoji_rich_text/licenses/TWEMOJI-ATTRIBUTION.txt new file mode 100644 index 00000000..fbd05919 --- /dev/null +++ b/gui/vector_emoji_rich_text/licenses/TWEMOJI-ATTRIBUTION.txt @@ -0,0 +1,7 @@ +Emoji are derived from Twemoji Mozilla color-font assets. +Twemoji graphics: CC BY 4.0 (Twitter/Twemoji contributors). +Source: https://github.com/mozilla/twemoji-colr +Attribution: Graphics from Twemoji, copyright contributors, licensed CC BY 4.0. +This copy has its COLR table converted from version 0 to COLRv1 Paint glyphs. +See https://creativecommons.org/licenses/by/4.0/ +Please verify license obligations before distribution. diff --git a/gui/vector_emoji_rich_text/main/hello.gui b/gui/vector_emoji_rich_text/main/hello.gui new file mode 100644 index 00000000..5f5b27f0 --- /dev/null +++ b/gui/vector_emoji_rich_text/main/hello.gui @@ -0,0 +1,23 @@ +script: "/main/hello.gui_script" +fonts { + name: "hello" + font: "/fonts/hello.font" +} +nodes { + position { + x: 360.0 + y: 360.0 + } + size { + x: 200.0 + y: 100.0 + } + type: TYPE_TEXT + text: "Hello World! \360\237\232\200" + font: "hello" + id: "text" + inherit_alpha: true +} +material: "/builtins/materials/gui.material" +adjust_reference: ADJUST_REFERENCE_PARENT +max_nodes: 1 diff --git a/gui/vector_emoji_rich_text/main/hello.gui_script b/gui/vector_emoji_rich_text/main/hello.gui_script new file mode 100644 index 00000000..71c21359 --- /dev/null +++ b/gui/vector_emoji_rich_text/main/hello.gui_script @@ -0,0 +1,72 @@ + +-- Demonstrates how to use vector fonts, Rich Text effects and color emoji. + +-- Emojis displayed in sequence, changing every second. +local EMOJI = { "🚀", "😀", "😉", "👍" } + +-- Path to the COLRv1 font containing full-color emoji glyphs. +local EMOJI_FONT_PATH = "/fonts/Twemoji-COLRv1.ttf" + +-- Rich Text tags control color, size and animated effects. +-- The emoji is appended dynamically to the first line. +local function text_for(emoji) + return "Hello World! " .. emoji + .. "\n\n RichText, Vector Fonts and Emoji 😉" +end + +-- Called when the font glyphs have finished prewarming. +local function on_prewarmed(self, request_id, ok, err) + if not ok then + print("Emoji/font prewarm failed: " .. tostring(err)) + return + end + + -- Get the GUI text node and assign the vector font. + self.text = gui.get_node("text") + gui.set_font(self.text, "hello") + gui.set_pivot(self.text, gui.PIVOT_CENTER) + gui.set_line_break(self.text, false) + gui.set_color(self.text, vmath.vector4(1, 1, 1, 1)) + + -- Update the text with Rich Text formatting and the current emoji. + gui.set_text(self.text, text_for(EMOJI[self.emoji_index])) + + -- Animate the change of the scale of the text smoothly. + gui.set_scale(self.text, vmath.vector3(3.8, 3.8, 1)) + gui.animate(self.text, gui.PROP_SCALE, + vmath.vector3(4.5, 4.5, 1), gui.EASING_INOUTSINE, + 1.5, 0, nil, gui.PLAYBACK_LOOP_PINGPONG) +end + +function init(self) + -- Track the current emoji and elapsed time. + self.emoji_index = 1 + self.emoji_timer = 0 + + -- Add the COLRv1 font as a fallback for color emojis. + local font_resource = gui.get_font_resource("hello") + + -- Register the emoji font as a fallback for the main vector font. + font.add_font(font_resource, EMOJI_FONT_PATH) + + -- Preload the required glyphs and call on_prewarmed when ready. + -- We prewarm glyphs before displaying the text to avoid missing characters. + font.prewarm_text(font_resource, "Hello World! 🚀 😀 😉 👍", on_prewarmed) +end + +function update(self, dt) + -- Wait until the font is ready and the text node is set up. + if not self.text then return end + + -- Accumulate frame time (dt) to change the emoji every second. + self.emoji_timer = self.emoji_timer + dt + while self.emoji_timer >= 1.0 do + self.emoji_timer = self.emoji_timer - 1.0 + + -- Advance to the next emoji, wrapping back to the first. + self.emoji_index = self.emoji_index % #EMOJI + 1 + + -- Update the text while keeping the zoom animation running. + gui.set_text(self.text, text_for(EMOJI[self.emoji_index])) + end +end diff --git a/gui/vector_emoji_rich_text/main/main.collection b/gui/vector_emoji_rich_text/main/main.collection new file mode 100644 index 00000000..156e3996 --- /dev/null +++ b/gui/vector_emoji_rich_text/main/main.collection @@ -0,0 +1,10 @@ +name: "main" +scale_along_z: 0 +embedded_instances { + id: "go" + data: "components {\n" + " id: \"hello\"\n" + " component: \"/main/hello.gui\"\n" + "}\n" + "" +} diff --git a/gui/vector_emoji_rich_text/setup.png b/gui/vector_emoji_rich_text/setup.png new file mode 100644 index 00000000..7929a0c5 Binary files /dev/null and b/gui/vector_emoji_rich_text/setup.png differ diff --git a/gui/vector_emoji_rich_text/thumbnail.webp b/gui/vector_emoji_rich_text/thumbnail.webp new file mode 100644 index 00000000..ee1b2ae1 Binary files /dev/null and b/gui/vector_emoji_rich_text/thumbnail.webp differ