summaryrefslogtreecommitdiff
path: root/src/gui.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui.rs')
-rw-r--r--src/gui.rs341
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();
+ }
+}