diff --git a/CHANGELOG.md b/CHANGELOG.md index 821beb28c02c..f2e4e1213ecf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,12 @@ - [Fixed developer console error about failing to decode a notification "executionContext/visualisationEvaluationFailed"][3193] +#### EnsoGL (rendering engine) + +- [You can change font and set letters bold in the text::Area + component][3385]. Use the set_font and + set_bold_bytes respectively. + #### Enso Standard Library - [Implemented `Vector.distinct` allowing to remove duplicate elements from a @@ -152,6 +158,7 @@ [3379]: https://github.com/enso-org/enso/pull/3379 [3381]: https://github.com/enso-org/enso/pull/3381 [3383]: https://github.com/enso-org/enso/pull/3383 +[3385]: https://github.com/enso-org/enso/pull/3385 [3392]: https://github.com/enso-org/enso/pull/3392 #### Enso Compiler diff --git a/Cargo.lock b/Cargo.lock index cf0b88b1a48e..4406bfc8e182 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -565,6 +565,26 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "const_format" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22bc6cd49b0ec407b680c3e380182b6ac63b73991cb7602de350352fc309b614" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -1679,6 +1699,7 @@ dependencies = [ name = "ensogl-text" version = "0.1.0" dependencies = [ + "const_format", "enso-frp", "enso-prelude", "enso-shapely", diff --git a/lib/rust/ensogl/component/text/Cargo.toml b/lib/rust/ensogl/component/text/Cargo.toml index 6327a66b9116..e32eb66fefbd 100644 --- a/lib/rust/ensogl/component/text/Cargo.toml +++ b/lib/rust/ensogl/component/text/Cargo.toml @@ -17,6 +17,7 @@ ensogl-core = { path = "../../core" } ensogl-text-embedded-fonts = { path = "embedded-fonts" } ensogl-text-msdf-sys = { path = "msdf-sys" } ensogl-hardcoded-theme = { path = "../../app/theme/hardcoded" } +const_format = "0.2.22" xi-rope = { version = "0.3.0" } [dev-dependencies] diff --git a/lib/rust/ensogl/component/text/src/buffer/style.rs b/lib/rust/ensogl/component/text/src/buffer/style.rs index 4323f66353e8..54fdaae3cc8e 100644 --- a/lib/rust/ensogl/component/text/src/buffer/style.rs +++ b/lib/rust/ensogl/component/text/src/buffer/style.rs @@ -225,6 +225,7 @@ def_style_property!(Size(f32)); def_style_property!(Bold(bool)); def_style_property!(Italic(bool)); def_style_property!(Underline(bool)); +def_style_property!(SdfBold(f32)); impl Default for Size { fn default() -> Self { @@ -247,12 +248,19 @@ impl Default for Underline { } } +impl Default for SdfBold { + fn default() -> Self { + Self::new(0.0) + } +} + define_styles! { size : Size, color : color::Rgba, bold : Bold, italics : Italic, underline : Underline, + sdf_bold : SdfBold, } diff --git a/lib/rust/ensogl/component/text/src/buffer/view.rs b/lib/rust/ensogl/component/text/src/buffer/view.rs index c1048f16d667..2a1816efe013 100644 --- a/lib/rust/ensogl/component/text/src/buffer/view.rs +++ b/lib/rust/ensogl/component/text/src/buffer/view.rs @@ -368,7 +368,8 @@ ensogl_core::define_endpoints! { redo (), set_default_color (color::Rgba), set_default_text_size (style::Size), - set_color_bytes (buffer::Range,color::Rgba), + set_color_bytes (buffer::Range, color::Rgba), + set_sdf_bold (buffer::Range, style::SdfBold), } Output { @@ -449,6 +450,7 @@ impl View { eval input.set_default_color ((t) m.set_default(*t)); eval input.set_default_text_size ((t) m.set_default(*t)); eval input.set_color_bytes (((range,color)) m.replace(range,*color)); + eval input.set_sdf_bold (((range,value)) m.replace(range,*value)); eval input.set_default_color ((color) m.set_default(*color)); output.source.selection_edit_mode <+ sel_on_modification; diff --git a/lib/rust/ensogl/component/text/src/component/area.rs b/lib/rust/ensogl/component/text/src/component/area.rs index 07dd11d35573..137143614fd4 100644 --- a/lib/rust/ensogl/component/text/src/component/area.rs +++ b/lib/rust/ensogl/component/text/src/component/area.rs @@ -260,9 +260,16 @@ ensogl_core::define_endpoints! { insert (String), set_color_bytes (buffer::Range,color::Rgba), set_color_all (color::Rgba), + set_sdf_bold (buffer::Range,style::SdfBold), set_default_color (color::Rgba), set_selection_color (color::Rgb), set_default_text_size (style::Size), + /// Set font in the text area. The name will be looked up in [`typeface::font::Registry`]. + /// + /// Note, that this is a relatively heavy operation - it requires not only redrawing all + /// lines, but also re-load internal structures for rendering (like WebGL buffers, + /// MSDF texture, etc.). + set_font (String), set_content (String), } Output { @@ -474,6 +481,11 @@ impl Area { input.remove_all_cursors(); }); + // === Font === + + eval input.set_font ((t) m.set_font(t)); + + // === Colors === eval input.set_default_color ((t) m.buffer.frp.set_default_color(*t)); @@ -495,6 +507,13 @@ impl Area { }); self.frp.source.selection_color <+ self.frp.set_selection_color; + + // === Style === + + m.buffer.frp.set_sdf_bold <+ input.set_sdf_bold; + eval_ input.set_sdf_bold (m.redraw(false)); + + // === Changes === // The `content` event should be fired first, as any listener for `changed` may want to @@ -545,7 +564,7 @@ impl Area { #[cfg(target_arch = "wasm32")] fn symbols(&self) -> SmallVec<[display::Symbol; 1]> { - let text_symbol = self.data.glyph_system.sprite_system().symbol.clone_ref(); + let text_symbol = self.data.glyph_system.borrow().sprite_system().symbol.clone_ref(); let shapes = &self.data.app.display.default_scene.shapes; let selection_system = shapes.shape_system(PhantomData::); let _selection_symbol = selection_system.shape_system.symbol.clone_ref(); @@ -573,7 +592,7 @@ pub struct AreaModel { buffer: buffer::View, display_object: display::object::Instance, #[cfg(target_arch = "wasm32")] - glyph_system: glyph::System, + glyph_system: Rc>, lines: Lines, single_line: Rc>, selection_map: Rc>, @@ -586,21 +605,20 @@ impl AreaModel { let scene = &app.display.default_scene; let logger = Logger::new("text_area"); let selection_map = default(); + let display_object = display::object::Instance::new(&logger); #[cfg(target_arch = "wasm32")] let glyph_system = { let fonts = scene.extension::(); let font = fonts.load("DejaVuSansMono"); - typeface::glyph::System::new(&scene, font) + let glyph_system = typeface::glyph::System::new(&scene, font); + display_object.add_child(&glyph_system); + Rc::new(RefCell::new(glyph_system)) }; - let display_object = display::object::Instance::new(&logger); let buffer = default(); let lines = default(); let single_line = default(); let camera = Rc::new(CloneRefCell::new(scene.camera().clone_ref())); - #[cfg(target_arch = "wasm32")] - display_object.add_child(&glyph_system); - // FIXME[WD]: These settings should be managed wiser. They should be set up during // initialization of the shape system, not for every area creation. To be improved during // refactoring of the architecture some day. @@ -792,12 +810,13 @@ impl AreaModel { let line_object = line.display_object().clone_ref(); let line_range = self.buffer.byte_range_of_view_line_index_snapped(view_line_index.into()); let mut line_style = self.buffer.sub_style(line_range.start..line_range.end).iter(); - let mut pen = pen::Pen::new(&self.glyph_system.font); + let glyph_system = self.glyph_system.borrow(); + let mut pen = pen::Pen::new(&glyph_system.font); let mut divs = vec![]; let mut column = 0.column(); let mut last_cursor = None; let mut last_cursor_target = default(); - line.resize_with(content.chars().count(), || self.glyph_system.new_glyph()); + line.resize_with(content.chars().count(), || glyph_system.new_glyph()); let mut iter = line.glyphs.iter_mut().zip(content.chars()); loop { let next = iter.next(); @@ -822,15 +841,16 @@ impl AreaModel { Some((glyph, chr)) => { let chr_bytes: Bytes = chr.len_utf8().into(); line_style.drop(chr_bytes - 1.bytes()); - let glyph_info = self.glyph_system.font.glyph_info(chr); - let size = glyph_info.scale.scale(chr_size); + let glyph_info = glyph_system.font.glyph_info(chr); let glyph_offset = glyph_info.offset.scale(chr_size); let glyph_x = info.offset + glyph_offset.x; let glyph_y = glyph_offset.y; glyph.set_position_xy(Vector2(glyph_x, glyph_y)); glyph.set_char(chr); glyph.set_color(style.color); - glyph.size.set(size); + glyph.set_bold(style.bold.raw); + glyph.set_sdf_bold(style.sdf_bold.raw); + glyph.set_font_size(chr_size); match &last_cursor { None => line_object.add_child(glyph), Some(cursor) => { @@ -920,6 +940,24 @@ impl AreaModel { let end = self.buffer.snap_location(selection.end); selection.with_start(start).with_end(end) } + + #[cfg(target_arch = "wasm32")] + fn set_font(&self, font_name: &str) { + let app = &self.app; + let scene = &app.display.default_scene; + let fonts = scene.extension::(); + let font = fonts.load(font_name); + let glyph_system = typeface::glyph::System::new(&scene, font); + self.display_object.add_child(&glyph_system); + let old_glyph_system = self.glyph_system.replace(glyph_system); + self.display_object.remove_child(&old_glyph_system); + // Remove old Glyph structures, as they still refer to the old Glyph System. + self.lines.rc.take(); + self.redraw(true); + } + + #[cfg(not(target_arch = "wasm32"))] + fn set_font(&self, _font_name: &str) {} } impl display::Object for AreaModel { diff --git a/lib/rust/ensogl/component/text/src/typeface/glsl/glyph.glsl b/lib/rust/ensogl/component/text/src/typeface/glsl/glyph.glsl index edadc18f570f..879671c7d4fc 100644 --- a/lib/rust/ensogl/component/text/src/typeface/glsl/glyph.glsl +++ b/lib/rust/ensogl/component/text/src/typeface/glsl/glyph.glsl @@ -1,8 +1,15 @@ +// A factor describing much the bold letters will be fattened, expressed as the fraction of font size. +const float BOLD_FATTING = 0.04; + highp float median(highp vec3 v) { return max(min(v.x, v.y), min(max(v.x, v.y), v.z)); } -highp vec2 get_scaled_uv() { +/// Compute the uv coordinates of the MSDF texture fragment where it should be sampled. +/// +/// Essentially, it's an input_uv which is a bit transformed to "cut off" the half of the MSDF cell from each side. This +/// way we have better pixel alignment on low resolutions. +highp vec2 msdf_fragment_uv() { highp vec2 msdf_cell_size = 1.0/input_msdf_size; highp vec2 offset = msdf_cell_size/2.0; highp vec2 scale = 1.0 - msdf_cell_size; @@ -10,9 +17,17 @@ highp vec2 get_scaled_uv() { } highp vec2 get_texture_coord() { - highp vec2 msdf_fragment_size = input_msdf_size / vec2(textureSize(input_atlas,0)); + highp vec2 msdf_fragment_size = input_msdf_size / vec2(textureSize(input_atlas, 0)); highp vec2 offset = vec2(0.0, input_atlas_index) * msdf_fragment_size; - return offset + get_scaled_uv() * msdf_fragment_size; + return offset + msdf_fragment_uv() * msdf_fragment_size; +} + +highp float get_fatting() { + bool glyph_is_bold = (input_style & STYLE_BOLD_FLAG) != 0; + highp vec2 local_to_px_ratio = 1.0 / fwidth(input_local.xy); + highp float font_size_px = input_font_size * (local_to_px_ratio.x + local_to_px_ratio.y) / 2.0; + highp float fatting = (glyph_is_bold ? BOLD_FATTING : 0.0) + input_sdf_bold; + return font_size_px * fatting; } highp float msdf_alpha() { @@ -23,12 +38,11 @@ highp float msdf_alpha() { // We use this parameter to fatten somewhat font on low resolutions. The thershold and exact // value of this fattening was picked by trial an error, searching for best rendering effect. - highp float dpi_dilate = avg_msdf_unit_px < input_msdf_range*0.49 ? 1.0 : 0.0; - - highp vec3 msdf_sample = texture(input_atlas,tex_coord).rgb; - highp float sig_dist = median(msdf_sample) - 0.5; - highp float sig_dist_px = sig_dist * avg_msdf_unit_px; - highp float opacity = 0.5 + sig_dist_px + dpi_dilate * 0.08; + highp float dpi_dilate = avg_msdf_unit_px < input_msdf_range*0.49 ? 1.0 : 0.0; + highp vec3 msdf_sample = texture(input_atlas,tex_coord).rgb; + highp float sig_dist = median(msdf_sample) - 0.5; + highp float sig_dist_px = sig_dist * avg_msdf_unit_px + get_fatting(); + highp float opacity = 0.5 + sig_dist_px + dpi_dilate * 0.08; opacity = clamp(opacity, 0.0, 1.0); return opacity; } diff --git a/lib/rust/ensogl/component/text/src/typeface/glsl/glyph_mac.glsl b/lib/rust/ensogl/component/text/src/typeface/glsl/glyph_mac.glsl index 113f9dd44bd4..da3880755c38 100644 --- a/lib/rust/ensogl/component/text/src/typeface/glsl/glyph_mac.glsl +++ b/lib/rust/ensogl/component/text/src/typeface/glsl/glyph_mac.glsl @@ -1,20 +1,36 @@ +// A factor describing much the bold letters will be fattened, expressed as the fraction of font size. +const float BOLD_FATTING = 0.04; + highp float median(highp vec3 v) { return max(min(v.x, v.y), min(max(v.x, v.y), v.z)); } -highp vec2 get_scaled_uv() { +/// Compute the uv coordinates of the MSDF texture fragment where it should be sampled. +/// +/// Essentially, it's an input_uv which is a bit transformed to "cut off" the half of the MSDF cell from each side. This +/// way we have better pixel alignment on low resolutions. +highp vec2 msdf_fragment_uv() { highp vec2 msdf_cell_size = 1.0/input_msdf_size; highp vec2 offset = msdf_cell_size/2.0; highp vec2 scale = 1.0 - msdf_cell_size; return offset + input_uv * scale; } + highp vec2 get_texture_coord() { highp vec2 msdf_fragment_size = input_msdf_size / vec2(textureSize(input_atlas,0)); highp vec2 offset = vec2(0.0, input_atlas_index) * msdf_fragment_size; return offset + get_scaled_uv() * msdf_fragment_size; } +highp float get_fatting() { + bool glyph_is_bold = (input_style & STYLE_BOLD_FLAG) != 0; + highp vec2 local_to_px_ratio = 1.0 / fwidth(input_local.xy); + highp float font_size_px = input_font_size * (local_to_px_ratio.x + local_to_px_ratio.y) / 2.0; + highp float fatting = (glyph_is_bold ? BOLD_FATTING : 0.0) + input_sdf_bold; + return font_size_px * fatting; +} + // FIXME // The following function uses non-standard font adjustiments (lines marked with FIXME). They make // the font bolder and more crisp. It was designed to look nice on nodes in the GUI but leaves the @@ -28,12 +44,11 @@ highp float msdf_alpha() { // We use this parameter to fatten somewhat font on low resolutions. The thershold and exact // value of this fattening was picked by trial an error, searching for best rendering effect. - highp float dpi_dilate = avg_msdf_unit_px < input_msdf_range*0.49 ? 1.0 : 0.0; - - highp vec3 msdf_sample = texture(input_atlas,tex_coord).rgb; - highp float sig_dist = median(msdf_sample) - 0.5; - highp float sig_dist_px = sig_dist * avg_msdf_unit_px; - highp float opacity = 0.5 + sig_dist_px + dpi_dilate * 0.08; + highp float dpi_dilate = avg_msdf_unit_px < input_msdf_range*0.49 ? 1.0 : 0.0; + highp vec3 msdf_sample = texture(input_atlas,tex_coord).rgb; + highp float sig_dist = median(msdf_sample) - 0.5; + highp float sig_dist_px = sig_dist * avg_msdf_unit_px + get_fatting(); + highp float opacity = 0.5 + sig_dist_px + dpi_dilate * 0.08; opacity += 0.6; // FIXME: Widen + sharpen opacity = clamp(opacity, 0.0, 1.0); opacity = pow(opacity,3.0); // FIXME: sharpen diff --git a/lib/rust/ensogl/component/text/src/typeface/glyph.rs b/lib/rust/ensogl/component/text/src/typeface/glyph.rs index a5ed1146cf66..8561822ed2d2 100644 --- a/lib/rust/ensogl/component/text/src/typeface/glyph.rs +++ b/lib/rust/ensogl/component/text/src/typeface/glyph.rs @@ -2,21 +2,36 @@ //! but can differ in all other aspects. use crate::prelude::*; -use ensogl_core::display::world::*; -use super::font; +use crate::typeface::font; + +use const_format::concatcp; use ensogl_core::data::color::Rgba; use ensogl_core::display; use ensogl_core::display::layout::Alignment; use ensogl_core::display::scene::Scene; use ensogl_core::display::symbol::material::Material; use ensogl_core::display::symbol::shader::builder::CodeTemplate; +use ensogl_core::display::world::*; use ensogl_core::system::gpu; use ensogl_core::system::gpu::texture; use font::Font; use font::GlyphRenderInfo; +// ================= +// === Constants === +// ================= + +mod style_flag { + use const_format::concatcp; + + pub const BOLD: i32 = 1 << 0; + + pub const GLSL_DEFINITIONS: &str = concatcp!("const int STYLE_BOLD_FLAG = ", BOLD, ";\n"); +} + + // ============= // === Glyph === @@ -25,17 +40,21 @@ use font::GlyphRenderInfo; /// Glyph texture. Contains all letters encoded in MSDF format. pub type Texture = gpu::Texture; -/// A glyph rendered on screen. The displayed character will be stretched to fit the entire size of -/// underlying sprite. -#[derive(Clone, CloneRef, Debug, Shrinkwrap)] +/// A glyph rendered on screen. +/// +/// The underlying sprite's size is automatically adjusted depending on char and font size set. +#[derive(Clone, CloneRef, Debug)] pub struct Glyph { - #[shrinkwrap(main_field)] sprite: Sprite, context: Context, font: Font, + font_size: Attribute, color: Attribute>, + style: Attribute, + sdf_bold: Attribute, atlas_index: Attribute, atlas: Uniform, + char: Rc>, } impl Glyph { @@ -48,11 +67,40 @@ impl Glyph { self.color.set(color.into().into()) } + pub fn is_bold(&self) -> bool { + self.style.get() & style_flag::BOLD != 0 + } + + pub fn set_bold(&self, value: bool) { + self.style.modify(|v| if value { *v |= style_flag::BOLD } else { *v &= !style_flag::BOLD }); + } + + pub fn sdf_bold(&self) -> f32 { + self.sdf_bold.get() + } + + pub fn set_sdf_bold(&self, value: f32) { + self.sdf_bold.set(value); + } + + pub fn font_size(&self) -> f32 { + self.font_size.get() + } + + pub fn set_font_size(&self, size: f32) { + self.font_size.set(size); + let glyph_info = self.font.glyph_info(self.char.get()); + self.sprite.size.set(glyph_info.scale.scale(size)); + } + /// Change the displayed character. pub fn set_char(&self, ch: char) { + self.char.set(ch); let glyph_info = self.font.glyph_info(ch); self.atlas_index.set(glyph_info.msdf_texture_glyph_id as f32); self.update_msdf_texture(); + let font_size = self.font_size(); + self.sprite.size.set(glyph_info.scale.scale(font_size)); } // FIXME: How does it work? Replace with better checking. @@ -90,7 +138,10 @@ pub struct System { context: Context, sprite_system: SpriteSystem, pub font: Font, + font_size: Buffer, color: Buffer>, + style: Buffer, + sdf_bold: Buffer, atlas_index: Buffer, atlas: Uniform, } @@ -119,7 +170,10 @@ impl System { sprite_system, font, atlas: symbol.variables().add_or_panic("atlas", texture), + font_size: mesh.instance_scope().add_buffer("font_size"), color: mesh.instance_scope().add_buffer("color"), + style: mesh.instance_scope().add_buffer("style"), + sdf_bold: mesh.instance_scope().add_buffer("sdf_bold"), atlas_index: mesh.instance_scope().add_buffer("atlas_index"), } } @@ -130,13 +184,17 @@ impl System { let context = self.context.clone(); let sprite = self.sprite_system.new_instance(); let instance_id = sprite.instance_id; + let font_size = self.font_size.at(instance_id); let color = self.color.at(instance_id); + let style = self.style.at(instance_id); + let sdf_bold = self.sdf_bold.at(instance_id); let atlas_index = self.atlas_index.at(instance_id); let font = self.font.clone_ref(); let atlas = self.atlas.clone(); + let char = default(); color.set(Vector4::new(0.0, 0.0, 0.0, 0.0)); atlas_index.set(0.0); - Glyph { sprite, context, font, color, atlas_index, atlas } + Glyph { sprite, context, font, font_size, color, style, sdf_bold, atlas_index, atlas, char } } /// Get underlying sprite system. @@ -154,9 +212,10 @@ impl display::Object for System { // === Material === #[cfg(target_os = "macos")] -const FUNCTIONS: &str = include_str!("glsl/glyph_mac.glsl"); +const FUNCTIONS: &str = + concatcp!(style_flag::GLSL_DEFINITIONS, include_str!("glsl/glyph_mac.glsl")); #[cfg(not(target_os = "macos"))] -const FUNCTIONS: &str = include_str!("glsl/glyph.glsl"); +const FUNCTIONS: &str = concatcp!(style_flag::GLSL_DEFINITIONS, include_str!("glsl/glyph.glsl")); const MAIN: &str = "output_color = color_from_msdf(); output_id=vec4(0.0,0.0,0.0,0.0);"; @@ -170,7 +229,10 @@ impl System { material.add_input("pixel_ratio", 1.0); material.add_input("z_zoom_1", 1.0); material.add_input("msdf_range", GlyphRenderInfo::MSDF_PARAMS.range as f32); + material.add_input("font_size", 10.0); material.add_input("color", Vector4::new(0.0, 0.0, 0.0, 1.0)); + material.add_input("style", 0); + material.add_input("sdf_bold", 0.0); // FIXME We need to use this output, as we need to declare the same amount of shader // FIXME outputs as the number of attachments to framebuffer. We should manage this more // FIXME intelligent. For example, we could allow defining output shader fragments, diff --git a/lib/rust/ensogl/example/glyph-system/src/lib.rs b/lib/rust/ensogl/example/glyph-system/src/lib.rs index dd1b2aa8689f..6968396be39f 100644 --- a/lib/rust/ensogl/example/glyph-system/src/lib.rs +++ b/lib/rust/ensogl/example/glyph-system/src/lib.rs @@ -28,7 +28,7 @@ use wasm_bindgen::prelude::*; use ensogl_core::data::color; use ensogl_text_msdf_sys::run_once_initialized; - +const CHARS_TO_TEST: &[&str] = &["abcdqwerty", "ABCDQUERTY"]; /// Main example runner. #[entry_point] @@ -37,20 +37,41 @@ pub fn main() { run_once_initialized(|| init(&World::new().displayed_in("root"))); } + fn init(world: &World) { let fonts = world.default_scene.extension::(); let font = fonts.load("DejaVuSans"); let glyph_system = glyph::System::new(&world.default_scene, font); let height = 32.0; let color = color::Rgba::new(0.5, 0.0, 0.0, 1.0); - let glyph = glyph_system.new_glyph(); - glyph.set_char('Q'); - glyph.set_color(color); - glyph.size.set(Vector2(height, height)); + let start_pos = Vector2(-300.0, -300.0); + + for (line_ind, line) in CHARS_TO_TEST.iter().enumerate() { + for (char_ind, char) in line.chars().enumerate() { + let glyph = glyph_system.new_glyph(); + let bold_glyph = glyph_system.new_glyph(); + glyph.set_char(char); + glyph.set_color(color); + glyph.set_font_size(height); + + bold_glyph.set_char(char); + bold_glyph.set_color(color); + bold_glyph.set_font_size(height); + bold_glyph.set_bold(true); + + let x = char_ind as f32 * (height + 4.0); + let y = line_ind as f32 * (height * 2.0 + 8.0); + let bold_y = y + height + 4.0; + glyph.set_position_xy(start_pos + Vector2(x, y)); + bold_glyph.set_position_xy(start_pos + Vector2(x, bold_y)); + world.add_child(&glyph); + world.add_child(&bold_glyph); + std::mem::forget(glyph); + std::mem::forget(bold_glyph); + } + } world.add_child(&glyph_system); - world.add_child(&glyph); world.keep_alive_forever(); std::mem::forget(glyph_system); - std::mem::forget(glyph); } diff --git a/lib/rust/ensogl/example/text-area/src/lib.rs b/lib/rust/ensogl/example/text-area/src/lib.rs index ed3d70d0608f..811200104da9 100644 --- a/lib/rust/ensogl/example/text-area/src/lib.rs +++ b/lib/rust/ensogl/example/text-area/src/lib.rs @@ -25,6 +25,8 @@ use wasm_bindgen::prelude::*; use ensogl_core::application::Application; use ensogl_core::display::navigation::navigator::Navigator; +use ensogl_text::style; +use ensogl_text::traits::*; use ensogl_text::Area; use ensogl_text_msdf_sys::run_once_initialized; @@ -40,27 +42,35 @@ pub fn entry_point_text_area() { } fn init(app: Application) { + use ensogl_text::Bytes; + use ensogl_text::Range; + let area = app.new_view::(); area.set_position_x(-100.0); - area.set_content( - "Et Eärello Endorenna utúlien.\nSinome maruvan ar Hildinyar tenn' Ambar-metta", - ); + let quote = "Et Eärello Endorenna utúlien.\nSinome maruvan ar Hildinyar tenn' Ambar-metta\n"; + let snowman = "\u{2603}"; + let zalgo = "Z̮̞̠͙͔ͅḀ̗̞͈̻̗Ḷ͙͎̯̹̞͓G̻O̭̗̮"; + let text = quote.to_string() + snowman + zalgo; + area.set_content(text.clone() + "\n" + text.as_str()); + area.set_font("DejaVuSans"); area.focus(); area.hover(); area.set_cursor_at_end(); + area.set_sdf_bold(Range::new(4.bytes(), 6.bytes()), style::SdfBold(0.02)); + area.set_sdf_bold(Range::new(7.bytes(), 15.bytes()), style::SdfBold(0.04)); + area.set_sdf_bold(Range::new(24.bytes(), 26.bytes()), style::SdfBold(0.02)); + area.set_sdf_bold(Range::new(37.bytes(), 41.bytes()), style::SdfBold(0.05)); + area.set_sdf_bold(Range::new(55.bytes(), 56.bytes()), style::SdfBold(0.03)); + let quote_length = Bytes::from(quote.len()); + let text_length = Bytes::from(text.len()); + area.set_sdf_bold(Range::new(quote_length, text_length), style::SdfBold(0.02)); + let scene = &app.display.default_scene; let navigator = Navigator::new(scene, &scene.camera()); app.display.default_scene.add_child(&area); - let keep = Some(area); - app.display - .on - .before_frame - .add(move |_frame| { - let _ = &keep; - }) - .forget(); mem::forget(navigator); mem::forget(app); + mem::forget(area); }