tetra 0.8.0

A simple 2D game framework written in Rust
Documentation
use tetra::graphics::text::{Font, Text};
use tetra::graphics::{self, Color, NineSlice, Rectangle, Texture};
use tetra::input::{self, Key};
use tetra::math::Vec2;
use tetra::{window, Context, ContextBuilder, State};

const LABEL: &str = "\
Use the scroll wheel or W/S to read more!

This is a very long string. Too long, in fact, to fit into the box we're rendering! \
We want to be able to exclude any text that overflows the window.

An easy way to do this is to use graphics::set_scissor, which effectively tells the \
GPU to completely ignore any pixels outside of the specified rectangle when drawing. \
By setting the scissor to the size of this window, we can easily add a scroll \
function, without having to write any complex shaders or use expensive canvas \
switches.";

const PANEL_WIDTH: f32 = 320.0;
const PANEL_HEIGHT: f32 = 128.0;

struct GameState {
    panel_texture: Texture,
    panel_config: NineSlice,
    text: Text,
    text_pos: Vec2<f32>,
}

impl GameState {
    fn new(ctx: &mut Context) -> tetra::Result<GameState> {
        Ok(GameState {
            panel_texture: Texture::new(ctx, "./examples/resources/panel.png")?,
            panel_config: NineSlice::with_border(Rectangle::new(0.0, 0.0, 32.0, 32.0), 4.0),
            text: Text::wrapped(
                LABEL,
                Font::vector(ctx, "./examples/resources/DejaVuSansMono.ttf", 14.0)?,
                PANEL_WIDTH - 16.0,
            ),
            text_pos: Vec2::new(8.0, 8.0),
        })
    }
}

impl State for GameState {
    fn update(&mut self, ctx: &mut Context) -> tetra::Result {
        self.text_pos.y += (input::get_mouse_wheel_movement(ctx).y * 8) as f32;

        if input::is_key_down(ctx, Key::W) {
            self.text_pos.y += 2.0;
        }

        if input::is_key_down(ctx, Key::S) {
            self.text_pos.y -= 2.0;
        }

        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> tetra::Result {
        graphics::clear(ctx, Color::rgb(0.392, 0.584, 0.929));

        let (window_width, window_height) = window::get_size(ctx);

        let panel_pos = Vec2::new(
            (window_width as f32 / 2.0) - (PANEL_WIDTH / 2.0),
            (window_height as f32 / 2.0) - (PANEL_HEIGHT / 2.0),
        )
        .round();

        self.panel_texture.draw_nine_slice(
            ctx,
            &self.panel_config,
            PANEL_WIDTH,
            PANEL_HEIGHT,
            panel_pos,
        );

        graphics::set_scissor(
            ctx,
            Rectangle::new(
                panel_pos.x as i32 + 4,
                panel_pos.y as i32 + 4,
                PANEL_WIDTH as i32 - 8,
                PANEL_HEIGHT as i32 - 8,
            ),
        );

        self.text.draw(ctx, panel_pos + self.text_pos);

        graphics::reset_scissor(ctx);

        Ok(())
    }
}

fn main() -> tetra::Result {
    ContextBuilder::new("Scissor Rectangles", 640, 480)
        .resizable(true)
        .quit_on_escape(true)
        .build()?
        .run(GameState::new)
}