Implement game tree navigation #237
|
@ -263,8 +263,7 @@ impl Default for DepthTree {
|
|||
pub struct SizeNode {
|
||||
/// Use this to map back to the node in the original game tree. This way we know how to
|
||||
/// correspond from a node in the review tree back to there.
|
||||
#[allow(dead_code)]
|
||||
game_node_id: nary_tree::NodeId,
|
||||
pub game_node_id: nary_tree::NodeId,
|
||||
|
||||
/// How deep into the tree is this node?
|
||||
depth: usize,
|
||||
|
@ -285,6 +284,14 @@ impl DepthTree {
|
|||
&self.nodes[idx].content
|
||||
}
|
||||
|
||||
pub fn parent(&self, node: &Node<T>) -> Option<&Node<T>> {
|
||||
if let Some(parent_idx) = node.parent {
|
||||
self.nodes.get(parent_idx)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Add a node to the parent specified by parent_idx. Return the new index. This cannot be used
|
||||
// to add the root node, but the constructor should handle that, anyway.
|
||||
fn add_node(&mut self, parent_idx: usize, node: T) -> usize {
|
||||
|
|
|
@ -21,6 +21,10 @@ use otg_core::GameReviewViewModel;
|
|||
const WIDTH: i32 = 200;
|
||||
const HEIGHT: i32 = 800;
|
||||
|
||||
const RADIUS: f64 = 7.5;
|
||||
const HIGHLIGHT_WIDTH: f64 = 4.;
|
||||
const SPACING: f64 = 30.;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ReviewTree {
|
||||
widget: gtk::ScrolledWindow,
|
||||
|
@ -32,16 +36,19 @@ pub struct ReviewTree {
|
|||
impl ReviewTree {
|
||||
pub fn new(view: GameReviewViewModel) -> ReviewTree {
|
||||
let drawing_area = gtk::DrawingArea::new();
|
||||
let widget = gtk::ScrolledWindow::builder()
|
||||
.child(&drawing_area)
|
||||
.build();
|
||||
let widget = gtk::ScrolledWindow::builder().child(&drawing_area).build();
|
||||
|
||||
widget.set_width_request(WIDTH);
|
||||
widget.set_height_request(HEIGHT);
|
||||
|
||||
drawing_area.set_height_request(view.tree_max_depth() as i32 * 20 + 40);
|
||||
// TODO: figure out the maximum width of the tree so that we can also set a width request
|
||||
drawing_area.set_height_request(view.tree_max_depth() as i32 * SPACING as i32);
|
||||
|
||||
let s = Self { widget, drawing_area, view };
|
||||
let s = Self {
|
||||
widget,
|
||||
drawing_area,
|
||||
view,
|
||||
};
|
||||
|
||||
s.drawing_area.set_draw_func({
|
||||
let s = s.clone();
|
||||
|
@ -54,20 +61,55 @@ impl ReviewTree {
|
|||
}
|
||||
|
||||
fn redraw(&self, ctx: &Context, _width: i32, _height: i32) {
|
||||
self.view.map_tree(move |node, current| {
|
||||
ctx.set_source_rgb(0.7, 0.7, 0.7);
|
||||
let (row, column) = node.data().position();
|
||||
let y = (row as f64) * 20. + 10.;
|
||||
let x = (column as f64) * 20. + 10.;
|
||||
ctx.arc(x, y, 5., 0., 2. * std::f64::consts::PI);
|
||||
println!("redraw: {} {}", _width, _height);
|
||||
|
||||
if current == Some(node.node_id()) {
|
||||
ctx.set_line_width(3.);
|
||||
} else {
|
||||
#[allow(deprecated)]
|
||||
let context = WidgetExt::style_context(&self.widget);
|
||||
#[allow(deprecated)]
|
||||
let foreground_color = context.lookup_color("sidebar_fg_color").unwrap();
|
||||
#[allow(deprecated)]
|
||||
let accent_color = context.lookup_color("accent_color").unwrap();
|
||||
|
||||
self.view.map_tree(move |node, current| {
|
||||
let parent = node.parent();
|
||||
ctx.set_source_rgb(
|
||||
foreground_color.red().into(),
|
||||
foreground_color.green().into(),
|
||||
foreground_color.blue().into(),
|
||||
);
|
||||
let (row, column) = node.data().position();
|
||||
let y = (row as f64) * SPACING + RADIUS * 2.;
|
||||
let x = (column as f64) * SPACING + RADIUS * 2.;
|
||||
ctx.arc(x, y, RADIUS, 0., 2. * std::f64::consts::PI);
|
||||
let _ = ctx.fill();
|
||||
|
||||
if let Some(parent) = parent {
|
||||
ctx.set_line_width(1.);
|
||||
let (row, column) = parent.data().position();
|
||||
let py = (row as f64) * SPACING + RADIUS * 2.;
|
||||
let px = (column as f64) * SPACING + RADIUS * 2.;
|
||||
ctx.move_to(px, py);
|
||||
ctx.line_to(x, y);
|
||||
let _ = ctx.stroke();
|
||||
}
|
||||
|
||||
let _ = ctx.stroke();
|
||||
if current == Some(node.data().game_node_id) {
|
||||
ctx.set_line_width(HIGHLIGHT_WIDTH);
|
||||
ctx.set_source_rgb(
|
||||
accent_color.red().into(),
|
||||
accent_color.green().into(),
|
||||
accent_color.blue().into(),
|
||||
);
|
||||
ctx.arc(
|
||||
x,
|
||||
y,
|
||||
RADIUS + HIGHLIGHT_WIDTH / 2.,
|
||||
0.,
|
||||
2. * std::f64::consts::PI,
|
||||
);
|
||||
let _ = ctx.stroke();
|
||||
ctx.set_line_width(2.);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue