use super::GraphNode;
use crate::graphics::*;
pub struct Node<T: GraphNode> {
pub(super) obj: T,
pub(super) rect: Rect,
pub(super) border: Option<LineType>,
pub(super) text_align: TextAlignment,
pub(super) text_attr: Option<CharAttribute>,
pub(super) edges_in: Vec<u32>,
pub(super) edges_out: Vec<u32>,
pub(super) filtered: bool,
pub(super) selected: bool,
}
impl<T> Node<T>
where
T: GraphNode,
{
pub fn value(&self) -> &T {
&self.obj
}
#[inline(always)]
pub fn is_selected(&self) -> bool {
self.selected
}
pub(super) fn label_content_size(&self, multiselect_ui: bool) -> Size {
let gutter = if multiselect_ui { 2u32 } else { 0u32 };
let mut w = self.rect.width();
let mut h = self.rect.height();
if self.border.is_some() {
w = w.saturating_sub(2);
h = h.saturating_sub(2);
if multiselect_ui {
h = h.saturating_sub(1);
}
} else {
w = w.saturating_sub(2);
}
w = w.saturating_sub(gutter);
Size::new(w, h)
}
pub(super) fn resize(&mut self, mut size: Size, multiselect_ui: bool) {
let p = self.rect.top_left();
let gutter = if multiselect_ui { 2u32 } else { 0u32 };
if self.border.is_some() {
if multiselect_ui {
size.height += 1;
}
size.width += 2;
size.height += 2;
} else {
size.width += 2;
}
size.width += gutter;
self.rect = Rect::with_point_and_size(p, size);
}
#[inline]
pub(super) fn contains(&self, x: i32, y: i32) -> bool {
self.rect.contains(Point::new(x, y))
}
pub(super) fn paint(&self, surface: &mut Surface, attr: CharAttribute, out: &mut String, multiselect_ui: bool) {
surface.fill_rect(self.rect, Character::with_attributes(' ', attr));
let mut clip_rect = if let Some(line_type) = self.border {
surface.draw_rect(self.rect, line_type, attr);
Rect::new(self.rect.left() + 1, self.rect.top() + 1, self.rect.right() - 1, self.rect.bottom() - 1)
} else {
self.rect
};
if multiselect_ui {
surface.write_char(
clip_rect.left(),
clip_rect.top(),
Character::with_attributes(if self.selected { '☑' } else { '☐' }, attr),
);
clip_rect.set_left(clip_rect.left() + 2, false);
}
if self.obj.write_label(out, clip_rect.size()).is_err() {
out.clear();
out.push_str("???");
}
let w = clip_rect.width() as u16;
let x = match self.text_align {
TextAlignment::Left => clip_rect.left(),
TextAlignment::Center => {
let mut len = 0;
for c in out.chars() {
if c == '\n' || c == '\r' {
break;
} else {
len += 1;
if len >= w {
break;
}
}
}
if ((len + w) & 1) ==0 {
clip_rect.left() + (w / 2) as i32
} else {
clip_rect.center_x()
}
}
TextAlignment::Right => clip_rect.right(),
};
let format = TextFormatBuilder::new()
.align(self.text_align)
.attribute(attr)
.wrap_type(WrapType::WordWrap(w))
.position(x, clip_rect.top())
.build();
surface.set_relative_clip(clip_rect.left(), clip_rect.top(), clip_rect.right(), clip_rect.bottom());
surface.write_text(out, &format);
surface.reset_clip();
}
}
pub struct NodeBuilder<T>
where
T: GraphNode,
{
node: Node<T>,
size: Option<Size>,
}
impl<T> NodeBuilder<T>
where
T: GraphNode,
{
pub fn new(obj: T) -> Self {
Self {
node: Node {
obj,
rect: Rect::new(0, 0, 0, 0),
border: None,
text_align: TextAlignment::Center,
text_attr: None,
edges_in: Vec::new(),
edges_out: Vec::new(),
filtered: false,
selected: false,
},
size: None,
}
}
pub fn border(mut self, line_type: LineType) -> Self {
self.node.border = Some(line_type);
self
}
pub fn text_attribute(mut self, attr: CharAttribute) -> Self {
self.node.text_attr = Some(attr);
self
}
pub fn text_alignment(mut self, align: TextAlignment) -> Self {
self.node.text_align = align;
self
}
pub fn size(mut self, size: Size) -> Self {
self.size = Some(size);
self
}
pub fn position(mut self, p: Point) -> Self {
self.node.rect.set_left(p.x, true);
self.node.rect.set_top(p.y, true);
self
}
#[inline(always)]
pub fn build(mut self) -> Node<T> {
if let Some(size) = self.size {
self.node.resize(size, false);
} else {
let sz = self.node.obj.prefered_size();
self.node.resize(sz, false);
}
self.node
}
}
pub struct EditableNode<'a, T: GraphNode + 'a> {
node: &'a mut Node<T>,
changed: &'a mut bool,
}
impl<'a, T> EditableNode<'a, T>
where
T: GraphNode + 'a,
{
pub(super) fn new(node: &'a mut Node<T>, changed: &'a mut bool) -> Self {
Self { node, changed }
}
#[inline(always)]
pub fn value(&self) -> &T {
&self.node.obj
}
#[inline(always)]
pub fn value_mut(&mut self) -> &mut T {
&mut self.node.obj
}
#[inline(always)]
pub fn set_value(&mut self, value: T) {
self.node.obj = value;
*self.changed = true;
}
#[inline(always)]
pub fn bounds(&self) -> Rect {
self.node.rect
}
#[inline(always)]
pub fn set_bounds(&mut self, r: Rect) {
if self.node.rect != r {
self.node.rect = r;
*self.changed = true;
}
}
#[inline(always)]
pub fn position(&self) -> Point {
self.node.rect.top_left()
}
#[inline(always)]
pub fn set_position(&mut self, p: Point) {
self.set_bounds(Rect::with_point_and_size(p, self.node.rect.size()));
}
#[inline(always)]
pub fn size(&self) -> Size {
self.node.rect.size()
}
#[inline(always)]
pub fn set_size(&mut self, size: Size) {
self.set_bounds(Rect::with_point_and_size(self.node.rect.top_left(), size));
}
#[inline(always)]
pub fn text_alignment(&self) -> TextAlignment {
self.node.text_align
}
#[inline(always)]
pub fn set_text_alignment(&mut self, align: TextAlignment) {
if self.node.text_align != align {
self.node.text_align = align;
*self.changed = true;
}
}
#[inline(always)]
pub fn text_attribute(&self) -> Option<CharAttribute> {
self.node.text_attr
}
#[inline(always)]
pub fn set_text_attribute(&mut self, attr: CharAttribute) {
if self.node.text_attr != Some(attr) {
self.node.text_attr = Some(attr);
*self.changed = true;
}
}
#[inline(always)]
pub fn clear_text_attribute(&mut self) {
if self.node.text_attr.is_some() {
self.node.text_attr = None;
*self.changed = true;
}
}
#[inline(always)]
pub fn border(&self) -> Option<LineType> {
self.node.border
}
#[inline(always)]
pub fn set_border(&mut self, border: LineType) {
if self.node.border != Some(border) {
self.node.border = Some(border);
if self.node.rect.height() < 3 {
self.node.rect.set_bottom(self.node.rect.top() + 2, false);
}
*self.changed = true;
}
}
#[inline(always)]
pub fn clear_border(&mut self) {
if self.node.border.is_some() {
self.node.border = None;
*self.changed = true;
}
}
#[inline(always)]
pub fn set_selected(&mut self, selected: bool) {
if self.node.selected != selected {
self.node.selected = selected;
*self.changed = true;
}
}
#[inline(always)]
pub fn is_selected(&self) -> bool {
self.node.selected
}
}