import { Button, VerticalBox, GridBox, ScrollView, ListView, LineEdit, ComboBox, SpinBox, StandardButton, CheckBox } from "std-widgets.slint"; import { BoardTheme, PieceTheme, BoardThemes, PieceThemes } from "./theme.slint"; import { SettingsDialog_UI } from "settings.slint"; import { MoveHistory } from "move_history.slint"; import { Piece_UI, Move_UI, MoveNotation_UI, PieceType_UI, PieceColour_UI, Square, PieceImg } from "defs.slint"; import { Import_UI } from "import.slint"; import "resources/font/CaskaydiaCoveNerdFont-Regular.ttf"; // re-export for use in rust, this file is what is compiled in build.rs export { SettingsDialog_UI, Import_UI } export component Board_UI inherits Window { title: "Chess"; icon: @image-url("resources/chesslogo.png"); default-font-family: "CaskaydiaCove Nerd Font"; //background: rgb(218, 235, 235); property board-size: 8; property square-size: 60px; in-out property <[Piece_UI]> position; out property selected-from-square: -1; out property selected-to-square: -1; in-out property engine-made-move: true; in-out property last-move: { from-square: -1, to-square: -1, string: "" }; in-out property player-colour: PieceColour-UI.White; in-out property <[MoveNotation-UI]> move-history; in property fen; in-out property depth: "5"; // default depth in-out property gamestate; in property board-theme: BoardThemes.wood; in property piece-theme: PieceThemes.default; callback new-game(); callback make-move() -> bool; callback engine-make-move(); callback refresh-position(); callback get-gamestate(); callback settings-dialog(); callback import-dialog(); public function reset-properties(player-colour: PieceColour-UI, side-to-move-colour: PieceColour-UI) { self.selected-from-square = -1; self.selected-to-square = -1; self.engine-made-move = true; self.last-move = { from-square: -1, to-square: -1 }; root.player-colour = player-colour; refresh-position(); if player-colour == side-to-move-colour { root.engine-made-move = true; } else { root.engine-made-move = false; root.engine-make-move(); } } HorizontalLayout { padding: 10px; VerticalLayout { alignment: center; spacing: 10px; HorizontalLayout { spacing: 5px; alignment: center; Button { text: "\u{eb52}"; width: 5%; height: 25px; padding: 10px; clicked => { settings-dialog(); } } Button { text: "New Game (white)"; width: 25%; height: 25px; padding: 10px; clicked => { root.new-game(); root.reset-properties(PieceColour-UI.White, PieceColour-UI.White); } } Button { text: "New Game (black)"; width: 25%; height: 25px; padding: 10px; clicked => { root.new-game(); root.reset-properties(PieceColour-UI.Black, PieceColour-UI.White); } } } Rectangle { //background: rgb(218, 235, 235); game-info := Text { text: gamestate; horizontal-alignment: center; font-size: 20px; } } VerticalLayout { spacing: 10px; //width: board.width; // VerticalLayout { // for rank in ["8", "7", "6", "5", "4", "3", "2", "1"]: Text { // text: rank; // font-size: 12px; // font-weight: 500; // height: square-size; // vertical-alignment: center; // horizontal-alignment: center; // } // } board := Rectangle { border-color: black; border-width: 1px; border-radius: 10px; width: (square-size * board-size) + square-size; height: (square-size * board-size) + square-size; clip: true; background: root.board-theme.dark-square.darker(0.5); VerticalLayout { alignment: center; for row in board-size: HorizontalLayout { alignment: center; Text { text: root.player-colour == PieceColour-UI.White ? (board-size - row) + " " : (row + 1) + " "; font-size: 14px; font-weight: 500; vertical-alignment: center; } for sq in board-size: square := Square { index: sq + (row * board-size); width: square-size; height: square-size; theme: root.board-theme; piece-img := PieceImg { piece: root.position[parent.index]; width: parent.width; height: parent.height; piece-theme: root.piece-theme; } ta := TouchArea { height: 100%; width: 100%; clicked => { if root.selected-from-square == -1 { root.selected-from-square = square.index; } else if root.selected-from-square >= 0 && root.selected-to-square == -1 && root.engine-made-move { root.selected-to-square = square.index; if root.make-move() == true { // side effect makes move but only returns true if move is valid root.refresh-position(); root.engine-made-move = false; root.engine-make-move(); }// reset variables even if move was invalid root.selected-from-square = -1; root.selected-to-square = -1; // reselect square if there was a players piece there if root.position[square.index].piece-colour == root.player-colour { root.selected-from-square = square.index; } }// shouldnt reach this else else { root.selected-from-square = -1; root.selected-to-square = -1; } } // moved => { // if (self.pressed) { // piece-img.x = self.mouse-x - self.pressed-x; // piece-img.y = self.mouse-y - self.pressed-y; // } // } } states [ last-move when root.last-move.from-square == square.index || root.last-move.to-square == square.index: { square.border-color: salmon; square.border-width: 2px; } clicked-from when root.selected-from-square == square.index && root.engine-made-move: { square.background: square.get-square-background().darker(0.2); } hovered when ta.has-hover && root.engine-made-move: { square.background: square.get-square-background().darker(0.1); } disabled when !root.engine-made-move: { ta.enabled: false; } ] } } HorizontalLayout { alignment: center; // left padding to align with board, not ideal but looks good enough to not annoy me property <[string]> files: root.player-colour == PieceColour-UI.White ? [" a", " b", " c", " d", " e", " f", " g", " h"] : [" h", " g", " f", " e", " d", " c", " b", " a"]; for file in files: Text { text: file; font-size: 14px; font-weight: 500; width: square-size; horizontal-alignment: center; } } } } HorizontalLayout { width: board.width; alignment: center; spacing: 5px; fen_export := LineEdit { width: 65%; visible: true; font-size: 10px; text: root.fen; read-only: true; horizontal-alignment: center; } Button { width: 30%; text: "Import"; clicked => { import-dialog(); } } } } } MoveHistory { move-history: root.move-history; } } }