flo_draw 0.3.1

Hassle-free windowed 2D graphics rendering
Documentation
use flo_draw::*;
use flo_draw::canvas::*;

use std::sync::*;

///
/// Example that displays various effects that can be achieved via 
/// flo_canvas's text layout functions
///
pub fn main() {
    with_2d_graphics(|| {
        let lato        = CanvasFontFace::from_slice(include_bytes!("Lato-Regular.ttf"));
        let lato_bold   = CanvasFontFace::from_slice(include_bytes!("Lato-Bold.ttf"));

        // Create a window
        let canvas      = create_drawing_window("Text layout example");

        // Various text layout demonstrations
        canvas.draw(|gc| {
            // Set up the canvas
            gc.canvas_height(1000.0);
            gc.center_region(0.0, 0.0, 1000.0, 1000.0);

            // Load the fonts
            gc.define_font_data(FontId(1), Arc::clone(&lato));
            gc.define_font_data(FontId(2), Arc::clone(&lato_bold));
            gc.set_font_size(FontId(1), 18.0);
            gc.set_font_size(FontId(2), 18.0);
        });

        canvas.draw(|gc| {
            // Draw some text with layout in these fonts
            gc.fill_color(Color::Rgba(0.0, 0.0, 0.6, 1.0));

            // Start with a simple text layout
            gc.begin_line_layout(18.0, 900.0, TextAlignment::Left);
            gc.layout_text(FontId(1), "Simple text layout".to_string());
            gc.draw_text_layout();
        });

        canvas.draw(|gc| {
            // We can change fonts during a layout
            gc.begin_line_layout(18.0, 900.0 - 30.0, TextAlignment::Left);
            gc.layout_text(FontId(1), "We can change ".to_string());
            gc.layout_text(FontId(2), "fonts".to_string());
            gc.layout_text(FontId(1), " during layout ".to_string());
            gc.draw_text_layout();
        });

        canvas.draw(|gc| {
            // We can change colours during a layout
            gc.begin_line_layout(18.0, 900.0 - 60.0, TextAlignment::Left);
            gc.layout_text(FontId(1), "Or we could change ".to_string());
            gc.fill_color(Color::Rgba(0.8, 0.6, 0.0, 1.0));
            gc.layout_text(FontId(1), "colours".to_string());
            gc.fill_color(Color::Rgba(0.0, 0.0, 0.6, 1.0));
            gc.layout_text(FontId(1), " during layout ".to_string());
            gc.draw_text_layout();
        });

        canvas.draw(|gc| {
            // We can change font sizes during a layout
            gc.begin_line_layout(18.0, 900.0 - 100.0, TextAlignment::Left);
            gc.layout_text(FontId(1), "It's also possible to alter ".to_string());
            gc.set_font_size(FontId(1), 36.0);
            gc.layout_text(FontId(1), "sizes".to_string());
            gc.set_font_size(FontId(1), 18.0);
            gc.layout_text(FontId(1), " during layout ".to_string());
            gc.draw_text_layout();
        });

        canvas.draw(|gc| {
            // Can align text with all the effects
            gc.begin_line_layout(500.0, 500.0, TextAlignment::Center);
            gc.layout_text(FontId(1), "Text layout demonstration, with changing ".to_string());
            gc.set_font_size(FontId(1), 36.0);
            gc.layout_text(FontId(1), "sizes,".to_string());
            gc.set_font_size(FontId(1), 18.0);
            gc.fill_color(Color::Rgba(0.8, 0.6, 0.0, 1.0));
            gc.layout_text(FontId(1), " colours,".to_string());
            gc.fill_color(Color::Rgba(0.0, 0.0, 0.6, 1.0));
            gc.layout_text(FontId(2), " fonts,".to_string());
            gc.layout_text(FontId(1), " and center alignment ".to_string());
            gc.draw_text_layout();

            gc.begin_line_layout(1000.0-18.0, 80.0, TextAlignment::Right);
            gc.layout_text(FontId(1), "Right alignment is supported too".to_string());
            gc.draw_text_layout();
        });

        canvas.draw(|gc| {
            // Can perform fully manual layout, and annotate with other drawing
            let mut text_layout = CanvasFontLineLayout::new(&lato, 18.0);
            text_layout.add_text("Performing layout manually is also possible");

            // Calling 'align_transform' moves the text to its final position, and 'to_drawing' generates the drawing instructions for the layout (the layout needs to know the FontId to generate drawing instructions)
            text_layout.align_transform(500.0, 400.0, TextAlignment::Center);
            gc.draw_list(text_layout.to_drawing(FontId(1)));
        });

        canvas.draw(|gc| {
            // We can use the measure() and draw() functions to add annotations to the text as we generate the layout
            // font_metrics(em_size) gives some information about a particular font
            let lato_metrics        = lato.font_metrics(18.0).unwrap();
            let mut text_layout     = CanvasFontLineLayout::new(&lato, 18.0);
            
            text_layout.add_text("Manual layout allows ");

            // 'measure()' interrupts the layout, so measuring half-way between 'f' and 'i' will force the layout to produce no ligature
            let start_pos   = text_layout.measure();
            text_layout.add_text("custom");
            let end_pos     = text_layout.measure();

            // start_pos and end_pos show where the word 'custom' began and ended (as well as giving the overall bounding box)
            // CanvasFontLineLayout implements GraphicsContext, so we draw straight to the layout as we're constructing it
            // `align_transform` later on will move our drawing with the text
            text_layout.new_path();
            text_layout.move_to(start_pos.pos.x() as _, start_pos.pos.y() as f32 + lato_metrics.underline_position.unwrap().offset);
            text_layout.line_to(end_pos.pos.x() as _, end_pos.pos.y() as f32 + lato_metrics.underline_position.unwrap().offset);
            text_layout.stroke_color(Color::Rgba(0.8, 0.6, 0.0, 1.0));
            text_layout.line_width(lato_metrics.underline_position.unwrap().thickness);
            text_layout.stroke();

            text_layout.new_path();
            text_layout.move_to(start_pos.pos.x() as _, start_pos.pos.y() as f32 + lato_metrics.ascender);
            text_layout.line_to(end_pos.pos.x() as _, end_pos.pos.y() as f32 + lato_metrics.ascender);
            text_layout.stroke_color(Color::Rgba(0.8, 0.6, 0.0, 1.0));
            text_layout.line_width(lato_metrics.underline_position.unwrap().thickness);
            text_layout.stroke();

            let mid_point = (start_pos.pos + end_pos.pos) * 0.5;
            text_layout.move_to(mid_point.x() as _, mid_point.y() as f32 + lato_metrics.underline_position.unwrap().offset);
            text_layout.line_to(mid_point.x() as _, mid_point.y() as f32 + lato_metrics.underline_position.unwrap().offset - 8.0);
            text_layout.stroke_color(Color::Rgba(0.8, 0.6, 0.0, 1.0));
            text_layout.line_width(2.0);
            text_layout.stroke();

            // Even possible to lay out text in text ('layout_text' is from the graphics context, so we wind up performing recursive layout here)
            text_layout.begin_line_layout(mid_point.x() as _, mid_point.y() as f32-lato_metrics.underline_position.unwrap().offset - 30.0, TextAlignment::Center);
            text_layout.layout_text(FontId(1), "here".to_string());
            text_layout.draw_text_layout();

            // Finish up the text...
            text_layout.add_text(" drawing effects, such as this underline");

            // ... and align it using align_transform so the underline is moved along with the text
            text_layout.align_transform(500.0, 370.0, TextAlignment::Center);
            gc.draw_list(text_layout.to_drawing(FontId(1)));
        });

        canvas.draw(|gc| {
            // It's still possible to change fonts and colours while using a manual layout
            let mut text_layout = CanvasFontLineLayout::new(&lato, 18.0);
            text_layout.add_text("Changing ");
            text_layout.fill_color(Color::Rgba(0.8, 0.6, 0.0, 1.0));
            text_layout.add_text("colour");
            text_layout.fill_color(Color::Rgba(0.0, 0.0, 0.6, 1.0));
            text_layout.add_text(" and ");

            // FontId 1 = lato, FontId 2 = lato bold (note we supply the old font ID and not the new one here!)
            let mut text_layout = text_layout.continue_with_new_font(FontId(1), &lato_bold, 18.0);
            text_layout.add_text("font");
            let mut text_layout = text_layout.continue_with_new_font(FontId(2), &lato, 18.0);
            text_layout.add_text(" is still possible with manual layouts");

            // Calling 'align_transform' moves the text to its final position, and 'to_drawing' generates the drawing instructions for the layout (the layout needs to know the FontId to generate drawing instructions)
            text_layout.align_transform(500.0, 310.0, TextAlignment::Center);
            gc.draw_list(text_layout.to_drawing(FontId(1)));
        });
    });
}