aboutsummaryrefslogtreecommitdiff
path: root/src/gui.rs
diff options
context:
space:
mode:
authortslil clingman <>2020-11-01 21:50:21 -0500
committertslil clingman <>2020-11-01 21:50:21 -0500
commitc120fcdaac1911280f18ddb153068a2589823b7d (patch)
tree97e443724a5aaffb050dd55bf1719201ecde44c6 /src/gui.rs
parent9ac8a63a5319fb4c846d7b9f777b1572a47d9560 (diff)
Working on rendering
Diffstat (limited to 'src/gui.rs')
-rw-r--r--src/gui.rs327
1 files changed, 288 insertions, 39 deletions
diff --git a/src/gui.rs b/src/gui.rs
index f4ff501..5c4c06a 100644
--- a/src/gui.rs
+++ b/src/gui.rs
@@ -1,44 +1,302 @@
+/*
+ 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::*, frame::*, menu::*, text::*, window::*};
+use crate::game::*;
+
+struct BoardRendererBackend {
+ xorg: i32,
+ yorg: i32,
+ square_size: i32,
+}
+
+impl BoardRendererBackend {
+ fn initialise(frame: &Frame, frame_pad: i32, game_size: i32) -> BoardRendererBackend {
+ let x0 = frame.x();
+ let y0 = frame.y();
+ let xorg = x0 + frame_pad;
+ let yorg = y0 + frame_pad;
+ let board_size = i32::min(frame.width(), frame.height()) - frame_pad * 2;
+
+ BoardRendererBackend {
+ xorg,
+ yorg,
+ square_size: board_size / game_size,
+ }
+ }
+}
+
#[derive(Clone, Copy)]
-enum GUIMessage {
- None,
- Test,
+struct BoardRenderer {
+ even_square_colour: Color,
+ odd_square_colour: Color,
+ black_colour: Color,
+ white_colour: Color,
+ stone_size: f32,
+ game_size: 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,
+ }
+ }
+
+ fn with_size(mut self, size: i32) -> BoardRenderer {
+ self.game_size = size;
+ return self;
+ }
+
+ fn render_stone(
+ &self,
+ backend: &BoardRendererBackend,
+ 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 = (backend.square_size as f32 * (1.00 - self.stone_size)).round() as i32;
+ let d = backend.square_size - stone_gap * 2;
+ let shift = (shift * d) / 8;
+ let x1 = backend.xorg + col * backend.square_size + stone_gap + shift;
+ let y1 = backend.yorg + row * backend.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,
+ backend: &BoardRendererBackend,
+ player: Player,
+ row: i32,
+ col: i32,
+ shift: i32,
+ ) {
+ let stone_gap = backend.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 = backend.xorg + col * backend.square_size + icon_gap;
+ let y1 = backend.yorg + (row + 1) * backend.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, backend: &BoardRendererBackend, 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(backend, piece.player, row, col, shift);
+ } else {
+ self.render_stone(
+ backend,
+ piece.player,
+ piece.stone,
+ row,
+ col,
+ shift - i32::max(0, cutoff),
+ );
+ }
+ }
+ }
+
+ fn render_board(&self, backend: &BoardRendererBackend) {
+ 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(
+ backend.xorg + x * backend.square_size,
+ backend.yorg + y * backend.square_size,
+ backend.square_size,
+ backend.square_size,
+ );
+ }
+ }
+ }
}
struct GUIBoard {
frame: Frame,
- test: bool,
+ renderer: BoardRenderer,
+ size: i32,
}
impl GUIBoard {
- fn new(frame: Frame) -> GUIBoard {
- let mut g = GUIBoard { frame, test: false };
- g.frame.draw2(g.draw());
- g
+ fn new(frame: Frame, size: i32) -> GUIBoard {
+ let renderer = BoardRenderer::default().with_size(size);
+ let mut g = GUIBoard {
+ frame,
+ size,
+ renderer,
+ };
+ g.frame.draw2(g.render());
+ return g;
}
- fn change(&mut self) {
- self.test = !self.test;
+ fn render(&self) -> impl Fn(&mut Frame) {
+ let renderer = self.renderer;
+ let game_size = self.size;
- self.frame.draw2(self.draw());
- self.frame.set_damage(true);
- }
+ 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,
+ },
+ ];
- fn draw(&self) -> impl Fn(&mut Frame) {
- let tc = self.test;
move |frame: &mut Frame| {
- const BOARD_PAD_INNER: i32 = 5;
- let bx = frame.x() + BOARD_PAD_INNER;
- let by = frame.y() + BOARD_PAD_INNER;
- push_clip(frame.x(), frame.y(), frame.width(), frame.height());
- set_draw_color(if tc { Color::Blue } else { Color::Red });
- draw_rectf(bx, by, 100, 30);
- pop_clip();
+ let backend = BoardRendererBackend::initialise(frame, 5, game_size);
+ renderer.render_board(&backend);
+
+ // Test code for now
+ renderer.render_stack(&backend, 1, 4, &stack);
+ renderer.render_stone(&backend, Player::Black, Stone::Capstone, 1, 2, 0);
+ renderer.render_stone(&backend, Player::Black, Stone::Standing, 2, 2, 0);
}
}
}
+#[derive(Clone, Copy)]
+enum GUIMessage {
+ None,
+ Test,
+}
+
pub struct GUI {
app: App,
window: Window,
@@ -65,7 +323,8 @@ impl GUI {
let (sender, receiver) = channel::<GUIMessage>();
- let mut menu = SysMenuBar::default().with_size(800, 24);
+ 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(
@@ -83,28 +342,23 @@ impl GUI {
GUIMessage::Test,
);
- const LOG_WIDTH: i32 = 10 * 16;
- const LOG_PAD: i32 = 0;
+ const LOG_WIDTH: i32 = 800 - 600 + MENU_HEIGHT;
let mut log = TextDisplay::default()
- .with_size(LOG_WIDTH, 600 - 2 * LOG_PAD - menu.height())
- .with_pos(800 - LOG_WIDTH - LOG_PAD, menu.height());
+ .with_size(LOG_WIDTH, 600 - menu.height())
+ .with_pos(800 - LOG_WIDTH, menu.height());
log.set_buffer(Some(TextBuffer::default()));
log.insert("test");
- const BOARD_PAD: i32 = 0;
let mut board_frame = Frame::default()
- .with_size(
- 800 - LOG_WIDTH - LOG_PAD * 2 - BOARD_PAD,
- 600 - 2 * BOARD_PAD - menu.height(),
- )
- .below_of(&menu, BOARD_PAD);
+ .with_size(800 - LOG_WIDTH, 600 - menu.height())
+ .below_of(&menu, 0);
board_frame.set_frame(FrameType::EmbossedBox);
window.resizable(&mut board_frame);
window.end();
window.show();
- let board = GUIBoard::new(board_frame);
+ let board = GUIBoard::new(board_frame, 5);
// button.emit(sender, GUIMessage::Foo);
@@ -119,14 +373,9 @@ impl GUI {
}
pub fn run(&mut self) -> Result<(), fltk::prelude::FltkError> {
- // self.window.show();
-
while self.app.wait() {
if let Some(msg) = self.receiver.recv() {
match msg {
- GUIMessage::Test => {
- self.board.change();
- }
_ => (),
}
}