Compare commits

..

3 Commits

8 changed files with 114 additions and 65 deletions

View File

@ -1,6 +1,6 @@
use std::time::Duration; use std::time::Duration;
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Color { pub enum Color {
Black, Black,
White, White,

View File

@ -2,4 +2,7 @@ mod playing_field;
pub use playing_field::{playing_field, PlayingFieldView}; pub use playing_field::{playing_field, PlayingFieldView};
mod types; mod types;
pub use types::{ChatElement, GobanElement, PlayerCardElement, StoneElement, TextFieldElement}; pub use types::{
BoardElement, ChatElement, IntersectionElement, PlayerCardElement, StoneElement,
TextFieldElement,
};

View File

@ -3,7 +3,7 @@ use crate::ui::types;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PlayingFieldView { pub struct PlayingFieldView {
pub board: types::GobanElement, pub board: types::BoardElement,
pub player_card_black: types::PlayerCardElement, pub player_card_black: types::PlayerCardElement,
pub player_card_white: types::PlayerCardElement, pub player_card_white: types::PlayerCardElement,
pub chat: types::ChatElement, pub chat: types::ChatElement,
@ -12,7 +12,7 @@ pub struct PlayingFieldView {
} }
pub fn playing_field() -> PlayingFieldView { pub fn playing_field() -> PlayingFieldView {
let board = types::GobanElement::default(); let board = types::BoardElement::default();
let player_card_black = types::PlayerCardElement { let player_card_black = types::PlayerCardElement {
color: Color::Black, color: Color::Black,

View File

@ -1,12 +1,12 @@
use crate::types::{Color, Size}; use crate::types::{Color, Size};
#[derive(Clone, Debug)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct Jitter { pub struct Jitter {
pub x: i8, pub x: i8,
pub y: i8, pub y: i8,
} }
#[derive(Clone, Debug)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct StoneElement { pub struct StoneElement {
pub color: Color, pub color: Color,
pub jitter: Jitter, pub jitter: Jitter,
@ -21,28 +21,44 @@ impl StoneElement {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct GobanElement { pub struct PlayStoneRequest {
pub size: Size, col: u8,
pub spaces: Vec<Option<StoneElement>>, row: u8,
} }
impl GobanElement { #[derive(Clone, Copy, Debug, PartialEq)]
pub enum IntersectionElement {
Unplayable,
Empty(PlayStoneRequest),
Filled(StoneElement),
}
#[derive(Clone, Debug)]
pub struct BoardElement {
pub size: Size,
pub spaces: Vec<IntersectionElement>,
}
impl BoardElement {
pub fn new(size: Size) -> Self { pub fn new(size: Size) -> Self {
Self { let spaces: Vec<IntersectionElement> = (0..size.height)
size: size.clone(), .map(|row| {
spaces: (0..((size.width as usize) * (size.height as usize))) (0..size.width)
.map(|_| None) .map(|col| IntersectionElement::Empty(PlayStoneRequest { col, row }))
.collect::<Vec<Option<StoneElement>>>(), .collect::<Vec<IntersectionElement>>()
} })
.collect::<Vec<Vec<IntersectionElement>>>()
.concat();
Self { size, spaces }
} }
pub fn stone_mut<'a>(&'a mut self, row: u8, col: u8) -> &'a mut Option<StoneElement> { pub fn stone_mut<'a>(&'a mut self, row: u8, col: u8) -> &'a mut IntersectionElement {
let addr = self.addr(row, col); let addr = self.addr(row, col);
&mut self.spaces[addr] &mut self.spaces[addr]
} }
pub fn stone(&self, row: u8, col: u8) -> Option<StoneElement> { pub fn stone(&self, row: u8, col: u8) -> IntersectionElement {
self.spaces[self.addr(row, col)].clone() self.spaces[self.addr(row, col)].clone()
} }
@ -51,7 +67,7 @@ impl GobanElement {
} }
} }
impl Default for GobanElement { impl Default for BoardElement {
fn default() -> Self { fn default() -> Self {
Self::new(Size::default()) Self::new(Size::default())
} }

View File

@ -6,7 +6,10 @@ use gtk::{
subclass::prelude::*, subclass::prelude::*,
}; };
use image::io::Reader as ImageReader; use image::io::Reader as ImageReader;
use kifu_core::{ui::GobanElement, Color}; use kifu_core::{
ui::{BoardElement, IntersectionElement},
Color,
};
use std::{cell::RefCell, io::Cursor, rc::Rc}; use std::{cell::RefCell, io::Cursor, rc::Rc};
const WIDTH: i32 = 800; const WIDTH: i32 = 800;
@ -18,36 +21,36 @@ struct Addr {
column: u8, column: u8,
} }
pub struct GobanPrivate { pub struct BoardPrivate {
drawing_area: gtk::DrawingArea, drawing_area: gtk::DrawingArea,
current_player: Rc<RefCell<Color>>, current_player: Rc<RefCell<Color>>,
goban: Rc<RefCell<GobanElement>>, board: Rc<RefCell<BoardElement>>,
cursor_location: Rc<RefCell<Addr>>, cursor_location: Rc<RefCell<Addr>>,
} }
#[glib::object_subclass] #[glib::object_subclass]
impl ObjectSubclass for GobanPrivate { impl ObjectSubclass for BoardPrivate {
const NAME: &'static str = "Goban"; const NAME: &'static str = "Board";
type Type = Goban; type Type = Board;
type ParentType = gtk::Grid; type ParentType = gtk::Grid;
fn new() -> GobanPrivate { fn new() -> BoardPrivate {
GobanPrivate { BoardPrivate {
drawing_area: Default::default(), drawing_area: Default::default(),
current_player: Rc::new(RefCell::new(Color::Black)), current_player: Rc::new(RefCell::new(Color::Black)),
goban: Default::default(), board: Default::default(),
cursor_location: Default::default(), cursor_location: Default::default(),
} }
} }
} }
impl ObjectImpl for GobanPrivate { impl ObjectImpl for BoardPrivate {
fn constructed(&self) { fn constructed(&self) {
self.drawing_area.set_width_request(WIDTH); self.drawing_area.set_width_request(WIDTH);
self.drawing_area.set_height_request(HEIGHT); self.drawing_area.set_height_request(HEIGHT);
let goban = self.goban.clone(); let board = self.board.clone();
let cursor_location = self.cursor_location.clone(); let cursor_location = self.cursor_location.clone();
let current_player = self.current_player.clone(); let current_player = self.current_player.clone();
@ -75,7 +78,7 @@ impl ObjectImpl for GobanPrivate {
self.drawing_area self.drawing_area
.set_draw_func(move |_, context, width, height| { .set_draw_func(move |_, context, width, height| {
let goban = goban.borrow(); let board = board.borrow();
match background { match background {
Ok(Some(ref background)) => { Ok(Some(ref background)) => {
context.set_source_pixbuf(&background, 0., 0.); context.set_source_pixbuf(&background, 0., 0.);
@ -87,8 +90,8 @@ impl ObjectImpl for GobanPrivate {
context.set_source_rgb(0.1, 0.1, 0.1); context.set_source_rgb(0.1, 0.1, 0.1);
context.set_line_width(2.); context.set_line_width(2.);
let hspace_between = ((width - 40) as f64) / ((goban.size.width - 1) as f64); let hspace_between = ((width - 40) as f64) / ((board.size.width - 1) as f64);
let vspace_between = ((height - 40) as f64) / ((goban.size.height - 1) as f64); let vspace_between = ((height - 40) as f64) / ((board.size.height - 1) as f64);
let pen = Pen { let pen = Pen {
x_offset: 20., x_offset: 20.,
@ -97,12 +100,12 @@ impl ObjectImpl for GobanPrivate {
vspace_between, vspace_between,
}; };
(0..goban.size.width).for_each(|col| { (0..board.size.width).for_each(|col| {
context.move_to(20.0 + (col as f64) * hspace_between, 20.0); context.move_to(20.0 + (col as f64) * hspace_between, 20.0);
context.line_to(20.0 + (col as f64) * hspace_between, (height as f64) - 20.0); context.line_to(20.0 + (col as f64) * hspace_between, (height as f64) - 20.0);
let _ = context.stroke(); let _ = context.stroke();
}); });
(0..goban.size.height).for_each(|row| { (0..board.size.height).for_each(|row| {
context.move_to(20.0, 20.0 + (row as f64) * vspace_between); context.move_to(20.0, 20.0 + (row as f64) * vspace_between);
context.line_to((width - 20) as f64, 20.0 + (row as f64) * vspace_between); context.line_to((width - 20) as f64, 20.0 + (row as f64) * vspace_between);
let _ = context.stroke(); let _ = context.stroke();
@ -117,29 +120,38 @@ impl ObjectImpl for GobanPrivate {
(0..19).for_each(|col| { (0..19).for_each(|col| {
(0..19).for_each(|row| { (0..19).for_each(|row| {
match goban.stone(row, col) { match board.stone(row, col) {
None => {} IntersectionElement::Unplayable => {}
Some(element) => { IntersectionElement::Empty(request) => {}
pen.stone(&context, row, col, element.color); IntersectionElement::Filled(stone) => {
pen.stone(&context, row, col, stone.color);
} }
}; };
}) })
}); });
let cursor = cursor_location.borrow(); let cursor = cursor_location.borrow();
pen.ghost_stone(context, cursor.row, cursor.column, *current_player.borrow()); match board.stone(cursor.row, cursor.column) {
IntersectionElement::Empty(_) => pen.ghost_stone(
context,
cursor.row,
cursor.column,
*current_player.borrow(),
),
_ => {}
}
}); });
let motion_controller = gtk::EventControllerMotion::new(); let motion_controller = gtk::EventControllerMotion::new();
{ {
let goban = self.goban.clone(); let board = self.board.clone();
let cursor = self.cursor_location.clone(); let cursor = self.cursor_location.clone();
let drawing_area = self.drawing_area.clone(); let drawing_area = self.drawing_area.clone();
motion_controller.connect_motion(move |_, x, y| { motion_controller.connect_motion(move |_, x, y| {
let goban = goban.borrow(); let board = board.borrow();
let mut cursor = cursor.borrow_mut(); let mut cursor = cursor.borrow_mut();
let hspace_between = ((WIDTH - 40) as f64) / ((goban.size.width - 1) as f64); let hspace_between = ((WIDTH - 40) as f64) / ((board.size.width - 1) as f64);
let vspace_between = ((HEIGHT - 40) as f64) / ((goban.size.height - 1) as f64); let vspace_between = ((HEIGHT - 40) as f64) / ((board.size.height - 1) as f64);
let addr = Addr { let addr = Addr {
column: ((x.round() - 20.) / hspace_between).round() as u8, column: ((x.round() - 20.) / hspace_between).round() as u8,
@ -153,25 +165,42 @@ impl ObjectImpl for GobanPrivate {
}); });
} }
let gesture = gtk::GestureClick::new();
{
let board = self.board.clone();
let cursor = self.cursor_location.clone();
gesture.connect_released(move |_, _, _, _| {
let board = board.borrow();
let cursor = cursor.borrow();
match board.stone(cursor.row, cursor.column) {
IntersectionElement::Empty(request) => {
println!("need to send request: {:?}", request)
}
_ => {}
}
});
}
self.drawing_area.add_controller(motion_controller); self.drawing_area.add_controller(motion_controller);
self.drawing_area.add_controller(gesture);
} }
} }
impl WidgetImpl for GobanPrivate {} impl WidgetImpl for BoardPrivate {}
impl GridImpl for GobanPrivate {} impl GridImpl for BoardPrivate {}
glib::wrapper! { glib::wrapper! {
pub struct Goban(ObjectSubclass<GobanPrivate>) @extends gtk::Grid, gtk::Widget; pub struct Board(ObjectSubclass<BoardPrivate>) @extends gtk::Grid, gtk::Widget;
} }
impl Goban { impl Board {
pub fn new() -> Self { pub fn new() -> Self {
let s: Self = Object::builder().build(); let s: Self = Object::builder().build();
s.attach(&s.imp().drawing_area, 1, 1, 1, 1); s.attach(&s.imp().drawing_area, 1, 1, 1, 1);
s s
} }
pub fn set_board(&self, goban: GobanElement) { pub fn set_board(&self, board: BoardElement) {
*self.imp().goban.borrow_mut() = goban; *self.imp().board.borrow_mut() = board;
self.imp().drawing_area.queue_draw(); self.imp().drawing_area.queue_draw();
} }

View File

@ -7,8 +7,8 @@ pub use chat::Chat;
mod playing_field; mod playing_field;
pub use playing_field::PlayingField; pub use playing_field::PlayingField;
mod goban; mod board;
pub use goban::Goban; pub use board::Board;
#[cfg(feature = "screenplay")] #[cfg(feature = "screenplay")]
pub use playing_field::playing_field_view; pub use playing_field::playing_field_view;

View File

@ -1,10 +1,10 @@
use crate::ui::{Chat, Goban, PlayerCard}; use crate::ui::{Board, Chat, PlayerCard};
use glib::Object; use glib::Object;
use gtk::{prelude::*, subclass::prelude::*}; use gtk::{prelude::*, subclass::prelude::*};
use kifu_core::{ use kifu_core::{
ui::{ ui::{
ChatElement, GobanElement, PlayerCardElement, PlayingFieldView, StoneElement, BoardElement, ChatElement, IntersectionElement, PlayerCardElement, PlayingFieldView,
TextFieldElement, StoneElement, TextFieldElement,
}, },
Color, Size, Color, Size,
}; };
@ -23,7 +23,7 @@ pub struct PlayingFieldView {
*/ */
pub struct PlayingFieldPrivate { pub struct PlayingFieldPrivate {
goban: Goban, goban: Board,
player_card_white: Rc<RefCell<Option<PlayerCard>>>, player_card_white: Rc<RefCell<Option<PlayerCard>>>,
player_card_black: Rc<RefCell<Option<PlayerCard>>>, player_card_black: Rc<RefCell<Option<PlayerCard>>>,
chat: Rc<RefCell<Option<Chat>>>, chat: Rc<RefCell<Option<Chat>>>,
@ -32,7 +32,7 @@ pub struct PlayingFieldPrivate {
impl Default for PlayingFieldPrivate { impl Default for PlayingFieldPrivate {
fn default() -> Self { fn default() -> Self {
Self { Self {
goban: Goban::new(), goban: Board::new(),
player_card_white: Rc::new(RefCell::new(None)), player_card_white: Rc::new(RefCell::new(None)),
player_card_black: Rc::new(RefCell::new(None)), player_card_black: Rc::new(RefCell::new(None)),
chat: Rc::new(RefCell::new(None)), chat: Rc::new(RefCell::new(None)),
@ -48,7 +48,7 @@ impl ObjectSubclass for PlayingFieldPrivate {
fn new() -> Self { fn new() -> Self {
Self { Self {
goban: Goban::new(), goban: Board::new(),
player_card_white: Rc::new(RefCell::new(None)), player_card_white: Rc::new(RefCell::new(None)),
player_card_black: Rc::new(RefCell::new(None)), player_card_black: Rc::new(RefCell::new(None)),
chat: Rc::new(RefCell::new(None)), chat: Rc::new(RefCell::new(None)),
@ -90,9 +90,13 @@ impl PlayingField {
#[cfg(feature = "screenplay")] #[cfg(feature = "screenplay")]
pub fn playing_field_view() -> PlayingFieldView { pub fn playing_field_view() -> PlayingFieldView {
let mut board = GobanElement::default(); let mut board = BoardElement::new(Size {
*board.stone_mut(4, 4) = Some(StoneElement::new(Color::White)); width: 19,
*board.stone_mut(15, 15) = Some(StoneElement::new(Color::Black)); height: 19,
});
*board.stone_mut(4, 4) = IntersectionElement::Filled(StoneElement::new(Color::White));
*board.stone_mut(15, 15) = IntersectionElement::Filled(StoneElement::new(Color::Black));
*board.stone_mut(18, 18) = IntersectionElement::Unplayable;
let player_card_black = PlayerCardElement { let player_card_black = PlayerCardElement {
color: Color::Black, color: Color::Black,
name: "Savanni".to_owned(), name: "Savanni".to_owned(),

View File

@ -1,3 +0,0 @@
[toolchain]
channel = "1.68.2"
components = ["rust-src"]