tetra 0.6.5

A simple 2D game framework written in Rust
Documentation
use tetra::graphics::scaling::{ScalingMode, ScreenScaler};
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::{Context, ContextBuilder, Event, State};

const LABEL: &str = "Press Space to cycle between scaling modes";
const SCREEN_WIDTH: f32 = 640.0;
const SCREEN_HEIGHT: f32 = 480.0;
const PANEL_WIDTH: f32 = SCREEN_WIDTH - 48.0;
const PANEL_HEIGHT: f32 = 48.0;
const PANEL_X: f32 = (SCREEN_WIDTH / 2.0) - (PANEL_WIDTH / 2.0);
const PANEL_Y: f32 = (SCREEN_HEIGHT / 2.0) - (PANEL_HEIGHT / 2.0);

struct GameState {
    scaler: ScreenScaler,
    panel_texture: Texture,
    panel_config: NineSlice,
    text: Text,
}

impl GameState {
    fn new(ctx: &mut Context) -> tetra::Result<GameState> {
        Ok(GameState {
            scaler: ScreenScaler::with_window_size(ctx, 640, 480, ScalingMode::Fixed)?,
            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::new(
                format!("{}\n{:?}", LABEL, ScalingMode::Fixed),
                Font::vector(ctx, "./examples/resources/DejaVuSansMono.ttf", 14.0)?,
            ),
        })
    }

    fn set_mode(&mut self, mode: ScalingMode) {
        self.scaler.set_mode(mode);
        self.text.set_content(format!("{}\n{:?}", LABEL, mode));
    }
}

impl State for GameState {
    fn update(&mut self, ctx: &mut Context) -> tetra::Result {
        if input::is_key_pressed(ctx, Key::Space) {
            let next = match self.scaler.mode() {
                ScalingMode::Fixed => ScalingMode::Stretch,
                ScalingMode::Stretch => ScalingMode::ShowAll,
                ScalingMode::ShowAll => ScalingMode::ShowAllPixelPerfect,
                ScalingMode::ShowAllPixelPerfect => ScalingMode::Crop,
                ScalingMode::Crop => ScalingMode::CropPixelPerfect,
                ScalingMode::CropPixelPerfect => ScalingMode::Fixed,
            };

            self.set_mode(next);
        }

        Ok(())
    }

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

        self.panel_texture.draw_nine_slice(
            ctx,
            &self.panel_config,
            PANEL_WIDTH,
            PANEL_HEIGHT,
            Vec2::new(PANEL_X, PANEL_Y),
        );
        self.text.draw(ctx, Vec2::new(PANEL_X + 8.0, PANEL_Y + 8.0));

        graphics::reset_canvas(ctx);
        graphics::clear(ctx, Color::BLACK);

        self.scaler.draw(ctx);

        Ok(())
    }

    fn event(&mut self, _: &mut Context, event: Event) -> tetra::Result {
        if let Event::Resized { width, height } = event {
            self.scaler.set_outer_size(width, height);
        }

        Ok(())
    }
}

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