<commit_before>extern crate rustbox;

use buffer::{Line, Buffer};
use cursor::Direction;
use cursor::Cursor;
use input::Input;
use uibuf::UIBuffer;

use utils;

/// A View is an abstract Window (into a Buffer).
///
/// It draws a portion of a Buffer to a UIBuffer which in turn is drawn to the
/// screen. It maintains the status bar for the current view, the "dirty status"
/// which is whether the buffer has been modified or not and a number of other
/// pieces of information.
pub struct View<'v> {
    pub buffer: Buffer,
    // the line number of the topmost `Line` for the View to render
    pub top_line_num: uint,
    pub cursor: Cursor<'v>,

    uibuf: UIBuffer,
    threshold: int,
}

impl<'v> View<'v> {
    pub fn new(source: Input) -> View<'v> {
        let mut buffer = match source {
            Input::Filename(path) => {
                match path {
                    Some(s) => Buffer::new_from_file(Path::new(s)),
                    None    => Buffer::new_empty(),
                }
            },
                Input::Stdin(reader) => {
                Buffer::new_from_reader(reader)
            },
        };

        let height: uint = utils::get_term_height();
        let width: uint = utils::get_term_width();

        // NOTE(greg): this may not play well with resizing
        let uibuf = UIBuffer::new(width, height);

        let mut cursor = Cursor::new();
        cursor.set_line(Some(&mut buffer.lines[0]));

        View {
            buffer: buffer,
            top_line_num: 0,
            cursor: cursor,
            uibuf: uibuf,
            threshold: 5,
        }
    }

    /// Clear the buffer
    ///
    /// Fills every cell in the UIBuffer with the space (' ') char.
    pub fn clear(&mut self) {
        self.uibuf.fill(' ');
        self.uibuf.draw_everything();
    }

    pub fn get_height(&self) -> uint {
        // NOTE(greg): when the status bar needs to move up, this value should be changed
        self.uibuf.get_height() -1
    }

    pub fn get_width(&self) -> uint {
        self.uibuf.get_width()
    }

    pub fn draw(&mut self) {
        let end_line = self.get_height();
        let num_lines = self.buffer.lines.len();
        let lines_to_draw = self.buffer.lines.slice(self.top_line_num, num_lines);

        for (index, line) in lines_to_draw.iter().enumerate() {
            if index < end_line {
                draw_line(&mut self.uibuf, line)
            }
        }

        self.uibuf.draw_everything();
    }

    pub fn draw_status(&mut self) {
        let buffer_status = self.buffer.get_status_text();
        let cursor_status = self.cursor.get_status_text();
        let status_text = format!("{} {}", buffer_status, cursor_status).into_bytes();
        let status_text_len = status_text.len();
        let width = self.get_width();
        let height = self.get_height();

        for index in range(0, width) {
            let mut ch: char = ' ';
            if index < status_text_len {
                ch = status_text[index] as char;
            }
            self.uibuf.update_cell(index, height, ch, rustbox::Color::Black, rustbox::Color::Blue);
        }

        self.uibuf.draw_range(height, height+1);
    }

    pub fn draw_cursor(&self) {
        let offset = self.cursor.get_offset();
        let linenum = self.cursor.get_linenum();

        utils::draw_cursor(offset, linenum-self.top_line_num);
    }

    pub fn resize(&mut self) {
        let width = self.uibuf.get_width();
        self.clear();
        self.uibuf = UIBuffer::new(width, 15);
    }

    pub fn move_cursor(&mut self, direction: Direction) {
        match direction {
            Direction::Up    => { self.move_cursor_up(); },
            Direction::Down  => { self.move_cursor_down(); },
            Direction::Right => { self.move_cursor_right(); },
            Direction::Left  => { self.cursor.move_left(); },
        }
    }

    fn move_cursor_right(&mut self) {
        let cursor_offset = self.cursor.get_offset();
        let next_offset = cursor_offset + 1;
        let width = self.get_width() - 1;

        if next_offset < width {
            self.cursor.move_right()
        }
    }

    // TODO(greg): refactor this method with move_cursor_down
    pub fn move_cursor_up(&mut self) {
        let cursor_linenum = self.cursor.get_linenum();
        let prev_linenum = cursor_linenum - 1;

        let num_lines = self.buffer.lines.len() - 1;
        if prev_linenum > num_lines { return }

        self.set_cursor_line(prev_linenum);

        let cursor_linenum = self.cursor.get_linenum() as int;
        let cursor_offset = cursor_linenum - self.top_line_num as int;

        if cursor_offset < self.threshold {
            let times = cursor_offset - self.threshold;
            self.move_top_line_n_times(times);
        }

    }

    // TODO(greg): refactor this method with move_cursor_up
    pub fn move_cursor_down(&mut self) {
        let cursor_linenum = self.cursor.get_linenum();
        let next_linenum = cursor_linenum + 1;

        let num_lines = self.buffer.lines.len() - 1;
        if next_linenum > num_lines { return }

        self.set_cursor_line(next_linenum);

        let cursor_linenum = self.cursor.get_linenum() as int;
        let cursor_offset = cursor_linenum - self.top_line_num as int;
        let height = self.get_height() as int;

        if cursor_offset >= (height - self.threshold) {
            let times = cursor_offset - (height - self.threshold) + 1;
            self.move_top_line_n_times(times);
        }
    }

    fn set_cursor_line(&mut self, linenum: uint) {
        let line = &mut self.buffer.lines[linenum];
        self.cursor.set_line(Some(line));
    }

    fn move_top_line_n_times(&mut self, mut num_times: int) {
        if num_times == 0 { return }

        // moving down
        if num_times > 0 {
            while num_times > 0 {
                self.top_line_num += 1;
                num_times -= 1;
            }
            return
        }

        // moving up
        if num_times < 0 {
            while num_times < 0 {
                if self.top_line_num == 0 { return }
                self.top_line_num -= 1;
                num_times += 1;
            }
            return
        }
    }

    pub fn delete_char(&mut self, direction: Direction) {
        let (offset, line_num) = self.cursor.get_position();

        if offset == 0 && direction.is_left() {
            let offset = self.buffer.join_line_with_previous(offset, line_num);
            self.move_cursor_up();
            self.cursor.set_offset(offset);
            return
        }

        let line_len = self.cursor.get_line_length();
        if offset == line_len && direction.is_right() {
            self.buffer.join_line_with_previous(offset, line_num+1);
            return
        }

        match direction {
            Direction::Left  => self.cursor.delete_backward_char(),
            Direction::Right => self.cursor.delete_forward_char(),
            _                => {}
        }
    }

    pub fn insert_tab(&mut self) {
        // A tab is just 4 spaces
        for _ in range(0i, 4) {
            self.insert_char(' ');
        }
    }

    pub fn insert_char(&mut self, ch: char) {
        self.cursor.insert_char(ch);
    }

    pub fn insert_line(&mut self) {
        let (offset, line_num) = self.cursor.get_position();
        self.buffer.insert_line(offset, line_num);

        self.move_cursor_down();
        self.cursor.set_offset(0);
    }
}

pub fn draw_line(buf: &mut UIBuffer, line: &Line) {
    let width = buf.get_width() -1;
    let index = line.linenum;
    let mut internal_index = 0;
    for ch in line.data.iter() {

        if internal_index < width {
            let ch = *ch;
            match ch as char {
                '\t' => {
                    // todo(greg): draw four spaces
                    // need to update how the cursor is rendered before doing this
                    // think about what will happen if the cursor sees a single '\t' char
                    // in the line, however this is represented as four chars to the user
                    // the actual cursor position will become separated from the drawn
                    // cursor position.
                    rustbox::shutdown();
                    panic!("found tab chars - can't process these right now")
                }
                _ => {
                    // draw the character
                    buf.update_cell_content(internal_index, index, ch as char);
                }
            }
            internal_index += 1;
        }

        // if the line is longer than the width of the view, draw a special char
        if internal_index == width {
            // fixme(greg): iota cant render this line correctly right now
            buf.update_cell_content(internal_index, index, '→');
            break;
        }
    }
}

#[cfg(test)]
mod tests {

    use buffer::{Line, Buffer};
    use cursor::{Cursor, Direction};
    use view::View;
    use uibuf::UIBuffer;
    use utils::data_from_str;

    fn setup_view<'v>() -> View<'v> {
        let mut view = View {
            buffer: Buffer::new(),
            top_line_num: 0,
            cursor: Cursor::new(),
            uibuf: UIBuffer::new(50, 50),
            threshold: 5,
        };

        let first_line = Line::new(data_from_str("test"), 0);
        let second_line = Line::new(data_from_str("second"), 1);

        view.buffer.lines = vec!(first_line, second_line);
        view.cursor.set_line(Some(&mut view.buffer.lines[0]));

        return view
    }

    #[test]
    fn test_move_cursor_down() {
        let mut view = setup_view();
        view.move_cursor_down();

        assert_eq!(view.cursor.get_linenum(), 1);
        assert_eq!(view.cursor.get_line().data, data_from_str("second"));
    }

    #[test]
    fn test_move_cursor_up() {
        let mut view = setup_view();
        view.move_cursor_down();
        view.move_cursor_up();
        assert_eq!(view.cursor.get_linenum(), 0);
        assert_eq!(view.cursor.get_line().data, data_from_str("test"));
    }

    #[test]
    fn test_insert_line() {
        let mut view = setup_view();
        view.cursor.move_right();
        view.insert_line();

        assert_eq!(view.buffer.lines.len(), 3);
        assert_eq!(view.cursor.get_offset(), 0);
        assert_eq!(view.cursor.get_line().linenum, 1);
    }

    #[test]
    fn test_insert_char() {
        let mut view = setup_view();
        view.insert_char('t');

        assert_eq!(view.cursor.get_line().data, data_from_str("ttest"));
    }

    #[test]
    fn test_delete_char_to_right() {
        let mut view = setup_view();
        view.delete_char(Direction::Right);

        assert_eq!(view.cursor.get_line().data, data_from_str("est"));
    }

    #[test]
    fn test_delete_char_to_left() {
        let mut view = setup_view();
        view.cursor.move_right();
        view.delete_char(Direction::Left);

        assert_eq!(view.cursor.get_line().data, data_from_str("est"));
    }

    #[test]
    fn test_delete_char_at_start_of_line() {
        let mut view = setup_view();
        view.move_cursor_down();
        view.delete_char(Direction::Left);

        assert_eq!(view.cursor.get_line().data, data_from_str("testsecond"));
    }

    #[test]
    fn test_delete_char_at_end_of_line() {
        let mut view = setup_view();
        view.cursor.set_offset(4);
        view.delete_char(Direction::Right);

        assert_eq!(view.cursor.get_line().data, data_from_str("testsecond"));
    }

    #[test]
    fn deleting_backward_at_start_of_first_line_does_nothing() {
        let mut view = setup_view();
        view.delete_char(Direction::Left);

        assert_eq!(view.buffer.lines.len(), 2);
        assert_eq!(view.cursor.get_line().data, data_from_str("test"));
    }
}
<commit_msg>Dont panic when trying to press Delete on empty line<commit_after>extern crate rustbox;

use buffer::{Line, Buffer};
use cursor::Direction;
use cursor::Cursor;
use input::Input;
use uibuf::UIBuffer;

use utils;

/// A View is an abstract Window (into a Buffer).
///
/// It draws a portion of a Buffer to a UIBuffer which in turn is drawn to the
/// screen. It maintains the status bar for the current view, the "dirty status"
/// which is whether the buffer has been modified or not and a number of other
/// pieces of information.
pub struct View<'v> {
    pub buffer: Buffer,
    // the line number of the topmost `Line` for the View to render
    pub top_line_num: uint,
    pub cursor: Cursor<'v>,

    uibuf: UIBuffer,
    threshold: int,
}

impl<'v> View<'v> {
    pub fn new(source: Input) -> View<'v> {
        let mut buffer = match source {
            Input::Filename(path) => {
                match path {
                    Some(s) => Buffer::new_from_file(Path::new(s)),
                    None    => Buffer::new_empty(),
                }
            },
                Input::Stdin(reader) => {
                Buffer::new_from_reader(reader)
            },
        };

        let height: uint = utils::get_term_height();
        let width: uint = utils::get_term_width();

        // NOTE(greg): this may not play well with resizing
        let uibuf = UIBuffer::new(width, height);

        let mut cursor = Cursor::new();
        cursor.set_line(Some(&mut buffer.lines[0]));

        View {
            buffer: buffer,
            top_line_num: 0,
            cursor: cursor,
            uibuf: uibuf,
            threshold: 5,
        }
    }

    /// Clear the buffer
    ///
    /// Fills every cell in the UIBuffer with the space (' ') char.
    pub fn clear(&mut self) {
        self.uibuf.fill(' ');
        self.uibuf.draw_everything();
    }

    pub fn get_height(&self) -> uint {
        // NOTE(greg): when the status bar needs to move up, this value should be changed
        self.uibuf.get_height() -1
    }

    pub fn get_width(&self) -> uint {
        self.uibuf.get_width()
    }

    pub fn draw(&mut self) {
        let end_line = self.get_height();
        let num_lines = self.buffer.lines.len();
        let lines_to_draw = self.buffer.lines.slice(self.top_line_num, num_lines);

        for (index, line) in lines_to_draw.iter().enumerate() {
            if index < end_line {
                draw_line(&mut self.uibuf, line)
            }
        }

        self.uibuf.draw_everything();
    }

    pub fn draw_status(&mut self) {
        let buffer_status = self.buffer.get_status_text();
        let cursor_status = self.cursor.get_status_text();
        let status_text = format!("{} {}", buffer_status, cursor_status).into_bytes();
        let status_text_len = status_text.len();
        let width = self.get_width();
        let height = self.get_height();

        for index in range(0, width) {
            let mut ch: char = ' ';
            if index < status_text_len {
                ch = status_text[index] as char;
            }
            self.uibuf.update_cell(index, height, ch, rustbox::Color::Black, rustbox::Color::Blue);
        }

        self.uibuf.draw_range(height, height+1);
    }

    pub fn draw_cursor(&self) {
        let offset = self.cursor.get_offset();
        let linenum = self.cursor.get_linenum();

        utils::draw_cursor(offset, linenum-self.top_line_num);
    }

    pub fn resize(&mut self) {
        let width = self.uibuf.get_width();
        self.clear();
        self.uibuf = UIBuffer::new(width, 15);
    }

    pub fn move_cursor(&mut self, direction: Direction) {
        match direction {
            Direction::Up    => { self.move_cursor_up(); },
            Direction::Down  => { self.move_cursor_down(); },
            Direction::Right => { self.move_cursor_right(); },
            Direction::Left  => { self.cursor.move_left(); },
        }
    }

    fn move_cursor_right(&mut self) {
        let cursor_offset = self.cursor.get_offset();
        let next_offset = cursor_offset + 1;
        let width = self.get_width() - 1;

        if next_offset < width {
            self.cursor.move_right()
        }
    }

    // TODO(greg): refactor this method with move_cursor_down
    pub fn move_cursor_up(&mut self) {
        let cursor_linenum = self.cursor.get_linenum();
        let prev_linenum = cursor_linenum - 1;

        let num_lines = self.buffer.lines.len() - 1;
        if prev_linenum > num_lines { return }

        self.set_cursor_line(prev_linenum);

        let cursor_linenum = self.cursor.get_linenum() as int;
        let cursor_offset = cursor_linenum - self.top_line_num as int;

        if cursor_offset < self.threshold {
            let times = cursor_offset - self.threshold;
            self.move_top_line_n_times(times);
        }

    }

    // TODO(greg): refactor this method with move_cursor_up
    pub fn move_cursor_down(&mut self) {
        let cursor_linenum = self.cursor.get_linenum();
        let next_linenum = cursor_linenum + 1;

        let num_lines = self.buffer.lines.len() - 1;
        if next_linenum > num_lines { return }

        self.set_cursor_line(next_linenum);

        let cursor_linenum = self.cursor.get_linenum() as int;
        let cursor_offset = cursor_linenum - self.top_line_num as int;
        let height = self.get_height() as int;

        if cursor_offset >= (height - self.threshold) {
            let times = cursor_offset - (height - self.threshold) + 1;
            self.move_top_line_n_times(times);
        }
    }

    fn set_cursor_line(&mut self, linenum: uint) {
        let line = &mut self.buffer.lines[linenum];
        self.cursor.set_line(Some(line));
    }

    fn move_top_line_n_times(&mut self, mut num_times: int) {
        if num_times == 0 { return }

        // moving down
        if num_times > 0 {
            while num_times > 0 {
                self.top_line_num += 1;
                num_times -= 1;
            }
            return
        }

        // moving up
        if num_times < 0 {
            while num_times < 0 {
                if self.top_line_num == 0 { return }
                self.top_line_num -= 1;
                num_times += 1;
            }
            return
        }
    }

    pub fn delete_char(&mut self, direction: Direction) {
        let (offset, line_num) = self.cursor.get_position();

        if offset == 0 && direction.is_left() {
            let offset = self.buffer.join_line_with_previous(offset, line_num);
            self.move_cursor_up();
            self.cursor.set_offset(offset);
            return
        }

        let line_len = self.cursor.get_line_length();
        if offset == line_len && direction.is_right() && line_len > 0 {
            self.buffer.join_line_with_previous(offset, line_num+1);
            return
        }

        match direction {
            Direction::Left  => self.cursor.delete_backward_char(),
            Direction::Right => self.cursor.delete_forward_char(),
            _                => {}
        }
    }

    pub fn insert_tab(&mut self) {
        // A tab is just 4 spaces
        for _ in range(0i, 4) {
            self.insert_char(' ');
        }
    }

    pub fn insert_char(&mut self, ch: char) {
        self.cursor.insert_char(ch);
    }

    pub fn insert_line(&mut self) {
        let (offset, line_num) = self.cursor.get_position();
        self.buffer.insert_line(offset, line_num);

        self.move_cursor_down();
        self.cursor.set_offset(0);
    }
}

pub fn draw_line(buf: &mut UIBuffer, line: &Line) {
    let width = buf.get_width() -1;
    let index = line.linenum;
    let mut internal_index = 0;
    for ch in line.data.iter() {

        if internal_index < width {
            let ch = *ch;
            match ch as char {
                '\t' => {
                    // todo(greg): draw four spaces
                    // need to update how the cursor is rendered before doing this
                    // think about what will happen if the cursor sees a single '\t' char
                    // in the line, however this is represented as four chars to the user
                    // the actual cursor position will become separated from the drawn
                    // cursor position.
                    rustbox::shutdown();
                    panic!("found tab chars - can't process these right now")
                }
                _ => {
                    // draw the character
                    buf.update_cell_content(internal_index, index, ch as char);
                }
            }
            internal_index += 1;
        }

        // if the line is longer than the width of the view, draw a special char
        if internal_index == width {
            // fixme(greg): iota cant render this line correctly right now
            buf.update_cell_content(internal_index, index, '→');
            break;
        }
    }
}

#[cfg(test)]
mod tests {

    use buffer::{Line, Buffer};
    use cursor::{Cursor, Direction};
    use view::View;
    use uibuf::UIBuffer;
    use utils::data_from_str;

    fn setup_view<'v>() -> View<'v> {
        let mut view = View {
            buffer: Buffer::new(),
            top_line_num: 0,
            cursor: Cursor::new(),
            uibuf: UIBuffer::new(50, 50),
            threshold: 5,
        };

        let first_line = Line::new(data_from_str("test"), 0);
        let second_line = Line::new(data_from_str("second"), 1);

        view.buffer.lines = vec!(first_line, second_line);
        view.cursor.set_line(Some(&mut view.buffer.lines[0]));

        return view
    }

    #[test]
    fn test_move_cursor_down() {
        let mut view = setup_view();
        view.move_cursor_down();

        assert_eq!(view.cursor.get_linenum(), 1);
        assert_eq!(view.cursor.get_line().data, data_from_str("second"));
    }

    #[test]
    fn test_move_cursor_up() {
        let mut view = setup_view();
        view.move_cursor_down();
        view.move_cursor_up();
        assert_eq!(view.cursor.get_linenum(), 0);
        assert_eq!(view.cursor.get_line().data, data_from_str("test"));
    }

    #[test]
    fn test_insert_line() {
        let mut view = setup_view();
        view.cursor.move_right();
        view.insert_line();

        assert_eq!(view.buffer.lines.len(), 3);
        assert_eq!(view.cursor.get_offset(), 0);
        assert_eq!(view.cursor.get_line().linenum, 1);
    }

    #[test]
    fn test_insert_char() {
        let mut view = setup_view();
        view.insert_char('t');

        assert_eq!(view.cursor.get_line().data, data_from_str("ttest"));
    }

    #[test]
    fn test_delete_char_to_right() {
        let mut view = setup_view();
        view.delete_char(Direction::Right);

        assert_eq!(view.cursor.get_line().data, data_from_str("est"));
    }

    #[test]
    fn test_delete_char_to_left() {
        let mut view = setup_view();
        view.cursor.move_right();
        view.delete_char(Direction::Left);

        assert_eq!(view.cursor.get_line().data, data_from_str("est"));
    }

    #[test]
    fn test_delete_char_at_start_of_line() {
        let mut view = setup_view();
        view.move_cursor_down();
        view.delete_char(Direction::Left);

        assert_eq!(view.cursor.get_line().data, data_from_str("testsecond"));
    }

    #[test]
    fn test_delete_char_at_end_of_line() {
        let mut view = setup_view();
        view.cursor.set_offset(4);
        view.delete_char(Direction::Right);

        assert_eq!(view.cursor.get_line().data, data_from_str("testsecond"));
    }

    #[test]
    fn delete_char_when_line_is_empty_does_nothing() {
        let mut view = setup_view();
        view.buffer.lines = vec!(Line::new(Vec::new(), 0));
        view.cursor.set_line(Some(&mut view.buffer.lines[0]));
        view.delete_char(Direction::Right);
        assert_eq!(view.cursor.get_line().data, data_from_str(""));
    }

    #[test]
    fn deleting_backward_at_start_of_first_line_does_nothing() {
        let mut view = setup_view();
        view.delete_char(Direction::Left);

        assert_eq!(view.buffer.lines.len(), 2);
        assert_eq!(view.cursor.get_line().data, data_from_str("test"));
    }
}
<|endoftext|>


<commit_before>
use rustbox::{ RustBox };
use ::control::Control;
use ::keyboard::Key;

pub struct Controls<'a> {
    rustbox: &'a RustBox,
    tb_d4: ::control::TextBox,
    tb_d6: ::control::TextBox,
    tb_d8: ::control::TextBox,
    tb_d10: ::control::TextBox,
    tb_d12: ::control::TextBox,
    tb_d20: ::control::TextBox,
    tb_mod: ::control::TextBox,
    ls_saved: ::control::ListSelect,
    ls_history: ::control::ListSelect,
    selected: usize,
    bottom_text: String
}

impl<'a> Controls<'a> {

    pub fn initialize(rustbox: &'a RustBox) -> Self {
        let tb_d4 = ::control::TextBox::new(" d4", 3, true);
        let tb_d6 = ::control::TextBox::new(" d6", 3, true);
        let tb_d8 = ::control::TextBox::new(" d8", 3, true);
        let tb_d10 = ::control::TextBox::new("d10", 3, true);
        let tb_d12 = ::control::TextBox::new("d12", 3, true);
        let tb_d20 = ::control::TextBox::new("d20", 3, true);
        let tb_mod = ::control::TextBox::new("mod", 3, false);
        let ls_saved = ::control::ListSelect::new("Saved");
        let ls_history = ::control::ListSelect::new("History");

        let mut result = Controls {
            rustbox: rustbox,
            tb_d4: tb_d4,
            tb_d6: tb_d6,
            tb_d8: tb_d8,
            tb_d10: tb_d10,
            tb_d12: tb_d12,
            tb_d20: tb_d20,
            tb_mod: tb_mod,
            ls_saved: ls_saved,
            ls_history: ls_history,
            selected: 0,
            bottom_text: String::from_str("")
        };

        result.tb_d4.set_selected(true);
        result
    }

    pub fn reposition(&mut self) {
        let mut top_y = self.rustbox.height() / 2 - 7;
        if top_y <= 0 {
            top_y = 2;
        }

        for (i, tb) in self.textboxes_mut().iter_mut().enumerate() {
            tb.set_location(1, top_y + i * 2);
        }

        self.ls_saved.set_location(self.rustbox.width() - 59, 1);
        self.ls_saved.set_size(29, self.rustbox.height() - 3);
        self.ls_history.set_location(self.rustbox.width() - 29, 1);
        self.ls_history.set_size(29, self.rustbox.height() - 3);
    }

    pub fn redraw(&self) {
        for tb in self.textboxes().iter() {
            tb.redraw(self.rustbox);
        }
        self.ls_history.redraw(self.rustbox);
        self.ls_saved.redraw(self.rustbox);
        self.rustbox.print(0,
                           self.rustbox.height() - 1,
                           ::rustbox::RB_NORMAL,
                           ::rustbox::Color::White,
                           ::rustbox::Color::Black,
                           ::std::iter::repeat(' ').take(self.rustbox.width()).collect::<String>().as_slice());
        self.rustbox.print(0,
                           self.rustbox.height() - 1,
                           ::rustbox::RB_NORMAL,
                           ::rustbox::Color::White,
                           ::rustbox::Color::Black,
                           self.bottom_text.as_slice());
        self.rustbox.present();
    }

    pub fn select_next(&mut self, skip_lists: bool) {
        self.selected_control_mut().set_selected(false);
        self.selected += 1;
        if self.selected == 7 && skip_lists {
            self.selected = 0;
        }
        if self.selected == 7 && !self.ls_saved.has_items() {
            self.selected += 1;
        }
        if self.selected == 8 && !self.ls_history.has_items() {
            self.selected += 1;
        }
        if self.selected == 9 { self.selected = 0; }
        self.selected_control_mut().set_selected(true);
    }

    pub fn select_prev(&mut self) {
        self.selected_control_mut().set_selected(false);

        if self.selected == 0 {
            self.selected = 6;
        } else {
            self.selected -= 1;
        }
        if self.selected == 8 && !self.ls_history.has_items() {
            self.selected -= 1;
        }
        if self.selected == 7 && !self.ls_saved.has_items() {
            self.selected -= 1;
        }
        self.selected_control_mut().set_selected(true);
    }

    fn selected_control_mut(&mut self) -> &mut Control {
        match self.selected {
            0 => &mut self.tb_d4,
            1 => &mut self.tb_d6,
            2 => &mut self.tb_d8,
            3 => &mut self.tb_d10,
            4 => &mut self.tb_d12,
            5 => &mut self.tb_d20,
            6 => &mut self.tb_mod,
            7 => &mut self.ls_saved,
            8 => &mut self.ls_history,
            _ => panic!("Could not find selected control")
        }
    }

    pub fn handle_key(&mut self, key: ::keyboard::Key) {
        match key {
            Key::Enter => self.roll_dice(),
            Key::Tab => self.select_next(false),
            Key::Down => {
                if self.selected <= 6 {
                    self.select_next(true);
                } else {
                    self.selected_control_mut().handle_key(key);
                }
            },
            Key::Up => {
                if self.selected <= 6 {
                    self.select_prev();
                } else {
                    self.selected_control_mut().handle_key(key);
                }
            },
            Key::Char('l') => {
                if self.selected == 7 {
                    let dice = self.ls_saved.selected_item().unwrap();
                    self.load_dice(dice);
                } else if self.selected == 8 {
                    let dice = self.ls_history.selected_item().unwrap();
                    self.load_dice(dice);
                }
            },
            Key::Char('s') => {
                if !self.textboxes().iter().all(|tb| tb.get_isize() == 0) {
                    let dice = self.current_dice();
                    self.ls_saved.add_item(dice);
                }
            },
            Key::Char('c') => self.clear_dice(),
            _ => (self.selected_control_mut().handle_key(key))
        }
    }

    fn load_dice(&mut self, dice: ::dice::Dice) {
        self.tb_d4.set_isize(dice.d4s as isize);
        self.tb_d6.set_isize(dice.d6s as isize);
        self.tb_d8.set_isize(dice.d8s as isize);
        self.tb_d10.set_isize(dice.d10s as isize);
        self.tb_d12.set_isize(dice.d12s as isize);
        self.tb_d20.set_isize(dice.d20s as isize);
        self.tb_mod.set_isize(dice.modifier as isize);
    }

    fn current_dice(&self) -> ::dice::Dice {
        ::dice::Dice {
            d4s: self.tb_d4.get_isize() as usize,
            d6s: self.tb_d6.get_isize() as usize,
            d8s: self.tb_d8.get_isize() as usize,
            d10s: self.tb_d10.get_isize() as usize,
            d12s: self.tb_d12.get_isize() as usize,
            d20s: self.tb_d20.get_isize() as usize,
            modifier: self.tb_mod.get_isize(),
        }
    }

    fn roll_dice(&mut self) {
        if !self.textboxes().iter().all(|tb| tb.get_isize() == 0) {
            let dice = self.current_dice();

            self.ls_history.add_item(dice);

            let dice_roll = ::dice_roll::DiceRoll::new(dice);
            self.bottom_text = format!("{:?}", dice_roll);
        }
    }

    fn clear_dice(&mut self) {
        for tb in self.textboxes_mut().iter_mut() {
            tb.clear_data();
        }
    }

    fn textboxes_mut(&mut self) -> Vec<&mut ::control::TextBox> {
        vec![
            &mut self.tb_d4,
            &mut self.tb_d6,
            &mut self.tb_d8,
            &mut self.tb_d10,
            &mut self.tb_d12,
            &mut self.tb_d20,
            &mut self.tb_mod]
    }

    fn textboxes(&self) -> Vec<&::control::TextBox> {
        vec![
            &self.tb_d4,
            &self.tb_d6,
            &self.tb_d8,
            &self.tb_d10,
            &self.tb_d12,
            &self.tb_d20,
            &self.tb_mod]
    }

}

<commit_msg>implemented 'C' being pressed: clears all data<commit_after>
use rustbox::{ RustBox };
use ::control::Control;
use ::keyboard::Key;

pub struct Controls<'a> {
    rustbox: &'a RustBox,
    tb_d4: ::control::TextBox,
    tb_d6: ::control::TextBox,
    tb_d8: ::control::TextBox,
    tb_d10: ::control::TextBox,
    tb_d12: ::control::TextBox,
    tb_d20: ::control::TextBox,
    tb_mod: ::control::TextBox,
    ls_saved: ::control::ListSelect,
    ls_history: ::control::ListSelect,
    selected: usize,
    bottom_text: String
}

impl<'a> Controls<'a> {

    pub fn initialize(rustbox: &'a RustBox) -> Self {
        let tb_d4 = ::control::TextBox::new(" d4", 3, true);
        let tb_d6 = ::control::TextBox::new(" d6", 3, true);
        let tb_d8 = ::control::TextBox::new(" d8", 3, true);
        let tb_d10 = ::control::TextBox::new("d10", 3, true);
        let tb_d12 = ::control::TextBox::new("d12", 3, true);
        let tb_d20 = ::control::TextBox::new("d20", 3, true);
        let tb_mod = ::control::TextBox::new("mod", 3, false);
        let ls_saved = ::control::ListSelect::new("Saved");
        let ls_history = ::control::ListSelect::new("History");

        let mut result = Controls {
            rustbox: rustbox,
            tb_d4: tb_d4,
            tb_d6: tb_d6,
            tb_d8: tb_d8,
            tb_d10: tb_d10,
            tb_d12: tb_d12,
            tb_d20: tb_d20,
            tb_mod: tb_mod,
            ls_saved: ls_saved,
            ls_history: ls_history,
            selected: 0,
            bottom_text: String::from_str("")
        };

        result.tb_d4.set_selected(true);
        result
    }

    pub fn reposition(&mut self) {
        let mut top_y = self.rustbox.height() / 2 - 7;
        if top_y <= 0 {
            top_y = 2;
        }

        for (i, tb) in self.textboxes_mut().iter_mut().enumerate() {
            tb.set_location(1, top_y + i * 2);
        }

        self.ls_saved.set_location(self.rustbox.width() - 59, 1);
        self.ls_saved.set_size(29, self.rustbox.height() - 3);
        self.ls_history.set_location(self.rustbox.width() - 29, 1);
        self.ls_history.set_size(29, self.rustbox.height() - 3);
    }

    pub fn redraw(&self) {
        for tb in self.textboxes().iter() {
            tb.redraw(self.rustbox);
        }
        self.ls_history.redraw(self.rustbox);
        self.ls_saved.redraw(self.rustbox);
        self.rustbox.print(0,
                           self.rustbox.height() - 1,
                           ::rustbox::RB_NORMAL,
                           ::rustbox::Color::White,
                           ::rustbox::Color::Black,
                           ::std::iter::repeat(' ').take(self.rustbox.width()).collect::<String>().as_slice());
        self.rustbox.print(0,
                           self.rustbox.height() - 1,
                           ::rustbox::RB_NORMAL,
                           ::rustbox::Color::White,
                           ::rustbox::Color::Black,
                           self.bottom_text.as_slice());
        self.rustbox.present();
    }

    pub fn select_next(&mut self, skip_lists: bool) {
        self.selected_control_mut().set_selected(false);
        self.selected += 1;
        if self.selected == 7 && skip_lists {
            self.selected = 0;
        }
        if self.selected == 7 && !self.ls_saved.has_items() {
            self.selected += 1;
        }
        if self.selected == 8 && !self.ls_history.has_items() {
            self.selected += 1;
        }
        if self.selected == 9 { self.selected = 0; }
        self.selected_control_mut().set_selected(true);
    }

    pub fn select_prev(&mut self) {
        self.selected_control_mut().set_selected(false);

        if self.selected == 0 {
            self.selected = 6;
        } else {
            self.selected -= 1;
        }
        if self.selected == 8 && !self.ls_history.has_items() {
            self.selected -= 1;
        }
        if self.selected == 7 && !self.ls_saved.has_items() {
            self.selected -= 1;
        }
        self.selected_control_mut().set_selected(true);
    }

    fn selected_control_mut(&mut self) -> &mut Control {
        match self.selected {
            0 => &mut self.tb_d4,
            1 => &mut self.tb_d6,
            2 => &mut self.tb_d8,
            3 => &mut self.tb_d10,
            4 => &mut self.tb_d12,
            5 => &mut self.tb_d20,
            6 => &mut self.tb_mod,
            7 => &mut self.ls_saved,
            8 => &mut self.ls_history,
            _ => panic!("Could not find selected control")
        }
    }

    pub fn handle_key(&mut self, key: ::keyboard::Key) {
        match key {
            Key::Enter => self.roll_dice(),
            Key::Tab => self.select_next(false),
            Key::Down => {
                if self.selected <= 6 {
                    self.select_next(true);
                } else {
                    self.selected_control_mut().handle_key(key);
                }
            },
            Key::Up => {
                if self.selected <= 6 {
                    self.select_prev();
                } else {
                    self.selected_control_mut().handle_key(key);
                }
            },
            Key::Char('l') => {
                if self.selected == 7 {
                    let dice = self.ls_saved.selected_item().unwrap();
                    self.load_dice(dice);
                } else if self.selected == 8 {
                    let dice = self.ls_history.selected_item().unwrap();
                    self.load_dice(dice);
                }
            },
            Key::Char('s') => {
                if !self.textboxes().iter().all(|tb| tb.get_isize() == 0) {
                    let dice = self.current_dice();
                    self.ls_saved.add_item(dice);
                }
            },
            Key::Char('c') => self.clear_dice(),
            Key::Char('C') => { self.clear_dice(); self.clear_lists(); },
            _ => (self.selected_control_mut().handle_key(key))
        }
    }

    fn load_dice(&mut self, dice: ::dice::Dice) {
        self.tb_d4.set_isize(dice.d4s as isize);
        self.tb_d6.set_isize(dice.d6s as isize);
        self.tb_d8.set_isize(dice.d8s as isize);
        self.tb_d10.set_isize(dice.d10s as isize);
        self.tb_d12.set_isize(dice.d12s as isize);
        self.tb_d20.set_isize(dice.d20s as isize);
        self.tb_mod.set_isize(dice.modifier as isize);
    }

    fn current_dice(&self) -> ::dice::Dice {
        ::dice::Dice {
            d4s: self.tb_d4.get_isize() as usize,
            d6s: self.tb_d6.get_isize() as usize,
            d8s: self.tb_d8.get_isize() as usize,
            d10s: self.tb_d10.get_isize() as usize,
            d12s: self.tb_d12.get_isize() as usize,
            d20s: self.tb_d20.get_isize() as usize,
            modifier: self.tb_mod.get_isize(),
        }
    }

    fn roll_dice(&mut self) {
        if !self.textboxes().iter().all(|tb| tb.get_isize() == 0) {
            let dice = self.current_dice();

            self.ls_history.add_item(dice);

            let dice_roll = ::dice_roll::DiceRoll::new(dice);
            self.bottom_text = format!("{:?}", dice_roll);
        }
    }

    fn clear_dice(&mut self) {
        for tb in self.textboxes_mut().iter_mut() {
            tb.clear_data();
        }
    }

    fn clear_lists(&mut self) {
        self.ls_history.clear_data();
        self.ls_saved.clear_data();
    }

    fn textboxes_mut(&mut self) -> Vec<&mut ::control::TextBox> {
        vec![
            &mut self.tb_d4,
            &mut self.tb_d6,
            &mut self.tb_d8,
            &mut self.tb_d10,
            &mut self.tb_d12,
            &mut self.tb_d20,
            &mut self.tb_mod]
    }

    fn textboxes(&self) -> Vec<&::control::TextBox> {
        vec![
            &self.tb_d4,
            &self.tb_d6,
            &self.tb_d8,
            &self.tb_d10,
            &self.tb_d12,
            &self.tb_d20,
            &self.tb_mod]
    }

}

<|endoftext|>


<commit_before>use std::f32::consts::PI;

use cgmath::{vec3};
use cgmath::prelude::*;

use num_traits::clamp;

// type Point3 = cgmath::Point3<f32>;
// type Vector3 = cgmath::Vector3<f32>;
// type Matrix4 = cgmath::Matrix4<f32>;

use render::Camera;
use render::math::*;

// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
#[derive(PartialEq)]
pub enum CameraMovement {
    FORWARD,
    BACKWARD,
    LEFT,
    RIGHT,
}
use self::CameraMovement::*;

#[derive(Debug)]
pub struct CameraParams {
    pub position: Vector3,
    pub view_matrix: Matrix4,
    pub projection_matrix: Matrix4,
}

// Default camera values
const YAW: f32 = -90.0;
const PITCH: f32 = 0.0;
const SPEED: f32 = 2.5;
const SENSITIVTY: f32 = 0.1;
const ZOOM_SENSITIVITY: f32 = 0.1;
pub const ZOOM: f32 = 45.0;
const MIN_ZOOM: f32 = 1.0;
const MAZ_ZOOM: f32 = 170.0;

pub struct CameraControls {
    // Camera Attributes
    pub position: Point3,

    /// mutually exlusive: if center is set, it is used
    pub front: Vector3,
    pub center: Option<Point3>,

    pub up: Vector3,
    pub right: Vector3,
    pub world_up: Vector3,
    // Euler Angles
    pub yaw: f32,
    pub pitch: f32,
    // Camera options
    pub movement_speed: f32,
    pub mouse_sensitivity: f32,

    pub camera: Camera,

    // pub moving_up: bool,
    pub moving_left: bool,
    // pub moving_down: bool,
    pub moving_right: bool,
    pub moving_forward: bool,
    pub moving_backward: bool,
}

impl Default for CameraControls {
    fn default() -> CameraControls {
        let mut controls = CameraControls {
            position: Point3::new(0.0, 0.0, 0.0),
            front: vec3(0.0, 0.0, -1.0),
            center: None,
            up: Vector3::zero(), // initialized later
            right: Vector3::zero(), // initialized later
            world_up: Vector3::unit_y(),
            yaw: YAW,
            pitch: PITCH,
            movement_speed: SPEED,
            mouse_sensitivity: SENSITIVTY,

            camera: Camera::default(),

            // moving_up: false,
            moving_left: false,
            // moving_down: false,
            moving_right: false,
            moving_forward: false,
            moving_backward: false,
        };
        // TODO!!: overriding default order...? -> NO!
        controls.update_camera_vectors();
        controls
    }
}

impl CameraControls {
    pub fn camera_params(&self) -> CameraParams {
        CameraParams {
            position: self.position.to_vec(),
            view_matrix: self.view_matrix(),
            projection_matrix: self.camera.projection_matrix,
        }
    }

    /// Returns the view matrix calculated using Euler Angles and the LookAt Matrix
    pub fn view_matrix(&self) -> Matrix4 {
        if let Some(center) = self.center {
            Matrix4::look_at(self.position, center, self.up)
        }
        else {
            Matrix4::look_at(self.position, self.position + self.front, self.up)
        }
    }

    pub fn update(&mut self, delta_time: f64) {
        let velocity = self.movement_speed * delta_time as f32;
        if self.moving_forward {
            self.position += self.front * velocity;
        }
        if self.moving_backward {
            self.position += -(self.front * velocity);
        }
        if self.moving_left {
            self.position += -(self.right * velocity);
        }
        if self.moving_right {
            self.position += self.right * velocity;
        }
    }

    pub fn process_keyboard(&mut self, direction: CameraMovement, pressed: bool) {
        match direction {
            FORWARD => self.moving_forward = pressed,
            BACKWARD => self.moving_backward= pressed,
            LEFT => self.moving_left = pressed,
            RIGHT => self.moving_right = pressed,
        }
    }

    /// Processes input received from a mouse input system. Expects the offset value in both the x and y direction.
    pub fn process_mouse_movement(&mut self, mut xoffset: f32, mut yoffset: f32, constrain_pitch: bool) {
        xoffset *= self.mouse_sensitivity;
        yoffset *= self.mouse_sensitivity;

        self.yaw += xoffset;
        self.pitch += yoffset;

        // Make sure that when pitch is out of bounds, screen doesn't get flipped
        if constrain_pitch {
            if self.pitch > 89.0 {
                self.pitch = 89.0;
            }
            if self.pitch < -89.0 {
                self.pitch = -89.0;
            }
        }

        // Update front, Right and Up Vectors using the updated Eular angles
        self.update_camera_vectors();
    }

    /// Calculates the front vector from the Camera's (updated) Eular Angles
    fn update_camera_vectors(&mut self) {
        if let Some(center) = self.center {
            self.front = center - self.position; // TODO!!!: overwritten again immediately...
            self.center = None;
        }
        // Calculate the new front vector
        let front = Vector3 {
            x: self.yaw.to_radians().cos() * self.pitch.to_radians().cos(),
            y: self.pitch.to_radians().sin(),
            z: self.yaw.to_radians().sin() * self.pitch.to_radians().cos(),
        };
        self.front = front.normalize();
        // Also re-calculate the Right and Up vector
        self.right = self.front.cross(self.world_up).normalize(); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
        self.up = self.right.cross(self.front).normalize();
    }

    pub fn set_camera(&mut self, camera: &Camera, transform: &Matrix4) {
        // spec: If no transformation is specified, the location of the camera is at the origin.
        let pos = transform * Vector4::zero();

        // spec: ... the lens looks towards the local -Z axis ...
        let look_at = transform * vec4(0.0, 0.0, -1.0, 0.0);

        self.position = Point3::new(pos.x, pos.y, pos.z);
        self.center = Some(Point3::new(look_at.x, look_at.y, look_at.z));

        // TODO!!: handle better (camera zoom/fovy)
        let mut camera = camera.clone();
        camera.fovy = self.camera.fovy;
        self.camera = camera;

        self.update_camera_vectors();
    }
}

#[derive(Clone)]
pub enum NavState {
    None,
    Rotating,
    Panning,
}

/// Inspirted by ThreeJS OrbitControls
pub struct OrbitControls {
    pub camera: Camera,

    pub position: Point3,

    // "target" sets the location of focus, where the object orbits around
	pub target: Point3,

    pub state: NavState,

	// current position in spherical coordinates
	spherical: Spherical,
	spherical_delta: Spherical,

    scale: f32,
    pan_offset: Vector3,

    rotate_start: Option<Vector2>,
    rotate_end: Vector2,

	pan_start: Option<Vector2>,
	pan_end: Vector2,

    pub screen_width: f32,
    pub screen_height: f32,

    // quat: Quaternion,

    // TODO!!: unused?
    last_position: Vector3,
    last_quaternion: Quaternion,
}

impl OrbitControls {
    pub fn new(position: Point3, screen_width: f32, screen_height: f32) -> Self {
        OrbitControls {
            camera: Camera::default(),

            position,
            target: Point3::new(0.0, 0.0, 0.0),

            state: NavState::None,

            // current position in spherical coordinates
            spherical: Spherical::default(),
            spherical_delta: Spherical::default(),

            scale: 1.0,
            pan_offset: Vector3::zero(),

            rotate_start: None,
            rotate_end: Vector2::zero(),

            pan_start: None,
            pan_end: Vector2::zero(),

            screen_width,
            screen_height,

            //

            // NOTE: original uses sth like Quaternion::from_arc from "up" to "y up"
            // and stores inverse quaternion
            // quat: Quaternion::one(),

            last_position: Vector3::zero(),
            last_quaternion: Quaternion::zero(),
        }
    }

    // TODO!!: cache/return reference? often many of them stay the same...
    pub fn camera_params(&self) -> CameraParams {
        CameraParams {
            position: self.position.to_vec(),
            view_matrix: self.view_matrix(),
            projection_matrix: self.camera.projection_matrix,
        }
    }

    fn view_matrix(&self) -> Matrix4 {
        Matrix4::look_at(self.position, self.target, vec3(0.0, 1.0, 0.0))
    }

    pub fn handle_mouse_move(&mut self, x: f32, y: f32) {
        match self.state {
            NavState::Rotating => self.handle_mouse_move_rotate(x, y),
            NavState::Panning => self.handle_mouse_move_pan(x, y),
            NavState::None => ()
        }
    }

    fn handle_mouse_move_rotate(&mut self, x: f32, y: f32) {
        self.rotate_end.x = x;
        self.rotate_end.y = y;
        let rotate_delta = if let Some(rotate_start) = self.rotate_start {
            self.rotate_end - rotate_start
        } else {
            Vector2::zero()
        };

        // rotating across whole screen goes 360 degrees around
        let rotate_speed = 1.0; // TODO: const/param/remove?
        let angle = 2.0 * PI * rotate_delta.x / self.screen_width * rotate_speed;
        self.rotate_left(angle);

        // rotating up and down along whole screen attempts to go 360, but limited to 180
        let angle = 2.0 * PI * rotate_delta.y / self.screen_height * rotate_speed;
		self.rotate_up(angle);

        self.rotate_start = Some(self.rotate_end);

		self.update();
    }

    pub fn handle_mouse_up(&mut self) {
        self.rotate_start = None;
        self.pan_start = None;
    }

    fn rotate_left(&mut self, angle: f32) {
        self.spherical_delta.theta -= angle;
    }

    fn rotate_up(&mut self, angle: f32) {
        self.spherical_delta.phi -= angle;
    }

    fn handle_mouse_move_pan(&mut self, x: f32, y: f32) {
        self.pan_end.x = x;
        self.pan_end.y = y;

        let pan_delta = if let Some(pan_start) = self.pan_start {
            self.pan_end - pan_start
        } else {
            Vector2::zero()
        };

        self.pan(&pan_delta);

        self.pan_start = Some(self.pan_end);

        self.update();
    }

    fn pan(&mut self, delta: &Vector2) {
        if self.camera.is_perspective() {
            let offset = self.position - self.target;
            let mut target_distance = offset.magnitude();

            // half of the fov is center to top of screen
            target_distance *= (self.camera.fovy / 2.0).tan() * PI / 180.0;

            // we actually don't use screen_width, since perspective camera is fixed to screen height
            let distance = 2.0 * delta.x * target_distance / self.screen_height;
            self.pan_left(distance);
            let distance = 2.0 * delta.y * target_distance / self.screen_height;
            self.pan_up(distance);
        } else {
            unimplemented!("orthographic camera zoom")
        }
    }

    pub fn pan_left(&mut self, distance: f32) {
        self.pan_offset.x -= distance
    }

    pub fn pan_up(&mut self, distance: f32) {
        self.pan_offset.y -= distance
    }

    // Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
    pub fn process_mouse_scroll(&mut self, mut yoffset: f32) {
        yoffset *= ZOOM_SENSITIVITY;
        if self.camera.fovy >= MIN_ZOOM && self.camera.fovy <= MAZ_ZOOM {
            self.camera.fovy -= yoffset;
        }
        if self.camera.fovy <= MIN_ZOOM {
            self.camera.fovy = MIN_ZOOM;
        }
        if self.camera.fovy >= MAZ_ZOOM {
            self.camera.fovy = MAZ_ZOOM;
        }
        self.camera.update_projection_matrix();
    }

    fn update(&mut self) {
        let mut offset = self.position - self.target;

        // rotate offset to "y-axis-is-up" space
        // self.offset = self.quat.rotate_vector(self.offset);

        // angle from z-axis around y-axis
        self.spherical = Spherical::from_vec3(offset);

        self.spherical.theta += self.spherical_delta.theta;
        self.spherical.phi += self.spherical_delta.phi;

        // TODO!: left out theta restrictions / make_safe for now

        // restrict phi to be between desired limits
        let epsilon = 0.0001;
        self.spherical.phi = clamp(self.spherical.phi, epsilon, PI - epsilon);

        self.spherical.radius *= self.scale;

        // TODO?: restrict radius to be between desired limits?

        // move target to panned location
        self.target += self.pan_offset;

        offset = self.spherical.to_vec3();

        // NOTE: skipped from original: rotate offset back to "camera-up-vector-is-up" space

        self.position = self.target + offset;

        // TODO!!: how to do this?
        // scope.object.lookAt( scope.target );

        // TODO!: if enable_damping...?
        self.spherical_delta = Spherical::from_vec3(Vector3::zero());

        self.scale = 1.0;
        self.pan_offset = Vector3::zero();

        // TODO!: zoomChanged stuff

        // TODO!!!
    }
}
<commit_msg>fix panning (mostly), clean up<commit_after>use std::f32::consts::PI;

use cgmath::{vec3};
use cgmath::prelude::*;

use num_traits::clamp;

// type Point3 = cgmath::Point3<f32>;
// type Vector3 = cgmath::Vector3<f32>;
// type Matrix4 = cgmath::Matrix4<f32>;

use render::Camera;
use render::math::*;

// Defines several possible options for camera movement. Used as abstraction to stay away from window-system specific input methods
#[derive(PartialEq)]
pub enum CameraMovement {
    FORWARD,
    BACKWARD,
    LEFT,
    RIGHT,
}
use self::CameraMovement::*;

#[derive(Debug)]
pub struct CameraParams {
    pub position: Vector3,
    pub view_matrix: Matrix4,
    pub projection_matrix: Matrix4,
}

// Default camera values
const YAW: f32 = -90.0;
const PITCH: f32 = 0.0;
const SPEED: f32 = 2.5;
const SENSITIVTY: f32 = 0.1;
const ZOOM_SENSITIVITY: f32 = 0.1;
pub const ZOOM: f32 = 45.0;
const MIN_ZOOM: f32 = 1.0;
const MAZ_ZOOM: f32 = 170.0;

/// NOTE: superceded by OrbitControls. Keeping the parts of it not (yet?) added there.
pub struct CameraControls {
    // Camera Attributes
    pub position: Point3,

    /// mutually exlusive: if center is set, it is used
    pub front: Vector3,
    pub center: Option<Point3>,

    pub up: Vector3,
    pub right: Vector3,
    pub world_up: Vector3,
    // Euler Angles
    pub yaw: f32,
    pub pitch: f32,
    // Camera options
    pub movement_speed: f32,
    pub mouse_sensitivity: f32,

    pub camera: Camera,

    // pub moving_up: bool,
    pub moving_left: bool,
    // pub moving_down: bool,
    pub moving_right: bool,
    pub moving_forward: bool,
    pub moving_backward: bool,
}

impl Default for CameraControls {
    fn default() -> CameraControls {
        let controls = CameraControls {
            position: Point3::new(0.0, 0.0, 0.0),
            front: vec3(0.0, 0.0, -1.0),
            center: None,
            up: Vector3::zero(), // initialized later
            right: Vector3::zero(), // initialized later
            world_up: Vector3::unit_y(),
            yaw: YAW,
            pitch: PITCH,
            movement_speed: SPEED,
            mouse_sensitivity: SENSITIVTY,

            camera: Camera::default(),

            // moving_up: false,
            moving_left: false,
            // moving_down: false,
            moving_right: false,
            moving_forward: false,
            moving_backward: false,
        };
        // TODO!!: overriding default order...? -> NO!
        // controls.update_camera_vectors();
        controls
    }
}

impl CameraControls {
    pub fn camera_params(&self) -> CameraParams {
        CameraParams {
            position: self.position.to_vec(),
            view_matrix: self.view_matrix(),
            projection_matrix: self.camera.projection_matrix,
        }
    }

    /// Returns the view matrix calculated using Euler Angles and the LookAt Matrix
    pub fn view_matrix(&self) -> Matrix4 {
        if let Some(center) = self.center {
            Matrix4::look_at(self.position, center, self.up)
        }
        else {
            Matrix4::look_at(self.position, self.position + self.front, self.up)
        }
    }

    pub fn update(&mut self, delta_time: f64) {
        let velocity = self.movement_speed * delta_time as f32;
        if self.moving_forward {
            self.position += self.front * velocity;
        }
        if self.moving_backward {
            self.position += -(self.front * velocity);
        }
        if self.moving_left {
            self.position += -(self.right * velocity);
        }
        if self.moving_right {
            self.position += self.right * velocity;
        }
    }

    pub fn process_keyboard(&mut self, direction: CameraMovement, pressed: bool) {
        match direction {
            FORWARD => self.moving_forward = pressed,
            BACKWARD => self.moving_backward= pressed,
            LEFT => self.moving_left = pressed,
            RIGHT => self.moving_right = pressed,
        }
    }

    pub fn set_camera(&mut self, camera: &Camera, transform: &Matrix4) {
        // spec: If no transformation is specified, the location of the camera is at the origin.
        let pos = transform * Vector4::zero();

        // spec: ... the lens looks towards the local -Z axis ...
        let look_at = transform * vec4(0.0, 0.0, -1.0, 0.0);

        self.position = Point3::new(pos.x, pos.y, pos.z);
        self.center = Some(Point3::new(look_at.x, look_at.y, look_at.z));

        // TODO!!: handle better (camera zoom/fovy)
        let mut camera = camera.clone();
        camera.fovy = self.camera.fovy;
        self.camera = camera;

        // self.update_camera_vectors();
    }
}

#[derive(Clone)]
pub enum NavState {
    None,
    Rotating,
    Panning,
}

/// Inspirted by ThreeJS OrbitControls
pub struct OrbitControls {
    pub camera: Camera,

    pub position: Point3,

    // "target" sets the location of focus, where the object orbits around
	pub target: Point3,

    pub state: NavState,

	// current position in spherical coordinates
	spherical: Spherical,
	spherical_delta: Spherical,

    scale: f32,
    pan_offset: Vector3,

    rotate_start: Option<Vector2>,
    rotate_end: Vector2,

	pan_start: Option<Vector2>,
	pan_end: Vector2,

    pub screen_width: f32,
    pub screen_height: f32,
}

impl OrbitControls {
    pub fn new(position: Point3, screen_width: f32, screen_height: f32) -> Self {
        OrbitControls {
            camera: Camera::default(),

            position,
            target: Point3::new(0.0, 0.0, 0.0),

            state: NavState::None,

            // current position in spherical coordinates
            spherical: Spherical::default(),
            spherical_delta: Spherical::default(),

            scale: 1.0, // TODO!: not really used
            pan_offset: Vector3::zero(),

            rotate_start: None,
            rotate_end: Vector2::zero(),

            pan_start: None,
            pan_end: Vector2::zero(),

            screen_width,
            screen_height,
        }
    }

    // TODO!!: cache/return reference? often many of them stay the same...
    pub fn camera_params(&self) -> CameraParams {
        CameraParams {
            position: self.position.to_vec(),
            view_matrix: self.view_matrix(),
            projection_matrix: self.camera.projection_matrix,
        }
    }

    fn view_matrix(&self) -> Matrix4 {
        Matrix4::look_at(self.position, self.target, vec3(0.0, 1.0, 0.0))
    }

    pub fn handle_mouse_move(&mut self, x: f32, y: f32) {
        match self.state {
            NavState::Rotating => self.handle_mouse_move_rotate(x, y),
            NavState::Panning => self.handle_mouse_move_pan(x, y),
            NavState::None => ()
        }
    }

    fn handle_mouse_move_rotate(&mut self, x: f32, y: f32) {
        self.rotate_end.x = x;
        self.rotate_end.y = y;
        let rotate_delta = if let Some(rotate_start) = self.rotate_start {
            self.rotate_end - rotate_start
        } else {
            Vector2::zero()
        };

        // rotating across whole screen goes 360 degrees around
        let rotate_speed = 1.0; // TODO: const/param/remove?
        let angle = 2.0 * PI * rotate_delta.x / self.screen_width * rotate_speed;
        self.rotate_left(angle);

        // rotating up and down along whole screen attempts to go 360, but limited to 180
        let angle = 2.0 * PI * rotate_delta.y / self.screen_height * rotate_speed;
		self.rotate_up(angle);

        self.rotate_start = Some(self.rotate_end);

		self.update();
    }

    pub fn handle_mouse_up(&mut self) {
        self.rotate_start = None;
        self.pan_start = None;
    }

    fn rotate_left(&mut self, angle: f32) {
        self.spherical_delta.theta -= angle;
    }

    fn rotate_up(&mut self, angle: f32) {
        self.spherical_delta.phi -= angle;
    }

    fn handle_mouse_move_pan(&mut self, x: f32, y: f32) {
        self.pan_end.x = x;
        self.pan_end.y = y;

        let pan_delta = if let Some(pan_start) = self.pan_start {
            self.pan_end - pan_start
        } else {
            Vector2::zero()
        };

        self.pan(&pan_delta);

        self.pan_start = Some(self.pan_end);

        self.update();
    }

    fn pan(&mut self, delta: &Vector2) {
        if self.camera.is_perspective() {
            let offset = self.position - self.target;
            let mut target_distance = offset.magnitude();

            // half of the fov is center to top of screen
            target_distance *= (self.camera.fovy / 2.0).tan() * PI / 180.0;

            // we actually don't use screen_width, since perspective camera is fixed to screen height
            let distance = 2.0 * delta.x * target_distance / self.screen_height;
            self.pan_left(distance);
            let distance = 2.0 * delta.y * target_distance / self.screen_height;
            self.pan_up(distance);
        } else {
            // TODO!: orthographic camera pan
            unimplemented!("orthographic camera pan")
        }
    }

    pub fn pan_left(&mut self, distance: f32) {
        self.pan_offset.x -= distance
    }

    pub fn pan_up(&mut self, distance: f32) {
        self.pan_offset.y -= distance
    }

    // Processes input received from a mouse scroll-wheel event. Only requires input on the vertical wheel-axis
    pub fn process_mouse_scroll(&mut self, mut yoffset: f32) {
        yoffset *= ZOOM_SENSITIVITY;
        if self.camera.fovy >= MIN_ZOOM && self.camera.fovy <= MAZ_ZOOM {
            self.camera.fovy -= yoffset;
        }
        if self.camera.fovy <= MIN_ZOOM {
            self.camera.fovy = MIN_ZOOM;
        }
        if self.camera.fovy >= MAZ_ZOOM {
            self.camera.fovy = MAZ_ZOOM;
        }
        self.camera.update_projection_matrix();
    }

    fn update(&mut self) {
        let mut offset = self.position - self.target;

        // NOTE: skipping rotate offset to "y-axis-is-up" space

        // angle from z-axis around y-axis
        self.spherical = Spherical::from_vec3(offset);

        self.spherical.theta += self.spherical_delta.theta;
        self.spherical.phi += self.spherical_delta.phi;

        // NOTE!: left out theta restrictions / make_safe for now

        // restrict phi to be between desired limits
        let epsilon = 0.0001;
        self.spherical.phi = clamp(self.spherical.phi, epsilon, PI - epsilon);

        self.spherical.radius *= self.scale;

        // TODO?: restrict radius to be between desired limits?

        // move target to panned location
        // NOTE: quite different from original
        // NOTE: skipped from original: rotate offset back to "camera-up-vector-is-up" space
        let pan_speed = 2.0; // TODO!!: test on non-retina display
        self.pan_offset *= pan_speed;
        let right = offset.cross(Vector3::unit_y()).normalize();
        let up = right.cross(offset).normalize();
        self.position += right * self.pan_offset.x;
        self.position += up * self.pan_offset.y;
        self.target += right * self.pan_offset.x;
        self.target += up * self.pan_offset.y;

        // apply rotation
        offset = self.spherical.to_vec3();
        self.position = self.target + offset;

        // TODO?: if enable_damping...?
        self.spherical_delta = Spherical::from_vec3(Vector3::zero());

        self.scale = 1.0;
        self.pan_offset = Vector3::zero();

        // NOTE: skip zoomChanged stuff
    }
}
<|endoftext|>


<commit_before>use dces::prelude::{Entity, World};
use std::{cell::RefCell, collections::HashMap, sync::mpsc};

use crate::{
    prelude::*,
    properties::Constraint,
    render, shell,
    shell::{ShellRequest, WindowRequest, WindowSettings},
    tree::Tree,
    utils::{Point, Rectangle},
};

/// Represents a window. Each window has its own tree, event pipeline and shell.
pub struct WindowAdapter {
    world: World<Tree, StringComponentStore, render::RenderContext2D>,
    ctx: ContextProvider,
}

impl WindowAdapter {
    /// Creates a new WindowAdapter.
    pub fn new(
        world: World<Tree, StringComponentStore, render::RenderContext2D>,
        ctx: ContextProvider,
    ) -> Self {
        WindowAdapter { world, ctx }
    }
}

impl WindowAdapter {
    fn root(&mut self) -> Entity {
        self.world
            .entity_component_manager()
            .entity_store()
            .root
            .unwrap()
    }
}

impl shell::WindowAdapter for WindowAdapter {
    fn resize(&mut self, width: f64, height: f64) {
        let root = self.root();
        self.ctx
            .event_queue
            .borrow_mut()
            .register_event_with_strategy(
                WindowEvent::Resize { width, height },
                EventStrategy::Direct,
                root,
            );
    }

    fn mouse(&mut self, x: f64, y: f64) {
        let root = self.root();
        self.ctx.mouse_position.set(Point::new(x, y));
        self.ctx
            .event_queue
            .borrow_mut()
            .register_event(MouseMoveEvent { x, y }, root)
    }

    fn scroll(&mut self, delta_x: f64, delta_y: f64) {
        let root = self.root();
        self.ctx.event_queue.borrow_mut().register_event(
            ScrollEvent {
                delta: Point::new(delta_x, delta_y),
            },
            root,
        )
    }

    fn mouse_event(&mut self, event: shell::MouseEvent) {
        let root = self.root();
        match event.state {
            shell::ButtonState::Up => {
                self.ctx.event_queue.borrow_mut().register_event(
                    MouseUpEvent {
                        x: event.x,
                        y: event.y,
                        button: event.button,
                    },
                    root,
                );
                self.ctx.event_queue.borrow_mut().register_event(
                    GlobalMouseUpEvent {
                        x: event.x,
                        y: event.y,
                        button: event.button,
                    },
                    root,
                );
            }
            shell::ButtonState::Down => self.ctx.event_queue.borrow_mut().register_event(
                MouseDownEvent {
                    x: event.x,
                    y: event.y,
                    button: event.button,
                },
                root,
            ),
        }
    }

    fn mouse_position(&self) -> Point {
        self.ctx.mouse_position.get()
    }

    fn key_event(&mut self, event: shell::KeyEvent) {
        let root = self.root();
        match event.state {
            shell::ButtonState::Up => self
                .ctx
                .event_queue
                .borrow_mut()
                .register_event(KeyUpEvent { event }, root),
            shell::ButtonState::Down => self
                .ctx
                .event_queue
                .borrow_mut()
                .register_event(KeyDownEvent { event }, root),
        }
    }

    fn quit_event(&mut self) {
        let root = self.root();

        self.ctx
            .event_queue
            .borrow_mut()
            .register_event(SystemEvent::Quit, root);
    }

    fn active(&mut self, active: bool) {
        let root = self.root();

        self.ctx
            .event_queue
            .borrow_mut()
            .register_event_with_strategy(
                WindowEvent::ActiveChanged(active),
                EventStrategy::Direct,
                root,
            );
    }

    fn run(&mut self, render_context: &mut render::RenderContext2D) {
        self.world.run_with_context(render_context);
    }
}

/// Creates a `WindowAdapter` and a `WindowSettings` object from a window builder closure.
pub fn create_window<F: Fn(&mut BuildContext) -> Entity + 'static>(
    app_name: impl Into<String>,
    request_sender: mpsc::Sender<ShellRequest<WindowAdapter>>,
    create_fn: F,
) -> (WindowAdapter, WindowSettings, mpsc::Receiver<WindowRequest>) {
    let app_name = app_name.into();
    let mut world: World<Tree, StringComponentStore, render::RenderContext2D> =
        World::from_stores(Tree::default(), StringComponentStore::default());

    let (sender, receiver) = mpsc::channel();

    let registry = Rc::new(RefCell::new(Registry::new()));

    if app_name.is_empty() {
        registry
            .borrow_mut()
            .register("settings", Settings::default());
    } else {
        registry
            .borrow_mut()
            .register("settings", Settings::new(app_name.clone()));
    };

    let context_provider = ContextProvider::new(sender, request_sender.clone(), app_name);

    let theme = crate::theme::default_theme();

    let window = {
        let overlay = Overlay::new().build(&mut BuildContext::new(
            world.entity_component_manager(),
            &context_provider.render_objects,
            &context_provider.layouts,
            &context_provider.handler_map,
            &mut *context_provider.states.borrow_mut(),
            &theme,
        ));

        {
            let tree: &mut Tree = world.entity_component_manager().entity_store_mut();
            tree.set_overlay(overlay);
        }

        let window = create_fn(&mut BuildContext::new(
            world.entity_component_manager(),
            &context_provider.render_objects,
            &context_provider.layouts,
            &context_provider.handler_map,
            &mut *context_provider.states.borrow_mut(),
            &theme,
        ));

        {
            let tree: &mut Tree = world.entity_component_manager().entity_store_mut();
            tree.set_root(window);
        }

        window
    };

    let constraint = *world
        .entity_component_manager()
        .component_store()
        .get::<Constraint>("constraint", window)
        .unwrap();

    let position = *world
        .entity_component_manager()
        .component_store()
        .get::<Point>("position", window)
        .unwrap();

    let mut fonts = HashMap::new();
    fonts.insert(
        "Roboto Regular".to_string(),
        crate::theme::fonts::ROBOTO_REGULAR_FONT,
    );
    fonts.insert(
        "Roboto Medium".to_string(),
        crate::theme::fonts::ROBOTO_MEDIUM_FONT,
    );
    fonts.insert(
        "Material Icons".to_string(),
        crate::theme::fonts::MATERIAL_ICONS_REGULAR_FONT,
    );

    let settings = WindowSettings {
        title: world
            .entity_component_manager()
            .component_store()
            .get::<String>("title", window)
            .unwrap()
            .clone(),
        borderless: *world
            .entity_component_manager()
            .component_store()
            .get::<bool>("borderless", window)
            .unwrap(),
        resizeable: *world
            .entity_component_manager()
            .component_store()
            .get::<bool>("resizeable", window)
            .unwrap(),
        always_on_top: *world
            .entity_component_manager()
            .component_store()
            .get::<bool>("always_on_top", window)
            .unwrap(),
        position: (position.x, position.y),
        size: (constraint.width(), constraint.height()),
        fonts,
    };

    world
        .entity_component_manager()
        .component_store_mut()
        .register("global", window, Global::default());
    world
        .entity_component_manager()
        .component_store_mut()
        .register("global", window, Global::default());
    world
        .entity_component_manager()
        .component_store_mut()
        .register(
            "bounds",
            window,
            Rectangle::from((0.0, 0.0, constraint.width(), constraint.height())),
        );

    world.register_init_system(InitSystem::new(context_provider.clone(), registry.clone()));

    world.register_cleanup_system(CleanupSystem::new(
        context_provider.clone(),
        registry.clone(),
    ));

    world
        .create_system(EventStateSystem::new(
            context_provider.clone(),
            registry.clone(),
        ))
        .with_priority(0)
        .build();

    world
        .create_system(LayoutSystem::new(context_provider.clone()))
        .with_priority(1)
        .build();

    world
        .create_system(PostLayoutStateSystem::new(
            context_provider.clone(),
            registry.clone(),
        ))
        .with_priority(2)
        .build();

    world
        .create_system(RenderSystem::new(context_provider.clone()))
        .with_priority(3)
        .build();

    (
        WindowAdapter::new(world, context_provider),
        settings,
        receiver,
    )
}
<commit_msg>api: application: windows_adapter update<commit_after>use dces::prelude::{Entity, World};
use std::{cell::RefCell, collections::HashMap, sync::mpsc};

use crate::{
    prelude::*,
    properties::Constraint,
    render, shell,
    shell::{ShellRequest, WindowRequest, WindowSettings},
    tree::Tree,
    utils::{Point, Rectangle},
};

/// Represents a window. Each window has its own tree, event pipeline and shell.
pub struct WindowAdapter {
    world: World<Tree, StringComponentStore, render::RenderContext2D>,
    ctx: ContextProvider,
}

impl WindowAdapter {
    /// Creates a new WindowAdapter.
    pub fn new(
        world: World<Tree, StringComponentStore, render::RenderContext2D>,
        ctx: ContextProvider,
    ) -> Self {
        WindowAdapter { world, ctx }
    }
}

impl WindowAdapter {
    fn root(&mut self) -> Entity {
        self.world
            .entity_component_manager()
            .entity_store()
            .root
            .unwrap()
    }
}

impl shell::WindowAdapter for WindowAdapter {
    fn resize(&mut self, width: f64, height: f64) {
        let root = self.root();
        self.ctx
            .event_queue
            .borrow_mut()
            .register_event_with_strategy(
                WindowEvent::Resize { width, height },
                EventStrategy::Direct,
                root,
            );
    }

    fn mouse(&mut self, x: f64, y: f64) {
        let root = self.root();
        self.ctx.mouse_position.set(Point::new(x, y));
        self.ctx
            .event_queue
            .borrow_mut()
            .register_event(MouseMoveEvent { x, y }, root)
    }

    fn scroll(&mut self, delta_x: f64, delta_y: f64) {
        let root = self.root();
        self.ctx.event_queue.borrow_mut().register_event(
            ScrollEvent {
                delta: Point::new(delta_x, delta_y),
            },
            root,
        )
    }

    fn mouse_event(&mut self, event: shell::MouseEvent) {
        let root = self.root();
        match event.state {
            shell::ButtonState::Up => {
                self.ctx.event_queue.borrow_mut().register_event(
                    MouseUpEvent {
                        x: event.x,
                        y: event.y,
                        button: event.button,
                    },
                    root,
                );
                self.ctx.event_queue.borrow_mut().register_event(
                    GlobalMouseUpEvent {
                        x: event.x,
                        y: event.y,
                        button: event.button,
                    },
                    root,
                );
            }
            shell::ButtonState::Down => self.ctx.event_queue.borrow_mut().register_event(
                MouseDownEvent {
                    x: event.x,
                    y: event.y,
                    button: event.button,
                },
                root,
            ),
        }
    }

    fn mouse_position(&self) -> Point {
        self.ctx.mouse_position.get()
    }

    fn key_event(&mut self, event: shell::KeyEvent) {
        let root = self.root();
        match event.state {
            shell::ButtonState::Up => self
                .ctx
                .event_queue
                .borrow_mut()
                .register_event(KeyUpEvent { event }, root),
            shell::ButtonState::Down => self
                .ctx
                .event_queue
                .borrow_mut()
                .register_event(KeyDownEvent { event }, root),
        }
    }

    fn quit_event(&mut self) {
        let root = self.root();

        self.ctx
            .event_queue
            .borrow_mut()
            .register_event(SystemEvent::Quit, root);
    }

    fn active(&mut self, active: bool) {
        let root = self.root();

        self.ctx
            .event_queue
            .borrow_mut()
            .register_event_with_strategy(
                WindowEvent::ActiveChanged(active),
                EventStrategy::Direct,
                root,
            );
    }

    fn run(&mut self, render_context: &mut render::RenderContext2D) {
        self.world.run_with_context(render_context);
    }
}

/// Creates a `WindowAdapter` and a `WindowSettings` object from a window builder closure.
pub fn create_window<F: Fn(&mut BuildContext) -> Entity + 'static>(
    app_name: impl Into<String>,
    request_sender: mpsc::Sender<ShellRequest<WindowAdapter>>,
    create_fn: F,
) -> (WindowAdapter, WindowSettings, mpsc::Receiver<WindowRequest>) {
    let app_name = app_name.into();
    let mut world: World<Tree, StringComponentStore, render::RenderContext2D> =
        World::from_stores(Tree::default(), StringComponentStore::default());

    let (sender, receiver) = mpsc::channel();

    let registry = Rc::new(RefCell::new(Registry::new()));

    if app_name.is_empty() {
        registry
            .borrow_mut()
            .register("settings", Settings::default());
    } else {
        registry
            .borrow_mut()
            .register("settings", Settings::new(app_name.clone()));
    };

    let context_provider = ContextProvider::new(sender, request_sender.clone(), app_name);

    let theme = crate::theme::default_theme();

    let window = {
        let overlay = Overlay::new().build(&mut BuildContext::new(
            world.entity_component_manager(),
            &context_provider.render_objects,
            &context_provider.layouts,
            &context_provider.handler_map,
            &mut *context_provider.states.borrow_mut(),
            &theme,
        ));

        {
            let tree: &mut Tree = world.entity_component_manager().entity_store_mut();
            tree.set_overlay(overlay);
        }

        let window = create_fn(&mut BuildContext::new(
            world.entity_component_manager(),
            &context_provider.render_objects,
            &context_provider.layouts,
            &context_provider.handler_map,
            &mut *context_provider.states.borrow_mut(),
            &theme,
        ));

        {
            let tree: &mut Tree = world.entity_component_manager().entity_store_mut();
            tree.set_root(window);
        }

        window
    };

    let constraint = *world
        .entity_component_manager()
        .component_store()
        .get::<Constraint>("constraint", window)
        .unwrap();

    let position = *world
        .entity_component_manager()
        .component_store()
        .get::<Point>("position", window)
        .unwrap();

    let mut fonts = HashMap::new();
    fonts.insert(
        "Roboto Regular".to_string(),
        crate::theme::fonts::ROBOTO_REGULAR_FONT,
    );
    fonts.insert(
        "Roboto Medium".to_string(),
        crate::theme::fonts::ROBOTO_MEDIUM_FONT,
    );
    fonts.insert(
        "Material Icons".to_string(),
        crate::theme::fonts::MATERIAL_ICONS_FONT_TTF,
    );

    let settings = WindowSettings {
        title: world
            .entity_component_manager()
            .component_store()
            .get::<String>("title", window)
            .unwrap()
            .clone(),
        borderless: *world
            .entity_component_manager()
            .component_store()
            .get::<bool>("borderless", window)
            .unwrap(),
        resizeable: *world
            .entity_component_manager()
            .component_store()
            .get::<bool>("resizeable", window)
            .unwrap(),
        always_on_top: *world
            .entity_component_manager()
            .component_store()
            .get::<bool>("always_on_top", window)
            .unwrap(),
        position: (position.x, position.y),
        size: (constraint.width(), constraint.height()),
        fonts,
    };

    world
        .entity_component_manager()
        .component_store_mut()
        .register("global", window, Global::default());
    world
        .entity_component_manager()
        .component_store_mut()
        .register("global", window, Global::default());
    world
        .entity_component_manager()
        .component_store_mut()
        .register(
            "bounds",
            window,
            Rectangle::from((0.0, 0.0, constraint.width(), constraint.height())),
        );

    world.register_init_system(InitSystem::new(context_provider.clone(), registry.clone()));

    world.register_cleanup_system(CleanupSystem::new(
        context_provider.clone(),
        registry.clone(),
    ));

    world
        .create_system(EventStateSystem::new(
            context_provider.clone(),
            registry.clone(),
        ))
        .with_priority(0)
        .build();

    world
        .create_system(LayoutSystem::new(context_provider.clone()))
        .with_priority(1)
        .build();

    world
        .create_system(PostLayoutStateSystem::new(
            context_provider.clone(),
            registry.clone(),
        ))
        .with_priority(2)
        .build();

    world
        .create_system(RenderSystem::new(context_provider.clone()))
        .with_priority(3)
        .build();

    (
        WindowAdapter::new(world, context_provider),
        settings,
        receiver,
    )
}
<|endoftext|>


<commit_before>use ra_syntax::{
    ast::{self, AstNode},
    TextRange, TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     <|>dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;

    if !is_valid_macrocall(&macro_call, "dbg")? {
        return None;
    }

    let semicolon_on_end = macro_call.semicolon_token().is_some();
    let is_leaf = macro_call.syntax().next_sibling().is_none();

    let macro_end = match semicolon_on_end {
        true => macro_call.syntax().text_range().end() - TextSize::of(';'),
        false => macro_call.syntax().text_range().end(),
    };

    // macro_range determines what will be deleted and replaced with macro_content
    let macro_range = TextRange::new(macro_call.syntax().text_range().start(), macro_end);
    let paste_instead_of_dbg = {
        let text = macro_call.token_tree()?.syntax().text();

        // leafines determines if we should include the parenthesis or not
        let slice_index: TextRange = match is_leaf {
            // leaf means - we can extract the contents of the dbg! in text
            true => TextRange::new(TextSize::of('('), text.len() - TextSize::of(')')),
            // not leaf - means we should keep the parens
            false => TextRange::new(TextSize::from(0 as u32), text.len()),
        };
        text.slice(slice_index).to_string()
    };

    let target = macro_call.syntax().text_range();
    acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", target, |builder| {
        builder.replace(macro_range, paste_instead_of_dbg);
    })
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens
fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<bool> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macro call, dbg followed by !
    let excl = path.syntax().next_sibling_or_token()?;

    if name_ref.text() != macro_name || excl.kind() != T![!] {
        return None;
    }

    let node = macro_call.token_tree()?.syntax().clone();
    let first_child = node.first_child_or_token()?;
    let last_child = node.last_child_or_token()?;

    match (first_child.kind(), last_child.kind()) {
        (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => Some(true),
        _ => Some(false),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");

        check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1");

        check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.checked_sub(4) {
        // ...
    }
}
",
        );
    }

    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1");
        check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
    }

    #[test]
    fn test_remove_dbg_target() {
        check_assist_target(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "dbg!(n.checked_sub(4))",
        );
    }

    #[test]
    fn test_remove_dbg_keep_semicolon() {
        // https://github.com/rust-analyzer/rust-analyzer/issues/5129#issuecomment-651399779
        // not quite though
        // adding a comment at the end of the line makes
        // the ast::MacroCall to include the semicolon at the end
        let code = "
let res = <|>dbg!(1 * 20); // needless comment
";
        let expected = "
let res = 1 * 20; // needless comment
";
        check_assist(remove_dbg, code, expected);
    }

    #[test]
    fn test_remove_dbg_keep_expression() {
        let code = "
let res = <|>dbg!(a + b).foo();";
        let expected = "let res = (a + b).foo();";
        check_assist(remove_dbg, code, expected);
    }

    #[test]
    fn test_remove_dbg_from_inside_fn() {
        let code = "
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!(\"{}\", x);
}";

        let expected = "
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(5 + 10);
    println!(\"{}\", x);
}";
        check_assist_target(remove_dbg, code, "dbg!(5 + 10)");
        check_assist(remove_dbg, code, expected);
    }
}
<commit_msg>Addressed code review<commit_after>use ra_syntax::{
    ast::{self, AstNode},
    TextRange, TextSize, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: remove_dbg
//
// Removes `dbg!()` macro call.
//
// ```
// fn main() {
//     <|>dbg!(92);
// }
// ```
// ->
// ```
// fn main() {
//     92;
// }
// ```
pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;

    if !is_valid_macrocall(&macro_call, "dbg")? {
        return None;
    }

    let is_leaf = macro_call.syntax().next_sibling().is_none();

    let macro_end = if macro_call.semicolon_token().is_some() {
        macro_call.syntax().text_range().end() - TextSize::of(';')
    } else {
        macro_call.syntax().text_range().end()
    };

    // macro_range determines what will be deleted and replaced with macro_content
    let macro_range = TextRange::new(macro_call.syntax().text_range().start(), macro_end);
    let paste_instead_of_dbg = {
        let text = macro_call.token_tree()?.syntax().text();

        // leafiness determines if we should include the parenthesis or not
        let slice_index: TextRange = if is_leaf {
            // leaf means - we can extract the contents of the dbg! in text
            TextRange::new(TextSize::of('('), text.len() - TextSize::of(')'))
        } else {
            // not leaf - means we should keep the parens
            TextRange::up_to(text.len())
        };
        text.slice(slice_index).to_string()
    };

    let target = macro_call.syntax().text_range();
    acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", target, |builder| {
        builder.replace(macro_range, paste_instead_of_dbg);
    })
}

/// Verifies that the given macro_call actually matches the given name
/// and contains proper ending tokens
fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<bool> {
    let path = macro_call.path()?;
    let name_ref = path.segment()?.name_ref()?;

    // Make sure it is actually a dbg-macro call, dbg followed by !
    let excl = path.syntax().next_sibling_or_token()?;

    if name_ref.text() != macro_name || excl.kind() != T![!] {
        return None;
    }

    let node = macro_call.token_tree()?.syntax().clone();
    let first_child = node.first_child_or_token()?;
    let last_child = node.last_child_or_token()?;

    match (first_child.kind(), last_child.kind()) {
        (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']) => Some(true),
        _ => Some(false),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    #[test]
    fn test_remove_dbg() {
        check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");

        check_assist(remove_dbg, "dbg!<|>((1 + 1))", "(1 + 1)");

        check_assist(remove_dbg, "dbg!(1 <|>+ 1)", "1 + 1");

        check_assist(remove_dbg, "let _ = <|>dbg!(1 + 1)", "let _ = 1 + 1");

        check_assist(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "
fn foo(n: usize) {
    if let Some(_) = n.checked_sub(4) {
        // ...
    }
}
",
        );
    }

    #[test]
    fn test_remove_dbg_with_brackets_and_braces() {
        check_assist(remove_dbg, "dbg![<|>1 + 1]", "1 + 1");
        check_assist(remove_dbg, "dbg!{<|>1 + 1}", "1 + 1");
    }

    #[test]
    fn test_remove_dbg_not_applicable() {
        check_assist_not_applicable(remove_dbg, "<|>vec![1, 2, 3]");
        check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
        check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
    }

    #[test]
    fn test_remove_dbg_target() {
        check_assist_target(
            remove_dbg,
            "
fn foo(n: usize) {
    if let Some(_) = dbg!(n.<|>checked_sub(4)) {
        // ...
    }
}
",
            "dbg!(n.checked_sub(4))",
        );
    }

    #[test]
    fn test_remove_dbg_keep_semicolon() {
        // https://github.com/rust-analyzer/rust-analyzer/issues/5129#issuecomment-651399779
        // not quite though
        // adding a comment at the end of the line makes
        // the ast::MacroCall to include the semicolon at the end
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(1 * 20); // needless comment"#,
            r#"let res = 1 * 20; // needless comment"#,
        );
    }

    #[test]
    fn test_remove_dbg_keep_expression() {
        check_assist(
            remove_dbg,
            r#"let res = <|>dbg!(a + b).foo();"#,
            r#"let res = (a + b).foo();"#,
        );
    }

    #[test]
    fn test_remove_dbg_from_inside_fn() {
        check_assist_target(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!("{}", x);
}"#,
            "dbg!(5 + 10)",
        );

        check_assist(
            remove_dbg,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(dbg<|>!(5 + 10));
    println!("{}", x);
}"#,
            r#"
fn square(x: u32) -> u32 {
    x * x
}

fn main() {
    let x = square(5 + 10);
    println!("{}", x);
}"#,
        );
    }
}
<|endoftext|>