aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortslil clingman <>2020-11-10 22:30:08 -0500
committertslil clingman <>2020-11-10 23:22:51 -0500
commit0b1ba27037bd116087cb1bdcfe81d850b20eab97 (patch)
treec3d6dcd96b27e2933c0cddf2d9cd2ccaea59ceef /src
parent9d8ef22f2c5645d6911f2677e877569874ccda38 (diff)
Refactoring
Diffstat (limited to 'src')
-rw-r--r--src/game.rs13
-rw-r--r--src/gui.rs419
-rw-r--r--src/gui/boardwidget.rs264
-rw-r--r--src/gui/mod.rs122
4 files changed, 399 insertions, 419 deletions
diff --git a/src/game.rs b/src/game.rs
index 4a91755..e86cf58 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -154,6 +154,19 @@ impl fmt::Display for WinType {
pub type Stack = Vec<Piece>;
+/*
+const-generics would solve a lot of my consistency problems here,
+
+ GameState<const size : u8> {
+ ...
+ board: [Stack; size],
+ }
+
+but at the time of writing this is not yet part of the language.
+Perhaps one day we'll have a systems programming language with a nice
+dependent type system ;)
+*/
+
struct GameState {
size: u8,
black_flats: u8,
diff --git a/src/gui.rs b/src/gui.rs
deleted file mode 100644
index 3e60913..0000000
--- a/src/gui.rs
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- This file is part of Takwrap.
-
- Takwrap is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- Foobar is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Foobar. If not, see <https://www.gnu.org/licenses/>.
-*/
-
-use fltk::{app::*, button::*, draw::*, menu::*, text::*, window::*};
-use std::rc::*;
-
-use crate::game::*;
-
-struct BoardRendererDetails {
- xorg: i32,
- yorg: i32,
- square_size: i32,
-}
-
-impl BoardRendererDetails {
- fn default() -> BoardRendererDetails {
- BoardRendererDetails {
- xorg: 0,
- yorg: 0,
- square_size: 0,
- }
- }
-
- fn set(&mut self, button: &Button, frame_pad: i32, game_size: i32) {
- let x0 = button.x();
- let y0 = button.y();
- self.xorg = x0 + frame_pad;
- self.yorg = y0 + frame_pad;
- let board_size = i32::min(button.width(), button.height()) - frame_pad * 2;
- self.square_size = board_size / game_size;
- }
-}
-
-struct BoardRenderer {
- even_square_colour: Color,
- odd_square_colour: Color,
- black_colour: Color,
- white_colour: Color,
- stone_size: f32,
- game_size: i32,
- frame_pad: i32,
-}
-
-impl BoardRenderer {
- fn default() -> BoardRenderer {
- BoardRenderer {
- // Sandy brown
- odd_square_colour: Color::from_rgb(244, 164, 96),
- // Moccasin
- even_square_colour: Color::from_rgb(255, 228, 181),
- // Colours for pieces
- black_colour: Color::from_rgb(178, 34, 34),
- white_colour: Color::from_rgb(250, 235, 215),
- // Gap between stones and edged of square
- stone_size: 0.8,
- // I'm not sure where this should go
- game_size: 5,
- frame_pad: 5,
- }
- }
-
- fn with_size(mut self, size: i32) -> BoardRenderer {
- self.game_size = size;
- return self;
- }
-
- fn render_stone(
- &self,
- details: &BoardRendererDetails,
- player: Player,
- stone: Stone,
- row: i32,
- col: i32,
- shift: i32,
- ) {
- set_draw_color(match player {
- Player::Black => self.black_colour,
- Player::White => self.white_colour,
- });
-
- // Gap scales with square size
- let stone_gap = (details.square_size as f32 * (1.00 - self.stone_size)).round() as i32;
- let d = details.square_size - stone_gap * 2;
- let shift = (shift * d) / 8;
- let x1 = details.xorg + col * details.square_size + stone_gap + shift;
- let y1 = details.yorg + row * details.square_size + stone_gap - shift;
-
- // Outline with thickness varying by stone size
- let setup_border_pen = || {
- set_line_style(LineStyle::Solid, i32::max(1, d / 24));
- set_draw_color(Color::Black);
- };
-
- match stone {
- Stone::Capstone => {
- draw_pie(x1, y1, d, d, 0.0, 360.0);
- setup_border_pen();
- let r = (d as f64) / 2.0;
- draw_circle(r + x1 as f64, r + y1 as f64, r);
- }
- Stone::Flat => {
- draw_rectf(x1, y1, d, d);
- setup_border_pen();
- draw_rect_with_color(x1, y1, d, d, Color::Black);
- }
- Stone::Standing => {
- // These look bad shifted so we shift it back if was
- let df = d as f64;
- // and we have to fudge it a little? Some rounding things presumably
- let ds = if shift > 0 { df / 8.0 - 0.5 } else { 0.0 };
- let x1 = x1 as f64 - ds;
- let y1 = y1 as f64 + ds;
-
- let verts = [
- (x1 + df * 0.3, y1 + df * 0.075),
- (x1 + df * 0.925, y1 + df * 0.7),
- (x1 + df * 0.7, y1 + df * 0.925),
- (x1 + df * 0.075, y1 + df * 0.3),
- ];
-
- begin_polygon();
- for (x, y) in verts.iter() {
- vertex(*x, *y)
- }
- end_polygon();
-
- setup_border_pen();
- begin_loop();
- for (x, y) in verts.iter() {
- vertex(*x, *y);
- }
- end_loop();
- }
- }
- set_line_style(LineStyle::Solid, 1);
- }
-
- fn render_icons(
- &self,
- details: &BoardRendererDetails,
- player: Player,
- row: i32,
- col: i32,
- shift: i32,
- ) {
- let stone_gap = details.square_size as f32 * (1.00 - self.stone_size);
- let icon_gap = (stone_gap * 0.15).round() as i32;
- let icon_width = (stone_gap * 0.7).round() as i32;
- let icon_height = icon_width / 2;
-
- let x1 = details.xorg + col * details.square_size + icon_gap;
- let y1 = details.yorg + (row + 1) * details.square_size - icon_gap;
-
- set_line_style(LineStyle::Solid, i32::max(1, icon_width / 8));
- set_draw_color(Color::Black);
-
- set_draw_color(match player {
- Player::Black => self.black_colour,
- Player::White => self.white_colour,
- });
- let y2 = y1 - icon_height * (1 + shift as i32);
- draw_rectf(x1, y2, icon_width, icon_height);
- draw_rect_with_color(x1, y2, icon_width, icon_height, Color::Black);
-
- set_line_style(LineStyle::Solid, 1);
- }
-
- fn render_stack(&self, details: &BoardRendererDetails, row: i32, col: i32, stack: &Stack) {
- // Is there a better way than usize::min((...), i32::MAX as usize) as i32 ?
- let cutoff = stack.len() as i32 - self.game_size;
-
- for (shift, piece) in stack.iter().enumerate() {
- let shift = shift as i32; // TODO: please don't overflow
- if shift < cutoff {
- self.render_icons(details, piece.player, row, col, shift);
- } else {
- self.render_stone(
- details,
- piece.player,
- piece.stone,
- row,
- col,
- shift - i32::max(0, cutoff),
- );
- }
- }
- }
-
- fn render_board(&self, details: &BoardRendererDetails) {
- for x in 0..self.game_size {
- for y in 0..self.game_size {
- if (x + y) % 2 == 0 {
- set_draw_color(self.even_square_colour);
- } else {
- set_draw_color(self.odd_square_colour);
- }
- draw_rectf(
- details.xorg + x * details.square_size,
- details.yorg + y * details.square_size,
- details.square_size,
- details.square_size,
- );
- }
- }
- }
-}
-
-struct GUIBoard {
- button: Button,
- renderer: Rc<BoardRenderer>,
- size: i32,
-}
-
-impl GUIBoard {
- fn new(button: Button, size: i32) -> GUIBoard {
- let renderer = Rc::new(BoardRenderer::default().with_size(size));
- let mut g = GUIBoard {
- button,
- size,
- renderer,
- };
- g.button.draw2(g.render());
- return g;
- }
-
- fn to_square(&self, x: i32, y: i32) -> (i32, i32) {
- // Irritatingly we have to basically inline the exact same
- // BoardRendererDetails code here as elsewhere, because
- // *.draw2 wants to own the closure and i can't find a way to
- // juggle this properly
-
- let mut details = BoardRendererDetails::default();
- details.set(
- &self.button,
- self.renderer.frame_pad,
- self.renderer.game_size,
- );
-
- let x = x - details.xorg;
- let y = y - details.yorg;
-
- (x / details.square_size, y / details.square_size)
- }
-
- fn render(&self) -> impl FnMut(&mut Button) {
- let renderer = self.renderer.clone();
- let game_size = self.size;
-
- // test code
- let stack = vec![
- Piece {
- player: Player::Black,
- stone: Stone::Flat,
- },
- Piece {
- player: Player::White,
- stone: Stone::Flat,
- },
- Piece {
- player: Player::White,
- stone: Stone::Flat,
- },
- Piece {
- player: Player::Black,
- stone: Stone::Flat,
- },
- Piece {
- player: Player::White,
- stone: Stone::Flat,
- },
- Piece {
- player: Player::Black,
- stone: Stone::Flat,
- },
- Piece {
- player: Player::Black,
- stone: Stone::Flat,
- },
- Piece {
- player: Player::White,
- stone: Stone::Flat,
- },
- Piece {
- player: Player::Black,
- stone: Stone::Flat,
- },
- Piece {
- player: Player::White,
- stone: Stone::Standing,
- },
- ];
-
- let mut details = BoardRendererDetails::default();
- move |button: &mut Button| {
- details.set(button, renderer.frame_pad, game_size);
- renderer.render_board(&details);
-
- // Test code for now
- renderer.render_stack(&details, 1, 4, &stack);
- renderer.render_stone(&details, Player::Black, Stone::Capstone, 1, 2, 0);
- renderer.render_stone(&details, Player::Black, Stone::Standing, 2, 2, 0);
- }
- }
-}
-
-#[derive(Clone, Copy)]
-enum GUIMessage {
- None,
- Test,
- BoardClick,
-}
-
-pub struct GUI {
- app: App,
- window: DoubleWindow,
- board: GUIBoard,
- log: TextDisplay,
- sender: Sender<GUIMessage>,
- receiver: Receiver<GUIMessage>,
-}
-
-impl GUI {
- pub fn new() -> GUI {
- let app = App::default();
-
- // TODO: Why is this not the same as
- /*
- let mut window = Window::default()
- .with_size(800, 600)
- .with_label("Takwrap")
- .center_screen();
- */
- // Windows made that way are not resizable?
-
- let mut window = DoubleWindow::new(0, 0, 800, 600, "Takwrap").center_screen();
-
- let (sender, receiver) = channel::<GUIMessage>();
-
- const MENU_HEIGHT: i32 = 24;
- let mut menu = SysMenuBar::default().with_size(800, MENU_HEIGHT);
- // menu.set_text_font(Font::Helvetica);
- menu.set_color(Color::Light2);
- menu.add_emit(
- "&File/New...\t",
- Shortcut::None,
- MenuFlag::Normal,
- sender,
- GUIMessage::None,
- );
- menu.add_emit(
- "&File/Open...\t",
- Shortcut::None,
- MenuFlag::Normal,
- sender,
- GUIMessage::Test,
- );
-
- const LOG_WIDTH: i32 = 800 - 600 + MENU_HEIGHT;
- let mut log = TextDisplay::default()
- .with_size(LOG_WIDTH, 600 - menu.height())
- .with_pos(800 - LOG_WIDTH, menu.height());
- log.set_buffer(Some(TextBuffer::default()));
- log.insert("test\n");
-
- let mut board_button = Button::default()
- .with_size(800 - LOG_WIDTH, 600 - menu.height())
- .below_of(&menu, 0);
- board_button.set_frame(FrameType::EmbossedBox);
- board_button.emit(sender, GUIMessage::BoardClick);
-
- window.resizable(&mut board_button);
- window.end();
- window.show();
-
- let board = GUIBoard::new(board_button, 5);
-
- // button.emit(sender, GUIMessage::Foo);
-
- GUI {
- app,
- board,
- window,
- log,
- sender,
- receiver,
- }
- }
-
- pub fn run(&mut self) -> Result<(), fltk::prelude::FltkError> {
- while self.app.wait() {
- if let Some(msg) = self.receiver.recv() {
- match msg {
- GUIMessage::BoardClick => {
- let x = event_x();
- let y = event_y();
- let (x, y) = self.board.to_square(x, y);
- self.log.insert(&format!("Board click ({},{})\n", x, y));
- }
- _ => (),
- }
- }
- }
- Ok(())
- }
-}
diff --git a/src/gui/boardwidget.rs b/src/gui/boardwidget.rs
new file mode 100644
index 0000000..3eca0ed
--- /dev/null
+++ b/src/gui/boardwidget.rs
@@ -0,0 +1,264 @@
+/*
+ This file is part of Takwrap.
+
+ Takwrap is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Foobar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Foobar. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+use fltk::{app::*, button::*, draw::*};
+use std::rc::*;
+
+use crate::game::*;
+
+// All of theses properties are not expected to change under window
+// resizing, so we don't need more than an Rc<...> to one of these
+// when rendering in button.draw2(...) later. We do, however, need to
+// use compute_square_size because the widget may be resized.
+struct BoardRenderer {
+ even_square_colour: Color,
+ odd_square_colour: Color,
+ black_colour: Color,
+ white_colour: Color,
+ stone_size: f32,
+ game_size: i32,
+ frame_pad: i32,
+ xorg: i32,
+ yorg: i32,
+}
+
+impl BoardRenderer {
+ fn default() -> BoardRenderer {
+ BoardRenderer {
+ // Sandy brown
+ odd_square_colour: Color::from_rgb(244, 164, 96),
+ // Moccasin
+ even_square_colour: Color::from_rgb(255, 228, 181),
+ // Colours for pieces
+ black_colour: Color::from_rgb(178, 34, 34),
+ white_colour: Color::from_rgb(250, 235, 215),
+ // Gap between stones and edged of square
+ stone_size: 0.8,
+ // Defaults
+ game_size: 5,
+ frame_pad: 5,
+ xorg: 0,
+ yorg: 0,
+ }
+ }
+
+ fn with_size(mut self, size: i32) -> BoardRenderer {
+ self.game_size = size;
+ return self;
+ }
+
+ fn with_org(mut self, button: &Button) -> BoardRenderer {
+ let x0 = button.x();
+ let y0 = button.y();
+ self.xorg = x0 + self.frame_pad;
+ self.yorg = y0 + self.frame_pad;
+ return self;
+ }
+
+ fn compute_square_size(&self, button: &Button) -> i32 {
+ let board_size = i32::min(button.width(), button.height()) - self.frame_pad * 2;
+ return board_size / self.game_size;
+ }
+
+ fn render_stone(
+ &self,
+ square_size: i32,
+ player: Player,
+ stone: Stone,
+ row: i32,
+ col: i32,
+ shift: i32,
+ ) {
+ set_draw_color(match player {
+ Player::Black => self.black_colour,
+ Player::White => self.white_colour,
+ });
+
+ // Gap scales with square size
+ let stone_gap = (square_size as f32 * (1.00 - self.stone_size)).round() as i32;
+ let d = square_size - stone_gap * 2;
+ let shift = (shift * d) / 8;
+ let x1 = self.xorg + col * square_size + stone_gap + shift;
+ let y1 = self.yorg + row * square_size + stone_gap - shift;
+
+ // Outline with thickness varying by stone size
+ let setup_border_pen = || {
+ set_line_style(LineStyle::Solid, i32::max(1, d / 24));
+ set_draw_color(Color::Black);
+ };
+
+ match stone {
+ Stone::Capstone => {
+ draw_pie(x1, y1, d, d, 0.0, 360.0);
+ setup_border_pen();
+ let r = (d as f64) / 2.0;
+ draw_circle(r + x1 as f64, r + y1 as f64, r);
+ }
+ Stone::Flat => {
+ draw_rectf(x1, y1, d, d);
+ setup_border_pen();
+ draw_rect_with_color(x1, y1, d, d, Color::Black);
+ }
+ Stone::Standing => {
+ // These look bad shifted so we shift it back if was
+ let df = d as f64;
+ // and we have to fudge it a little? Some rounding things presumably
+ let ds = if shift > 0 { df / 8.0 - 0.5 } else { 0.0 };
+ let x1 = x1 as f64 - ds;
+ let y1 = y1 as f64 + ds;
+
+ let verts = [
+ (x1 + df * 0.3, y1 + df * 0.075),
+ (x1 + df * 0.925, y1 + df * 0.7),
+ (x1 + df * 0.7, y1 + df * 0.925),
+ (x1 + df * 0.075, y1 + df * 0.3),
+ ];
+
+ begin_polygon();
+ for (x, y) in verts.iter() {
+ vertex(*x, *y)
+ }
+ end_polygon();
+
+ setup_border_pen();
+ begin_loop();
+ for (x, y) in verts.iter() {
+ vertex(*x, *y);
+ }
+ end_loop();
+ }
+ }
+ set_line_style(LineStyle::Solid, 1);
+ }
+
+ fn render_icons(&self, square_size: i32, player: Player, row: i32, col: i32, shift: i32) {
+ let stone_gap = square_size as f32 * (1.00 - self.stone_size);
+ let icon_gap = (stone_gap * 0.15).round() as i32;
+ let icon_width = (stone_gap * 0.7).round() as i32;
+ let icon_height = icon_width / 2;
+
+ let x1 = self.xorg + col * square_size + icon_gap;
+ let y1 = self.yorg + (row + 1) * square_size - icon_gap;
+
+ set_line_style(LineStyle::Solid, i32::max(1, icon_width / 8));
+ set_draw_color(Color::Black);
+
+ set_draw_color(match player {
+ Player::Black => self.black_colour,
+ Player::White => self.white_colour,
+ });
+ let y2 = y1 - icon_height * (1 + shift as i32);
+ draw_rectf(x1, y2, icon_width, icon_height);
+ draw_rect_with_color(x1, y2, icon_width, icon_height, Color::Black);
+
+ set_line_style(LineStyle::Solid, 1);
+ }
+
+ fn render_stack(&self, square_size: i32, row: i32, col: i32, stack: &Stack) {
+ // Is there a better way than usize::min((...), i32::MAX as usize) as i32 ?
+ let cutoff = stack.len() as i32 - self.game_size;
+
+ for (shift, piece) in stack.iter().enumerate() {
+ let shift = shift as i32; // TODO: please don't overflow
+ if shift < cutoff {
+ self.render_icons(square_size, piece.player, row, col, shift);
+ } else {
+ self.render_stone(
+ square_size,
+ piece.player,
+ piece.stone,
+ row,
+ col,
+ shift - i32::max(0, cutoff),
+ );
+ }
+ }
+ }
+
+ fn render_board(&self, square_size: i32, board: &Vec<Stack>) {
+ let mut x = 0;
+ let mut y = 0;
+ for stack in board.iter() {
+ self.render_stack(square_size, x, y, stack);
+ x += 1;
+ if x + 1 >= self.game_size {
+ x = 0;
+ y += 1;
+ if y + 1 >= self.game_size {
+ panic!("Game size mismatch between renderer and internal state.");
+ }
+ }
+ }
+ }
+
+ fn render_background(&self, square_size: i32) {
+ for x in 0..self.game_size {
+ for y in 0..self.game_size {
+ if (x + y) % 2 == 0 {
+ set_draw_color(self.even_square_colour);
+ } else {
+ set_draw_color(self.odd_square_colour);
+ }
+ draw_rectf(
+ self.xorg + x * square_size,
+ self.yorg + y * square_size,
+ square_size,
+ square_size,
+ );
+ }
+ }
+ }
+}
+
+pub struct BoardWidget {
+ button: Button,
+ renderer: Rc<BoardRenderer>,
+}
+
+impl BoardWidget {
+ pub fn new(button: Button, size: i32) -> BoardWidget {
+ let renderer = Rc::new(BoardRenderer::default().with_size(size).with_org(&button));
+ let mut g = BoardWidget { button, renderer };
+ g.button.draw2(g.generate_draw(&Vec::new()));
+ return g;
+ }
+
+ pub fn to_square(&self, x: i32, y: i32) -> (i32, i32) {
+ let square_size = self.renderer.compute_square_size(&self.button);
+
+ let x = x - self.renderer.xorg;
+ let y = y - self.renderer.yorg;
+
+ (x / square_size, y / square_size)
+ }
+
+ pub fn generate_draw(&self, board: &Vec<Stack>) -> impl FnMut(&mut Button) {
+ let renderer = self.renderer.clone();
+ let board = board.clone();
+
+ move |button: &mut Button| {
+ let square_size = renderer.compute_square_size(&button);
+ renderer.render_background(square_size);
+ renderer.render_board(square_size, &board);
+
+ // // Test code for now
+ // renderer.render_stack(square_size, 1, 4, &stack);
+ // renderer.render_stone(square_size, Player::Black, Stone::Capstone, 1, 2, 0);
+ // renderer.render_stone(square_size, Player::Black, Stone::Standing, 2, 2, 0);
+ }
+ }
+}
diff --git a/src/gui/mod.rs b/src/gui/mod.rs
new file mode 100644
index 0000000..341bded
--- /dev/null
+++ b/src/gui/mod.rs
@@ -0,0 +1,122 @@
+/*
+ This file is part of Takwrap.
+
+ Takwrap is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Foobar is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Foobar. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+mod boardwidget;
+use crate::gui::boardwidget::*;
+
+use fltk::{app::*, button::*, menu::*, text::*, window::*};
+
+#[derive(Clone, Copy)]
+enum GUIMessage {
+ None,
+ Test,
+ BoardClick,
+}
+
+pub struct GUI {
+ app: App,
+ window: DoubleWindow,
+ board: BoardWidget,
+ log: TextDisplay,
+ sender: Sender<GUIMessage>,
+ receiver: Receiver<GUIMessage>,
+}
+
+impl GUI {
+ pub fn new() -> GUI {
+ let app = App::default();
+
+ // TODO: Why is this not the same as
+ /*
+ let mut window = Window::default()
+ .with_size(800, 600)
+ .with_label("Takwrap")
+ .center_screen();
+ */
+ // Windows made that way are not resizable?
+
+ let mut window = DoubleWindow::new(0, 0, 800, 600, "Takwrap").center_screen();
+
+ let (sender, receiver) = channel::<GUIMessage>();
+
+ const MENU_HEIGHT: i32 = 24;
+ let mut menu = SysMenuBar::default().with_size(800, MENU_HEIGHT);
+ // menu.set_text_font(Font::Helvetica);
+ menu.set_color(Color::Light2);
+ menu.add_emit(
+ "&File/New...\t",
+ Shortcut::None,
+ MenuFlag::Normal,
+ sender,
+ GUIMessage::None,
+ );
+ menu.add_emit(
+ "&File/Open...\t",
+ Shortcut::None,
+ MenuFlag::Normal,
+ sender,
+ GUIMessage::Test,
+ );
+
+ const LOG_WIDTH: i32 = 800 - 600 + MENU_HEIGHT;
+ let mut log = TextDisplay::default()
+ .with_size(LOG_WIDTH, 600 - menu.height())
+ .with_pos(800 - LOG_WIDTH, menu.height());
+ log.set_buffer(Some(TextBuffer::default()));
+ log.insert("test\n");
+
+ let mut board_button = Button::default()
+ .with_size(800 - LOG_WIDTH, 600 - menu.height())
+ .below_of(&menu, 0);
+ board_button.set_frame(FrameType::EmbossedBox);
+ board_button.emit(sender, GUIMessage::BoardClick);
+
+ window.resizable(&mut board_button);
+ window.end();
+ window.show();
+
+ let board = BoardWidget::new(board_button, 5);
+
+ // button.emit(sender, GUIMessage::Foo);
+
+ GUI {
+ app,
+ board,
+ window,
+ log,
+ sender,
+ receiver,
+ }
+ }
+
+ pub fn run(&mut self) -> Result<(), fltk::prelude::FltkError> {
+ while self.app.wait() {
+ if let Some(msg) = self.receiver.recv() {
+ match msg {
+ GUIMessage::BoardClick => {
+ let x = event_x();
+ let y = event_y();
+ let (x, y) = self.board.to_square(x, y);
+ self.log.insert(&format!("Board click ({},{})\n", x, y));
+ }
+ _ => (),
+ }
+ }
+ }
+ Ok(())
+ }
+}