Created
October 1, 2021 03:57
-
-
Save MinusGix/b2cd23851c90e24633ed73bc1b6e77ba to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::rc::Rc; | |
use std::cell::{RefCell, Ref, RefMut}; | |
use std::any::Any; | |
// Fake UI | |
pub struct UI { | |
children: Vec<Box<dyn Node>>, | |
} | |
impl UI { | |
pub fn new() -> Self { | |
Self { | |
children: vec![ | |
Box::new(Text { | |
text: "Hola".to_owned() | |
}), | |
Box::new(BasicB), | |
Box::new(BasicB), | |
Box::new(Text { | |
text: "FUck you".to_owned() | |
}), | |
Box::new(BasicB), | |
] | |
} | |
} | |
pub fn get_node(&self, index: usize) -> Option<&dyn Node> { | |
self.children.get(index).map(|x| x.as_ref()) | |
} | |
pub fn get_node_mut(&mut self, index: usize) -> Option<&mut dyn Node> { | |
self.children.get_mut(index).map(|x| x.as_mut()) | |
} | |
} | |
pub trait AsAny { | |
fn as_any(&self) -> &dyn Any; | |
fn as_mut_any(&mut self) -> &mut dyn Any; | |
} | |
impl<T: Any> AsAny for T { | |
fn as_any(&self) -> &dyn Any { | |
self | |
} | |
fn as_mut_any(&mut self) -> &mut dyn Any { | |
self | |
} | |
} | |
pub trait Node: 'static + AsAny {} | |
#[derive(Debug)] | |
pub struct Text { | |
text: String, | |
} | |
impl Node for Text {} | |
#[derive(Debug)] | |
pub struct BasicB; | |
impl Node for BasicB {} | |
pub type RefUI = Rc<RefCell<UI>>; | |
pub struct View { | |
ui: RefUI, | |
inputs: Vec<Input>, | |
} | |
impl View { | |
pub fn new(ui: RefUI) -> Self { | |
Self { | |
ui, | |
inputs: Vec::new(), | |
} | |
} | |
pub fn add_input(&mut self, input: Input) { | |
self.inputs.push(input); | |
} | |
pub fn get_input_mut(&mut self, i: usize) -> Option<&mut Input> { | |
self.inputs.get_mut(i) | |
} | |
pub fn get_node(&self, i: usize) -> Option<Ref<dyn Node>> { | |
// We can't get a proper Option<&dyn Node> and return it | |
// because it would be referencing the contents of ui | |
// and it needs to know when things are referencing it or not | |
// so, we have to use `map` and thus have it wrapped in a Ref. | |
// God is dead | |
let ui = self.ui.borrow(); | |
if ui.get_node(i).is_some() { | |
// Ref::map requires a reference so we can't keep the Option | |
// in Option<&dyn Node> | |
// So we have to unwrap it, but we don't want to panic and want | |
// to keep the option | |
// So we first get the node outside the map to check if it exists | |
// (we can't return that one, lifetimes) | |
// before *actually* getting it to return | |
Some(Ref::map(ui, |ui| ui.get_node(i).unwrap())) | |
} else { | |
None | |
} | |
} | |
pub fn get_node_mut(&mut self, i: usize) -> Option<RefMut<dyn Node>> { | |
let mut ui = self.ui.borrow_mut(); | |
if ui.get_node_mut(i).is_some() { | |
Some(RefMut::map(ui, |ui| ui.get_node_mut(i).unwrap())) | |
} else { | |
None | |
} | |
} | |
pub fn get_t_node_mut<T: Node>(&mut self, i: usize) -> Option<RefMut<T>> { | |
let mut node = self.get_node_mut(i)?; | |
let mut node = RefMut::map(node, |node| | |
node | |
.as_mut_any() | |
.downcast_mut::<T>().unwrap() | |
); | |
Some(node) | |
} | |
} | |
pub struct Input { | |
ui: RefUI, | |
ui_idx: usize, | |
} | |
impl Input { | |
pub fn get_value(&mut self) -> String { | |
let idx: usize = self.ui_idx; | |
self.ui | |
.borrow() // Ref<UI> | |
.get_node(idx) // Option<&dyn Node> | |
.unwrap() // &dyn Node | |
.as_any() // &dyn Any | |
.downcast_ref::<Text>() // Option<&Text> | |
.unwrap() // &Text | |
.text // &String | |
.clone() // String | |
} | |
pub fn set_value(&mut self, text: String) { | |
let idx: usize = self.ui_idx; | |
self.ui | |
.borrow_mut() // RefMut<UI> | |
.get_node_mut(idx) // Option<&mut dyn Node> | |
.unwrap() // &mut dyn Node | |
.as_mut_any() // &mut dyn Any | |
.downcast_mut::<Text>() // Option<&mut Text> | |
.unwrap() // &mut Text | |
.text = text; | |
} | |
} | |
fn main () { | |
let ui = UI::new(); | |
let ui: RefUI = Rc::new(RefCell::new(ui)); | |
// Clone the Rc | |
let mut view = View::new(ui.clone()); | |
view.add_input(Input { | |
ui: ui.clone(), | |
ui_idx: 0, | |
}); | |
view.add_input(Input { | |
ui: ui.clone(), | |
ui_idx: 3, | |
}); | |
// ------ | |
{ | |
let input: &mut Input = view.get_input_mut(0).unwrap(); | |
let value: String = input.get_value(); | |
println!("V1: {}", value); | |
input.set_value("Damn commies".to_owned()); | |
let value: String = input.get_value(); | |
println!("V1: {}", value); | |
}; | |
// The above manages to work pretty well, even if it is ugly internally, but | |
// that is primarily because we are cloning the data out or | |
// we are calling a function that does the placing of data in by itself | |
// We would start getting ugly bits when we wanted to access the nodes | |
// directly | |
{ | |
let mut node: RefMut<dyn Node> = view.get_node_mut(0).unwrap(); | |
let mut node: RefMut<dyn Any> = RefMut::map(node, |node| node.as_mut_any()); | |
let mut node: RefMut<Text> = RefMut::map(node, |node| node.downcast_mut::<Text>().unwrap()); | |
node.text = "fuck".to_owned(); | |
}; | |
{ | |
let input: &mut Input = view.get_input_mut(0).unwrap(); | |
let value: String = input.get_value(); | |
println!("V1: {}", value); | |
}; | |
// Shorter version | |
{ | |
let mut text: RefMut<Text> = view.get_t_node_mut::<Text>(0).unwrap(); | |
text.text = "no".to_owned(); | |
}; | |
{ | |
let input: &mut Input = view.get_input_mut(0).unwrap(); | |
let value: String = input.get_value(); | |
println!("V1: {}", value); | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment