Render the goban along with ghost images for the cursor #36
|
@ -12,6 +12,15 @@ pub struct Size {
|
||||||
pub height: u8,
|
pub height: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Size {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
width: 19,
|
||||||
|
height: 19,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct AppState {
|
pub(crate) struct AppState {
|
||||||
game: Option<GameState>,
|
game: Option<GameState>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,4 @@ 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, GameBoardElement, PlayerCardElement, TextFieldElement};
|
pub use types::{ChatElement, GobanElement, PlayerCardElement, StoneElement, TextFieldElement};
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::ui::types;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PlayingFieldView {
|
pub struct PlayingFieldView {
|
||||||
pub board: types::GameBoardElement,
|
pub board: types::GobanElement,
|
||||||
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,16 +12,7 @@ pub struct PlayingFieldView {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn playing_field() -> PlayingFieldView {
|
pub fn playing_field() -> PlayingFieldView {
|
||||||
let mut spaces = Vec::new();
|
let board = types::GobanElement::default();
|
||||||
(0..19).for_each(|_| spaces.push(Vec::new()));
|
|
||||||
|
|
||||||
let board = types::GameBoardElement {
|
|
||||||
size: Size {
|
|
||||||
width: 19,
|
|
||||||
height: 19,
|
|
||||||
},
|
|
||||||
spaces,
|
|
||||||
};
|
|
||||||
|
|
||||||
let player_card_black = types::PlayerCardElement {
|
let player_card_black = types::PlayerCardElement {
|
||||||
color: Color::Black,
|
color: Color::Black,
|
||||||
|
|
|
@ -12,10 +12,49 @@ pub struct StoneElement {
|
||||||
pub jitter: Jitter,
|
pub jitter: Jitter,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StoneElement {
|
||||||
|
pub fn new(color: Color) -> Self {
|
||||||
|
Self {
|
||||||
|
color,
|
||||||
|
jitter: Jitter { x: 0, y: 0 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GameBoardElement {
|
pub struct GobanElement {
|
||||||
pub size: Size,
|
pub size: Size,
|
||||||
pub spaces: Vec<Vec<StoneElement>>,
|
pub spaces: Vec<Option<StoneElement>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GobanElement {
|
||||||
|
pub fn new(size: Size) -> Self {
|
||||||
|
Self {
|
||||||
|
size: size.clone(),
|
||||||
|
spaces: (0..((size.width as usize) * (size.height as usize)))
|
||||||
|
.map(|_| None)
|
||||||
|
.collect::<Vec<Option<StoneElement>>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stone_mut<'a>(&'a mut self, row: u8, col: u8) -> &'a mut Option<StoneElement> {
|
||||||
|
let addr = self.addr(row, col);
|
||||||
|
&mut self.spaces[addr]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stone(&self, row: u8, col: u8) -> Option<StoneElement> {
|
||||||
|
self.spaces[self.addr(row, col)].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addr(&self, row: u8, col: u8) -> usize {
|
||||||
|
((row as usize) * (self.size.width as usize) + (col as usize)) as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GobanElement {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(Size::default())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
|
@ -1,30 +1,131 @@
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
use gtk::{prelude::*, subclass::prelude::*};
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
|
use kifu_core::{ui::GobanElement, Color};
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
const WIDTH: i32 = 800;
|
||||||
|
const HEIGHT: i32 = 800;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GobanPrivate;
|
pub struct GobanPrivate {
|
||||||
|
drawing_area: gtk::DrawingArea,
|
||||||
|
|
||||||
|
goban: Rc<RefCell<GobanElement>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
impl ObjectSubclass for GobanPrivate {
|
impl ObjectSubclass for GobanPrivate {
|
||||||
const NAME: &'static str = "Goban";
|
const NAME: &'static str = "Goban";
|
||||||
type Type = Goban;
|
type Type = Goban;
|
||||||
type ParentType = gtk::DrawingArea;
|
type ParentType = gtk::Grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for GobanPrivate {}
|
impl ObjectImpl for GobanPrivate {
|
||||||
|
fn constructed(&self) {
|
||||||
|
self.drawing_area.set_width_request(WIDTH);
|
||||||
|
self.drawing_area.set_height_request(HEIGHT);
|
||||||
|
|
||||||
|
let goban = self.goban.clone();
|
||||||
|
self.drawing_area
|
||||||
|
.set_draw_func(move |_, context, width, height| {
|
||||||
|
let goban = goban.borrow();
|
||||||
|
context.set_source_rgb(0.7, 0.7, 0.7);
|
||||||
|
let _ = context.paint();
|
||||||
|
|
||||||
|
context.set_source_rgb(0.1, 0.1, 0.1);
|
||||||
|
context.set_line_width(2.);
|
||||||
|
let hspace_between = ((width - 40) as f64) / ((goban.size.width - 1) as f64);
|
||||||
|
let vspace_between = ((height - 40) as f64) / ((goban.size.height - 1) as f64);
|
||||||
|
|
||||||
|
let pen = Pen {
|
||||||
|
x_offset: 20.,
|
||||||
|
y_offset: 20.,
|
||||||
|
hspace_between,
|
||||||
|
vspace_between,
|
||||||
|
};
|
||||||
|
|
||||||
|
(0..goban.size.width).for_each(|col| {
|
||||||
|
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);
|
||||||
|
let _ = context.stroke();
|
||||||
|
});
|
||||||
|
(0..goban.size.height).for_each(|row| {
|
||||||
|
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);
|
||||||
|
let _ = context.stroke();
|
||||||
|
});
|
||||||
|
|
||||||
|
context.set_source_rgb(0.1, 0.1, 0.0);
|
||||||
|
vec![4, 9, 15].into_iter().for_each(|col| {
|
||||||
|
vec![4, 9, 15].into_iter().for_each(|row| {
|
||||||
|
pen.star_point(context, col, row);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
(0..19).for_each(|col| {
|
||||||
|
(0..19).for_each(|row| {
|
||||||
|
match goban.stone(row, col) {
|
||||||
|
None => {}
|
||||||
|
Some(element) => {
|
||||||
|
pen.stone(&context, row, col, element.color);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
impl WidgetImpl for GobanPrivate {}
|
impl WidgetImpl for GobanPrivate {}
|
||||||
impl DrawingAreaImpl for GobanPrivate {}
|
impl GridImpl for GobanPrivate {}
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct Goban(ObjectSubclass<GobanPrivate>) @extends gtk::DrawingArea, gtk::Widget;
|
pub struct Goban(ObjectSubclass<GobanPrivate>) @extends gtk::Grid, gtk::Widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Goban {
|
impl Goban {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let s: Self = Object::builder().build();
|
let s: Self = Object::builder().build();
|
||||||
s.set_width_request(1024);
|
s.attach(&s.imp().drawing_area, 1, 1, 1, 1);
|
||||||
s.set_height_request(768);
|
|
||||||
|
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_board(&self, goban: GobanElement) {
|
||||||
|
*self.imp().goban.borrow_mut() = goban;
|
||||||
|
self.imp().drawing_area.queue_draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Pen {
|
||||||
|
x_offset: f64,
|
||||||
|
y_offset: f64,
|
||||||
|
hspace_between: f64,
|
||||||
|
vspace_between: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pen {
|
||||||
|
fn star_point(&self, context: &cairo::Context, row: u8, col: u8) {
|
||||||
|
context.arc(
|
||||||
|
self.x_offset + (col as f64) * self.hspace_between,
|
||||||
|
self.y_offset + (row as f64) * self.vspace_between,
|
||||||
|
10.,
|
||||||
|
0.,
|
||||||
|
2. * std::f64::consts::PI,
|
||||||
|
);
|
||||||
|
let _ = context.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stone(&self, context: &cairo::Context, row: u8, col: u8, color: Color) {
|
||||||
|
match color {
|
||||||
|
Color::White => context.set_source_rgb(0.9, 0.9, 0.9),
|
||||||
|
Color::Black => context.set_source_rgb(0.0, 0.0, 0.0),
|
||||||
|
};
|
||||||
|
context.arc(
|
||||||
|
20.0 + (col as f64) * self.hspace_between,
|
||||||
|
20.0 + (row as f64) * self.vspace_between,
|
||||||
|
25.0,
|
||||||
|
0.0,
|
||||||
|
2.0 * std::f64::consts::PI,
|
||||||
|
);
|
||||||
|
let _ = context.fill();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,10 @@ use crate::ui::{Chat, Goban, PlayerCard};
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
use gtk::{prelude::*, subclass::prelude::*};
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
use kifu_core::{
|
use kifu_core::{
|
||||||
ui::{ChatElement, GameBoardElement, PlayerCardElement, PlayingFieldView, TextFieldElement},
|
ui::{
|
||||||
|
ChatElement, GobanElement, PlayerCardElement, PlayingFieldView, StoneElement,
|
||||||
|
TextFieldElement,
|
||||||
|
},
|
||||||
Color, Size,
|
Color, Size,
|
||||||
};
|
};
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
@ -10,7 +13,7 @@ use std::{cell::RefCell, rc::Rc};
|
||||||
/*
|
/*
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PlayingFieldView {
|
pub struct PlayingFieldView {
|
||||||
pub board: types::GameBoardElement,
|
pub board: types::GobanElement,
|
||||||
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,
|
||||||
|
@ -19,13 +22,24 @@ pub struct PlayingFieldView {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct PlayingFieldPrivate {
|
pub struct PlayingFieldPrivate {
|
||||||
|
goban: Goban,
|
||||||
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>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for PlayingFieldPrivate {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
goban: Goban::new(),
|
||||||
|
player_card_white: Rc::new(RefCell::new(None)),
|
||||||
|
player_card_black: Rc::new(RefCell::new(None)),
|
||||||
|
chat: Rc::new(RefCell::new(None)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
impl ObjectSubclass for PlayingFieldPrivate {
|
impl ObjectSubclass for PlayingFieldPrivate {
|
||||||
const NAME: &'static str = "PlayingField";
|
const NAME: &'static str = "PlayingField";
|
||||||
|
@ -34,6 +48,7 @@ impl ObjectSubclass for PlayingFieldPrivate {
|
||||||
|
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
goban: Goban::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)),
|
||||||
|
@ -53,12 +68,13 @@ impl PlayingField {
|
||||||
pub fn new(view: PlayingFieldView) -> PlayingField {
|
pub fn new(view: PlayingFieldView) -> PlayingField {
|
||||||
let s: Self = Object::builder().build();
|
let s: Self = Object::builder().build();
|
||||||
|
|
||||||
let goban = Goban::new();
|
|
||||||
let player_card_white = PlayerCard::new(view.player_card_white);
|
let player_card_white = PlayerCard::new(view.player_card_white);
|
||||||
let player_card_black = PlayerCard::new(view.player_card_black);
|
let player_card_black = PlayerCard::new(view.player_card_black);
|
||||||
let chat = Chat::new(view.chat);
|
let chat = Chat::new(view.chat);
|
||||||
|
|
||||||
s.attach(&goban, 1, 1, 1, 2);
|
s.imp().goban.set_board(view.board);
|
||||||
|
|
||||||
|
s.attach(&s.imp().goban, 1, 1, 1, 2);
|
||||||
s.attach(&player_card_black, 2, 1, 1, 1);
|
s.attach(&player_card_black, 2, 1, 1, 1);
|
||||||
s.attach(&player_card_white, 3, 1, 1, 1);
|
s.attach(&player_card_white, 3, 1, 1, 1);
|
||||||
s.attach(&chat, 2, 2, 2, 1);
|
s.attach(&chat, 2, 2, 2, 1);
|
||||||
|
@ -73,17 +89,9 @@ impl PlayingField {
|
||||||
|
|
||||||
#[cfg(feature = "screenplay")]
|
#[cfg(feature = "screenplay")]
|
||||||
pub fn playing_field_view() -> PlayingFieldView {
|
pub fn playing_field_view() -> PlayingFieldView {
|
||||||
let mut spaces = Vec::new();
|
let mut board = GobanElement::default();
|
||||||
(0..19).for_each(|_| spaces.push(Vec::new()));
|
*board.stone_mut(4, 4) = Some(StoneElement::new(Color::White));
|
||||||
|
*board.stone_mut(15, 15) = Some(StoneElement::new(Color::Black));
|
||||||
let board = GameBoardElement {
|
|
||||||
size: Size {
|
|
||||||
width: 19,
|
|
||||||
height: 19,
|
|
||||||
},
|
|
||||||
spaces,
|
|
||||||
};
|
|
||||||
|
|
||||||
let player_card_black = PlayerCardElement {
|
let player_card_black = PlayerCardElement {
|
||||||
color: Color::Black,
|
color: Color::Black,
|
||||||
name: "Savanni".to_owned(),
|
name: "Savanni".to_owned(),
|
||||||
|
|
Loading…
Reference in New Issue