diff options
Diffstat (limited to 'src/gui.rs')
| -rw-r--r-- | src/gui.rs | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/src/gui.rs b/src/gui.rs new file mode 100644 index 0000000..abeb902 --- /dev/null +++ b/src/gui.rs @@ -0,0 +1,341 @@ +extern crate pancurses; + +pub use pancurses::Input::*; +pub use pancurses::*; + +use crate::game::*; + +pub struct GameLog { + action_window: Window, + pieces_window: Window, +} + +impl GameLog { + pub fn new(ypos: i32, xpos: i32, height: i32, width: i32) -> GameLog { + GameLog { + pieces_window: newwin(2, width, ypos, xpos), + action_window: newwin(height, width, ypos + 3, xpos), + } + } + + pub fn update<L: Fn(String)>(&self, game: &Game<L>) { + self.pieces_window.clear(); + self.pieces_window.addstr(format!( + "W: {:2} B: {:2}\nWC: {} BC: {}", + game.query_pieces(Player::White, Stone::Flat), + game.query_pieces(Player::Black, Stone::Flat), + game.query_pieces(Player::White, Stone::Capstone), + game.query_pieces(Player::Black, Stone::Capstone), + )); + self.pieces_window.refresh(); + + self.action_window.clear(); + self.action_window.addstr(game.query_action_lines()); + self.action_window.refresh(); + } +} + +pub struct BoardGUI { + board_window: Window, + cell_windows: Vec<Window>, + size: u8, + cell_height: u8, + cell_width: u8, + hoff: i32, + cur_cell: Position, + has_colours: bool, + white_colour: u8, + black_colour: u8, +} + +#[derive(PartialEq)] +pub enum BGAction { + Quit, + None, +} + +impl BoardGUI { + fn draw_grid(&self) { + let s: i32 = self.size as i32; + let h: i32 = self.cell_height as i32; + let w: i32 = self.cell_width as i32; + for y in 0..h * s + 1 { + for x in 0..w * s + 1 { + if y % h == 0 { + if x % w == 0 { + self.board_window.mvaddch(y, x + self.hoff, '+'); + } else { + self.board_window.mvaddch(y, x + self.hoff, '-'); + } + } else if x % w == 0 { + self.board_window.mvaddch(y, x + self.hoff, '|'); + } + } + } + for y in 0..s { + self.board_window + .mvaddstr(y * h + h / 2, 0, format!("{}.", s - y)); + } + for x in 0..s { + self.board_window.mvaddstr( + s * h + 1, + w * x + w / 2 + self.hoff, + format!("{:x}.", x + 10), + ); + } + self.board_window.refresh(); + } + + fn pos_to_idx(&self, pos: &Position) -> usize { + let y = pos.y as usize; + let x = pos.x as usize; + x + y * (self.size as usize) + } + + fn mv_to_cell(&self, pos: &Position, line: i32) { + if let Some(win) = self.cell_windows.get(self.pos_to_idx(pos)) { + win.mv(line, 0); + win.refresh(); + } + } + + fn draw_stone(&self, win: &Window, top: bool, player: &Player, stone: &Stone) { + if self.has_colours { + match player { + Player::Black => win.attrset(ColorPair(self.black_colour)), + Player::White => win.attrset(ColorPair(self.white_colour)), + }; + if top { + win.addch(match stone { + Stone::Flat => '#', + Stone::Standing => '/', + Stone::Capstone => '*', + }); + } else { + win.addstr(format!("{}", player)); + } + } else { + win.addstr(format!("{}", player)); + if top { + win.addstr(format!("{}", stone)); + } + } + } + + fn draw_stack(&self, pos: &Position, stack: &Stack) { + if let Some(win) = self.cell_windows.get(self.pos_to_idx(pos)) { + win.mv(0, 0); + let l = stack.len() as i32; + if l > 0 { + for i in 0..l { + let piece = &stack[i as usize]; + self.draw_stone(win, i == l - 1, &piece.player, &piece.stone); + } + } + win.refresh(); + } + } + + pub fn update<L: Fn(String)>(&self, game: &Game<L>) { + // Is this really the `best' way? + assert!(game.get_size() == self.size); + let mut pos = Position { x: 0, y: 0 }; + for y in 0..self.size { + pos.y = y; + for x in 0..self.size { + pos.x = x; + if let Some(stack) = game.query_square(&pos) { + self.draw_stack(&pos, stack); + } + } + } + } + + pub fn read_action(&mut self) -> BGAction { + loop { + if let Some(key) = self.board_window.getch() { + match key { + KeyDown => { + if 0 < self.cur_cell.y { + self.cur_cell.y -= 1; + self.mv_to_cell(&self.cur_cell, 0); + } + } + KeyUp => { + if self.cur_cell.y + 1 < self.size { + self.cur_cell.y += 1; + self.mv_to_cell(&self.cur_cell, 0); + } + } + KeyLeft => { + if 0 < self.cur_cell.x { + self.cur_cell.x -= 1; + self.mv_to_cell(&self.cur_cell, 0); + } + } + KeyRight => { + if self.cur_cell.x + 1 < self.size { + self.cur_cell.x += 1; + self.mv_to_cell(&self.cur_cell, 0); + } + } + Character('q') => return BGAction::Quit, + + x => { + self.board_window.addstr(format!("{:?}", x)); + self.board_window.refresh(); + } + } + } + } + } + + pub fn new( + ypos: i32, + xpos: i32, + size: u8, + cell_height: u8, + cell_width: u8, + hoff: u8, + has_colours: bool, + white_colour: u8, + black_colour: u8, + ) -> BoardGUI { + let mut cell_windows: Vec<Window> = Vec::new(); + + let hoff = hoff as i32; + let s: i32 = size as i32; + let h: i32 = cell_height as i32; + let w: i32 = cell_width as i32; + for i in 0..s * s { + let y: i32 = i / s; + let x: i32 = i % s; + cell_windows.push(newwin(h - 1, w - 1, (s - y - 1) * h + 1, hoff + x * w + 1)); + } + + let bg = BoardGUI { + board_window: newwin(h * s + 3, w * s + 3 + hoff, ypos, xpos), + cell_windows: cell_windows, + size: size, + cell_height: cell_height, + cell_width: cell_width, + hoff: hoff, + cur_cell: Position { x: 0, y: 0 }, + has_colours: has_colours, + black_colour: black_colour, + white_colour: white_colour, + }; + + bg.board_window.keypad(true); + bg.board_window.nodelay(false); + + bg.draw_grid(); + + // for i in 0..s * s { + // let y: i32 = i / s; + // let x: i32 = i % s; + // let y = (s - y - 1) * h + 1; + // let x = hoff + x * w + 1; + // bg.cell_windows[i as usize].addstr(format!("{}:({},{})", i, y, x)); + // bg.cell_windows[i as usize].refresh(); + // } + + bg.mv_to_cell(&bg.cur_cell, 0); + bg.board_window.refresh(); + bg + } +} + +pub struct ActionEntry { + window: Window, + max_len: usize, + xstart: i32, +} + +impl ActionEntry { + pub fn new(ypos: i32, xpos: i32, max_len: usize) -> ActionEntry { + let txt = "Action entry: "; + let xstart = txt.len() as i32; + let width = max_len as i32 + xstart + 1; + + let ae = ActionEntry { + window: newwin(1, width, ypos, xpos), + max_len: max_len, + xstart: xstart, + }; + + ae.window.nodelay(false); + ae.window.keypad(true); + + ae.window.addstr(txt); + + for _i in 0..max_len { + ae.window.addch('_'); + } + + ae.window.mv(0, xstart); + ae.window.refresh(); + ae + } + + pub fn read_action(&self) -> String { + self.window.mv(0, self.xstart); + self.window.refresh(); + + let mut result = String::new(); + loop { + if let Some(inp) = self.window.getch() { + match inp { + Character('\n') => return result, + KeyEnter => return result, // TODO: is this ever used? + Character(c) => { + if result.len() < self.max_len { + result.push(c); + self.window.addch(c); + } + } + KeyBackspace => { + if !result.is_empty() { + result.pop(); + let (y, x) = (self.window.get_cur_y(), self.window.get_cur_x()); + self.window.mv(y, x - 1); + self.window.addch('_'); + self.window.mv(y, x - 1); + } + } + _ => (), + }; + } + self.window.refresh(); + } + } +} + +pub struct MessageLog { + window: Window, +} + +impl MessageLog { + pub fn new( + ypos: i32, + xpos: i32, + height: i32, + width: i32, + has_colours: bool, + red_colour: u8, + ) -> MessageLog { + let ml = MessageLog { + window: newwin(height, width, ypos, xpos), + }; + if has_colours { + ml.window.attrset(ColorPair(red_colour)); + } + ml + } + + pub fn log_error(&self, str: String) { + self.window.clear(); + self.window.addstr(str); + self.window.refresh(); + } +} |
