Create the game review page and work on navigating to it with a navigation stack
This commit is contained in:
parent
48271389ad
commit
95dc194d5d
|
@ -21,9 +21,10 @@ use otg_core::{
|
||||||
settings::{SettingsRequest, SettingsResponse},
|
settings::{SettingsRequest, SettingsResponse},
|
||||||
Config, CoreRequest, CoreResponse,
|
Config, CoreRequest, CoreResponse,
|
||||||
};
|
};
|
||||||
|
use sgf::Game;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use crate::views::{HomeView, SettingsView};
|
use crate::views::{GameReview, HomeView, SettingsView};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum AppView {
|
enum AppView {
|
||||||
|
@ -37,7 +38,6 @@ enum AppView {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppWindow {
|
pub struct AppWindow {
|
||||||
pub window: adw::ApplicationWindow,
|
pub window: adw::ApplicationWindow,
|
||||||
header: adw::HeaderBar,
|
|
||||||
|
|
||||||
// content is a stack which contains the view models for the application. These are the main
|
// content is a stack which contains the view models for the application. These are the main
|
||||||
// elements that users want to interact with: the home page, the game library, a review, a game
|
// elements that users want to interact with: the home page, the game library, a review, a game
|
||||||
|
@ -46,11 +46,11 @@ pub struct AppWindow {
|
||||||
// we can maintain the state of previous views. Since the two of these work together, they are
|
// we can maintain the state of previous views. Since the two of these work together, they are
|
||||||
// a candidate for extraction into a new widget or a new struct.
|
// a candidate for extraction into a new widget or a new struct.
|
||||||
stack: adw::NavigationView,
|
stack: adw::NavigationView,
|
||||||
content: Vec<AppView>,
|
view_states: Vec<AppView>,
|
||||||
|
|
||||||
// Overlays are for transient content, such as about and settings, which can be accessed from
|
// Overlays are for transient content, such as about and settings, which can be accessed from
|
||||||
// anywhere but shouldn't be part of the main application flow.
|
// anywhere but shouldn't be part of the main application flow.
|
||||||
panel_overlay: gtk::Overlay,
|
overlay: gtk::Overlay,
|
||||||
core: CoreApi,
|
core: CoreApi,
|
||||||
|
|
||||||
// Not liking this, but I have to keep track of the settings view model separately from
|
// Not liking this, but I have to keep track of the settings view model separately from
|
||||||
|
@ -61,28 +61,45 @@ pub struct AppWindow {
|
||||||
impl AppWindow {
|
impl AppWindow {
|
||||||
pub fn new(app: &adw::Application, core: CoreApi) -> Self {
|
pub fn new(app: &adw::Application, core: CoreApi) -> Self {
|
||||||
let window = Self::setup_window(app);
|
let window = Self::setup_window(app);
|
||||||
let header = Self::setup_header();
|
let overlay = Self::setup_overlay();
|
||||||
let panel_overlay = Self::setup_panel_overlay();
|
let stack = adw::NavigationView::new();
|
||||||
let (stack, content) = Self::setup_content(core.clone());
|
let view_states = vec![];
|
||||||
|
|
||||||
|
window.set_content(Some(&overlay));
|
||||||
|
overlay.set_child(Some(&stack));
|
||||||
|
|
||||||
|
let s = Self {
|
||||||
|
window,
|
||||||
|
stack,
|
||||||
|
view_states,
|
||||||
|
overlay,
|
||||||
|
core,
|
||||||
|
settings_view_model: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let home = s.setup_home();
|
||||||
|
|
||||||
|
let _ = s.stack.push(&home);
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_game_review(&self, _game: Game) {
|
||||||
|
let header = adw::HeaderBar::new();
|
||||||
|
let game_review = GameReview::new(self.core.clone());
|
||||||
|
|
||||||
let layout = gtk::Box::builder()
|
let layout = gtk::Box::builder()
|
||||||
.orientation(gtk::Orientation::Vertical)
|
.orientation(gtk::Orientation::Vertical)
|
||||||
.build();
|
.build();
|
||||||
layout.append(&header);
|
layout.append(&header);
|
||||||
layout.append(&panel_overlay);
|
layout.append(&game_review);
|
||||||
panel_overlay.set_child(Some(&stack));
|
|
||||||
|
|
||||||
window.set_content(Some(&layout));
|
let page = adw::NavigationPage::builder()
|
||||||
|
.can_pop(true)
|
||||||
Self {
|
.title("Game Review")
|
||||||
window,
|
.child(&layout)
|
||||||
header,
|
.build();
|
||||||
stack,
|
self.stack.push(&page);
|
||||||
content,
|
|
||||||
panel_overlay,
|
|
||||||
core,
|
|
||||||
settings_view_model: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_settings(&self) {
|
pub fn open_settings(&self) {
|
||||||
|
@ -123,7 +140,7 @@ impl AppWindow {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
s.panel_overlay.add_overlay(&view_model);
|
s.overlay.add_overlay(&view_model);
|
||||||
*s.settings_view_model.write().unwrap() = Some(view_model);
|
*s.settings_view_model.write().unwrap() = Some(view_model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +151,7 @@ impl AppWindow {
|
||||||
let mut view = self.settings_view_model.write().unwrap();
|
let mut view = self.settings_view_model.write().unwrap();
|
||||||
match *view {
|
match *view {
|
||||||
Some(ref mut settings) => {
|
Some(ref mut settings) => {
|
||||||
self.panel_overlay.remove_overlay(settings);
|
self.overlay.remove_overlay(settings);
|
||||||
*view = None;
|
*view = None;
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
|
@ -153,7 +170,7 @@ impl AppWindow {
|
||||||
|
|
||||||
fn setup_header() -> adw::HeaderBar {
|
fn setup_header() -> adw::HeaderBar {
|
||||||
let header = adw::HeaderBar::builder()
|
let header = adw::HeaderBar::builder()
|
||||||
.title_widget(>k::Label::new(Some("Kifu")))
|
.title_widget(>k::Label::new(Some("On the Grid")))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let app_menu = gio::Menu::new();
|
let app_menu = gio::Menu::new();
|
||||||
|
@ -169,28 +186,27 @@ impl AppWindow {
|
||||||
header
|
header
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_panel_overlay() -> gtk::Overlay {
|
fn setup_overlay() -> gtk::Overlay {
|
||||||
gtk::Overlay::new()
|
gtk::Overlay::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_content(core: CoreApi) -> (adw::NavigationView, Vec<AppView>) {
|
fn setup_home(&self) -> adw::NavigationPage {
|
||||||
let stack = adw::NavigationView::new();
|
let header = Self::setup_header();
|
||||||
let content = Vec::new();
|
let home = HomeView::new(self.core.clone(), {
|
||||||
|
let s = self.clone();
|
||||||
|
move |game| s.open_game_review(game)
|
||||||
|
});
|
||||||
|
|
||||||
let home = HomeView::new(core.clone());
|
let layout = gtk::Box::builder()
|
||||||
let _ = stack.push(
|
.orientation(gtk::Orientation::Vertical)
|
||||||
&adw::NavigationPage::builder()
|
.build();
|
||||||
|
layout.append(&header);
|
||||||
|
layout.append(&home);
|
||||||
|
|
||||||
|
adw::NavigationPage::builder()
|
||||||
.can_pop(false)
|
.can_pop(false)
|
||||||
.title("Kifu")
|
.title("Home")
|
||||||
.child(&home)
|
.child(&layout)
|
||||||
.build(),
|
.build()
|
||||||
);
|
|
||||||
// content.push(AppView::Home(HomeViewModel::new(core)));
|
|
||||||
|
|
||||||
(stack, content)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn set_content(content: &impl IsA<gtk::Widget>) -> adw::ViewStack {
|
|
||||||
// self.content.set_child(Some(content));
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ use gtk::{
|
||||||
};
|
};
|
||||||
use image::io::Reader as ImageReader;
|
use image::io::Reader as ImageReader;
|
||||||
use otg_core::{
|
use otg_core::{
|
||||||
ui::{BoardElement, IntersectionElement},
|
|
||||||
Color,
|
Color,
|
||||||
};
|
};
|
||||||
use std::{cell::RefCell, io::Cursor, rc::Rc};
|
use std::{cell::RefCell, io::Cursor, rc::Rc};
|
||||||
|
@ -27,7 +26,7 @@ pub struct BoardPrivate {
|
||||||
drawing_area: gtk::DrawingArea,
|
drawing_area: gtk::DrawingArea,
|
||||||
|
|
||||||
current_player: Rc<RefCell<Color>>,
|
current_player: Rc<RefCell<Color>>,
|
||||||
board: Rc<RefCell<BoardElement>>,
|
// board: Rc<RefCell<BoardElement>>,
|
||||||
cursor_location: Rc<RefCell<Option<Addr>>>,
|
cursor_location: Rc<RefCell<Option<Addr>>>,
|
||||||
|
|
||||||
api: Rc<RefCell<Option<CoreApi>>>,
|
api: Rc<RefCell<Option<CoreApi>>>,
|
||||||
|
@ -43,7 +42,7 @@ impl ObjectSubclass for BoardPrivate {
|
||||||
BoardPrivate {
|
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)),
|
||||||
board: Default::default(),
|
// board: Default::default(),
|
||||||
cursor_location: Default::default(),
|
cursor_location: Default::default(),
|
||||||
api: Default::default(),
|
api: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -55,7 +54,7 @@ impl ObjectImpl for BoardPrivate {
|
||||||
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 board = self.board.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();
|
||||||
|
|
||||||
|
@ -85,6 +84,7 @@ impl ObjectImpl for BoardPrivate {
|
||||||
.set_draw_func(move |_, context, width, height| {
|
.set_draw_func(move |_, context, width, height| {
|
||||||
perftrace("render drawing area", || {
|
perftrace("render drawing area", || {
|
||||||
let render_start = std::time::Instant::now();
|
let render_start = std::time::Instant::now();
|
||||||
|
/*
|
||||||
let board = board.borrow();
|
let board = board.borrow();
|
||||||
|
|
||||||
match background {
|
match background {
|
||||||
|
@ -137,7 +137,9 @@ impl ObjectImpl for BoardPrivate {
|
||||||
pen.star_point(context, col, row);
|
pen.star_point(context, col, row);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
(0..19).for_each(|col| {
|
(0..19).for_each(|col| {
|
||||||
(0..19).for_each(|row| {
|
(0..19).for_each(|row| {
|
||||||
if let IntersectionElement::Filled(stone) = board.stone(row, col) {
|
if let IntersectionElement::Filled(stone) = board.stone(row, col) {
|
||||||
|
@ -162,6 +164,7 @@ impl ObjectImpl for BoardPrivate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
let render_end = std::time::Instant::now();
|
let render_end = std::time::Instant::now();
|
||||||
println!("board rendering time: {:?}", render_end - render_start);
|
println!("board rendering time: {:?}", render_end - render_start);
|
||||||
})
|
})
|
||||||
|
@ -169,10 +172,11 @@ impl ObjectImpl for BoardPrivate {
|
||||||
|
|
||||||
let motion_controller = gtk::EventControllerMotion::new();
|
let motion_controller = gtk::EventControllerMotion::new();
|
||||||
{
|
{
|
||||||
let board = self.board.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 board = board.borrow();
|
let board = board.borrow();
|
||||||
let mut cursor = cursor.borrow_mut();
|
let mut cursor = cursor.borrow_mut();
|
||||||
let hspace_between = ((WIDTH - 40) as f64) / ((board.size.width - 1) as f64);
|
let hspace_between = ((WIDTH - 40) as f64) / ((board.size.width - 1) as f64);
|
||||||
|
@ -195,17 +199,21 @@ impl ObjectImpl for BoardPrivate {
|
||||||
*cursor = addr;
|
*cursor = addr;
|
||||||
drawing_area.queue_draw();
|
drawing_area.queue_draw();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let gesture = gtk::GestureClick::new();
|
let gesture = gtk::GestureClick::new();
|
||||||
{
|
{
|
||||||
let board = self.board.clone();
|
// let board = self.board.clone();
|
||||||
let cursor = self.cursor_location.clone();
|
let cursor = self.cursor_location.clone();
|
||||||
let api = self.api.clone();
|
let api = self.api.clone();
|
||||||
gesture.connect_released(move |_, _, _, _| {
|
gesture.connect_released(move |_, _, _, _| {
|
||||||
|
/*
|
||||||
let board = board.borrow();
|
let board = board.borrow();
|
||||||
let cursor = cursor.borrow();
|
let cursor = cursor.borrow();
|
||||||
|
*/
|
||||||
|
/*
|
||||||
match *cursor {
|
match *cursor {
|
||||||
None => {}
|
None => {}
|
||||||
Some(ref cursor) => {
|
Some(ref cursor) => {
|
||||||
|
@ -220,6 +228,7 @@ impl ObjectImpl for BoardPrivate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,10 +253,12 @@ impl Board {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
pub fn set_board(&self, board: BoardElement) {
|
pub fn set_board(&self, board: BoardElement) {
|
||||||
*self.imp().board.borrow_mut() = board;
|
*self.imp().board.borrow_mut() = board;
|
||||||
self.imp().drawing_area.queue_draw();
|
self.imp().drawing_area.queue_draw();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub fn set_current_player(&self, color: Color) {
|
pub fn set_current_player(&self, color: Color) {
|
||||||
*self.imp().current_player.borrow_mut() = color;
|
*self.imp().current_player.borrow_mut() = color;
|
||||||
|
|
|
@ -45,41 +45,10 @@ impl Default for LibraryPrivate {
|
||||||
let model = gio::ListStore::new::<GameObject>();
|
let model = gio::ListStore::new::<GameObject>();
|
||||||
model.extend_from_slice(&vector);
|
model.extend_from_slice(&vector);
|
||||||
|
|
||||||
/*
|
|
||||||
let factory = gtk::SignalListItemFactory::new();
|
|
||||||
|
|
||||||
factory.connect_setup(move |_, list_item| {
|
|
||||||
let preview = GamePreview::new();
|
|
||||||
list_item
|
|
||||||
.downcast_ref::<gtk::ListItem>()
|
|
||||||
.expect("Needs to be a ListItem")
|
|
||||||
.set_child(Some(&preview));
|
|
||||||
});
|
|
||||||
factory.connect_bind(move |_, list_item| {
|
|
||||||
let game_element = list_item
|
|
||||||
.downcast_ref::<gtk::ListItem>()
|
|
||||||
.expect("Needs to be ListItem")
|
|
||||||
.item()
|
|
||||||
.and_downcast::<GameObject>()
|
|
||||||
.expect("The item has to be a GameObject.");
|
|
||||||
|
|
||||||
let preview = list_item
|
|
||||||
.downcast_ref::<gtk::ListItem>()
|
|
||||||
.expect("Needs to be ListItem")
|
|
||||||
.child()
|
|
||||||
.and_downcast::<GamePreview>()
|
|
||||||
.expect("The child has to be a GamePreview object.");
|
|
||||||
|
|
||||||
match game_element.game() {
|
|
||||||
Some(game) => preview.set_game(game),
|
|
||||||
None => (),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
let selection_model = gtk::NoSelection::new(Some(model.clone()));
|
let selection_model = gtk::NoSelection::new(Some(model.clone()));
|
||||||
let list_view = gtk::ColumnView::builder()
|
let list_view = gtk::ColumnView::builder()
|
||||||
.model(&selection_model)
|
.model(&selection_model)
|
||||||
|
.single_click_activate(true)
|
||||||
.hexpand(true)
|
.hexpand(true)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -116,17 +85,7 @@ impl Default for LibraryPrivate {
|
||||||
.factory(&make_factory(|g| {
|
.factory(&make_factory(|g| {
|
||||||
g.dates
|
g.dates
|
||||||
.iter()
|
.iter()
|
||||||
.map(|date| {
|
.map(|date| format!("{}", date))
|
||||||
format!("{}", date)
|
|
||||||
/*
|
|
||||||
let l = locale!("en-US").into();
|
|
||||||
let options = length::Bag::from_date_style(length::Date::Medium);
|
|
||||||
let date = Date::try_new_iso_date(date.
|
|
||||||
let dtfmt =
|
|
||||||
DateFormatter::try_new_with_length(&l, options).unwrap();
|
|
||||||
dtfmt.format(date).unwrap()
|
|
||||||
*/
|
|
||||||
})
|
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
}))
|
}))
|
||||||
|
@ -196,6 +155,28 @@ impl Default for Library {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Library {
|
impl Library {
|
||||||
|
pub fn new(on_select: impl Fn(Game) + 'static) -> Library {
|
||||||
|
let s = Library::default();
|
||||||
|
|
||||||
|
s.imp().list_view.connect_activate({
|
||||||
|
let s = s.clone();
|
||||||
|
move |_, row_id| {
|
||||||
|
println!("row activated: {}", row_id);
|
||||||
|
|
||||||
|
let object = s.imp().model.item(row_id);
|
||||||
|
let game = object.and_downcast_ref::<GameObject>().unwrap();
|
||||||
|
println!(
|
||||||
|
"{:?} vs. {:?}",
|
||||||
|
game.game().unwrap().white_player,
|
||||||
|
game.game().unwrap().black_player
|
||||||
|
);
|
||||||
|
on_select(game.game().unwrap());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_games(&self, games: Vec<Game>) {
|
pub fn set_games(&self, games: Vec<Game>) {
|
||||||
let games = games
|
let games = games
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
mod board;
|
||||||
|
pub use board::Board;
|
||||||
|
|
||||||
// mod chat;
|
// mod chat;
|
||||||
// pub use chat::Chat;
|
// pub use chat::Chat;
|
||||||
|
|
||||||
|
@ -19,9 +22,6 @@ pub use library::Library;
|
||||||
// mod home;
|
// mod home;
|
||||||
// pub use home::Home;
|
// pub use home::Home;
|
||||||
|
|
||||||
// mod board;
|
|
||||||
// pub use board::Board;
|
|
||||||
|
|
||||||
#[cfg(feature = "screenplay")]
|
#[cfg(feature = "screenplay")]
|
||||||
pub use playing_field::playing_field_view;
|
pub use playing_field::playing_field_view;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>
|
||||||
|
|
||||||
|
This file is part of On the Grid.
|
||||||
|
|
||||||
|
On the Grid 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.
|
||||||
|
|
||||||
|
On the Grid 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 On the Grid. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Game review consists of the board, some information about the players, the game tree, and any
|
||||||
|
// commentary on the current move. This requires four major components, some of which are easier
|
||||||
|
// than others. The game board has to be kept in sync with the game tree, so there's a
|
||||||
|
// communication channel there.
|
||||||
|
//
|
||||||
|
// I'll get all of the information about the game from the core, and then render everything in the
|
||||||
|
// UI. So this will be a heavy lift on the UI side.
|
||||||
|
|
||||||
|
use glib::Object;
|
||||||
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
|
use crate::{components::Board, CoreApi};
|
||||||
|
|
||||||
|
pub struct GameReviewPrivate {}
|
||||||
|
|
||||||
|
impl Default for GameReviewPrivate {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for GameReviewPrivate {
|
||||||
|
const NAME: &'static str = "GameReview";
|
||||||
|
type Type = GameReview;
|
||||||
|
type ParentType = gtk::Grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for GameReviewPrivate {}
|
||||||
|
impl WidgetImpl for GameReviewPrivate {}
|
||||||
|
impl GridImpl for GameReviewPrivate {}
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct GameReview(ObjectSubclass<GameReviewPrivate>) @extends gtk::Grid, gtk::Widget, @implements gtk::Accessible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameReview {
|
||||||
|
pub fn new(api: CoreApi) -> Self {
|
||||||
|
let s: Self = Object::builder().build();
|
||||||
|
|
||||||
|
let board = Board::new(api);
|
||||||
|
s.attach(&board, 0, 0, 2, 2);
|
||||||
|
s.attach(>k::Label::new(Some("white player")), 0, 2, 1, 1);
|
||||||
|
s.attach(>k::Label::new(Some("black player")), 0, 2, 1, 2);
|
||||||
|
s.attach(>k::Label::new(Some("chat")), 1, 2, 2, 2);
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ use otg_core::{
|
||||||
library::{LibraryRequest, LibraryResponse},
|
library::{LibraryRequest, LibraryResponse},
|
||||||
CoreRequest, CoreResponse,
|
CoreRequest, CoreResponse,
|
||||||
};
|
};
|
||||||
|
use sgf::Game;
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -126,11 +127,11 @@ impl WidgetImpl for HomePrivate {}
|
||||||
impl BoxImpl for HomePrivate {}
|
impl BoxImpl for HomePrivate {}
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
pub struct HomeView(ObjectSubclass<HomePrivate>) @extends gtk::Box, gtk::Widget, @implements gtk::Orientable;
|
pub struct HomeView(ObjectSubclass<HomePrivate>) @extends gtk::Box, gtk::Widget, @implements gtk::Orientable, gtk::Accessible;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HomeView {
|
impl HomeView {
|
||||||
pub fn new(api: CoreApi) -> Self {
|
pub fn new(api: CoreApi, on_select_game: impl Fn(Game) + 'static) -> Self {
|
||||||
let s: Self = Object::builder().build();
|
let s: Self = Object::builder().build();
|
||||||
s.set_spacing(4);
|
s.set_spacing(4);
|
||||||
s.set_homogeneous(false);
|
s.set_homogeneous(false);
|
||||||
|
@ -157,7 +158,7 @@ impl HomeView {
|
||||||
s.append(&new_game_button);
|
s.append(&new_game_button);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let library = Library::default();
|
let library = Library::new(move |game| on_select_game(game));
|
||||||
let library_view = gtk::ScrolledWindow::builder()
|
let library_view = gtk::ScrolledWindow::builder()
|
||||||
.hscrollbar_policy(gtk::PolicyType::Never)
|
.hscrollbar_policy(gtk::PolicyType::Never)
|
||||||
.min_content_width(360)
|
.min_content_width(360)
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*
|
||||||
|
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>
|
||||||
|
|
||||||
|
This file is part of On the Grid.
|
||||||
|
|
||||||
|
On the Grid 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.
|
||||||
|
|
||||||
|
On the Grid 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 On the Grid. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mod game_review;
|
||||||
|
pub use game_review::GameReview;
|
||||||
|
|
||||||
mod home;
|
mod home;
|
||||||
pub use home::HomeView;
|
pub use home::HomeView;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue