0x05. 视图操做

简化主函数

咱们已经肯定要让主函数知道的更少, 在 phi 模块内定义一个 spawn 函数, 入口只要调用这个函数, 就完成视图的处理, 咱们要让入口不须要了解到具体生成窗口之类的细节.canvas

pub fn spawn(title: &str) {
    let sdl2_context = sdl2::init().unwrap();
    let video = sdl2_context.video().unwrap();
    let window = video
        .window(title, 800, 600)
        .position_centered()
        .opengl()
        .build()
        .unwrap();
    let canvas = window.renderer().accelerated().build().unwrap();
    let events = Events::new(sdl2_context.event_pump().unwrap());
    let mut context = Phi::new(events, canvas);
    let mut current_view: Box<View> = box views::DefaultView;

    'running: loop {
        context.events.pump();
        match current_view.render(&mut context) {
            ViewAction::None => context.canvas.present(),
            ViewAction::Quit => break 'running
        }
    }
}
复制代码

如今主函数直接调用 spawn 就能够了ide

fn main() {
    phi::spawn("Arcade Shooter");
}
复制代码

若是以前的代码写过的话, 可能发现其实 spawn 相较以前多了一个 box 的关键字, 若是使用的是 nightly 版本的 Rust, 能够在 main.rs 处添加这个特性来启用这个关键字函数

#![feature(box_syntax)]
复制代码

要是不想用不稳定的特性, 就把使用 box 的地方改为oop

Box::new(views::DefaultView)
复制代码

为何忽然用起了 Box<T>, 引经据典一下, 根据官方的 Rust Book 的说法, 使用 Box<T> 的场景一般分ui

  • When you have a type whose size can’t be known at compile time and you want to use a value of that type in a context that requires an exact size
  • When you have a large amount of data and you want to transfer ownership but ensure the data won’t be copied when you do so
  • When you want to own a value and you care only that it’s a type that implements a particular trait rather than being of a specific type

很明显知足第三个使用场景, 咱们只须要关心一个值的特征, 不须要关心它具体的类型, 就跟鸭子类型同样, 走起来是鸭子, 叫起来是鸭子, 它本质上是否是鸭子并不重要, 咱们只要口感上是鸭子就行了.
如今只考虑一个值是否 impl View 就够了, 因此这里直接使用 Box<T>.spa

如今执行一下能够看到跟以前的效果没什么区别, 目前已经作到让主函数变成甩手掌柜了.code

视图之间的切换

咱们开发一个 iOS App, 一般都会涉及到多个控制器以前的切换, 如今这个应用, 也来实现一下视图切换的事. 把处理事件的宏添加一下空格键的定义, 以后只要按了空格键就会触发切换视图的事件.事件

events_macro! {
    keyboard: {
        key_escape: Escape,
        key_up: Up,
        key_down: Down,
        key_space: Space
    },
    else: {
        quit: Quit { .. }
    }
}
复制代码

这时候不得再也不次感慨一下实现了宏的感受真赞.ip

而后在 views 的模块内, 以前已经为 DefaultView 实现了 View 这个 trait, 如今再来实现一个 ViewB, DefaultView 也能够更名成 ViewA 为了好区分. 并且这里能够先写上空格键触发后的控制语句.ci

impl View for ViewB {
    fn render(&mut self, context: &mut Phi) -> ViewAction {
        let canvas = &mut context.canvas;
        let events = &mut context.events;
        if events.now.quit || events.now.key_escape == Some(true) {
            return ViewAction::Quit;
        }
        if let Some(true) = events.now.key_space {}
        canvas.set_draw_color(Color::RGB(0, 0, 0));
        canvas.clear();
        ViewAction::None
    }
}
复制代码

咱们在 loop 这块有使用模式匹配的代码, 使用到了 ViewAction 这个枚举类型, 那就改一下 phi 模块内的 ViewAction 的定义, 添加一个切换视图的类型

pub enum ViewAction {
    None,
    Quit,
    ChangeView(Box<View>),
}
复制代码

活用各类数据类型能够简化解决不少问题, 如今再改一下两个视图结构体对于 View 的实现, 主要是刚才的空格键事件判断这块

pub struct ViewA;
pub struct ViewB;

impl View for ViewA {
    fn render(&mut self, context: &mut Phi) -> ViewAction {
        ......
        if let Some(true) = events.now.key_space {
            return ViewAction::ChangeView(box ViewB);
        }
        ......
    }
}

impl View for ViewB {
    fn render(&mut self, context: &mut Phi) -> ViewAction {
        ......
        if let Some(true) = events.now.key_space {
            return ViewAction::ChangeView(box ViewA);
        }
        ......
    }
}
复制代码

而后天然是修改 loop 内的代码

'running: loop {
    context.events.pump();
    match current_view.render(&mut context) {
        ViewAction::None => context.canvas.present(),
        ViewAction::Quit => break 'running,
        ViewAction::ChangeView(view) => current_view = view,
    }
}
复制代码

如今的效果就是点击空格就会发生视图改变, 为了让本身看得出来, 能够把每一个视图的背景色设置成不同的.


如今已经完成了简化主函数的工做, 还搞定了视图的切换. 以后再处理其余方面的工做.

相关文章
相关标签/搜索