Skip to content

Instantly share code, notes, and snippets.

@MinusGix
Created October 1, 2021 03:57
Show Gist options
  • Save MinusGix/b2cd23851c90e24633ed73bc1b6e77ba to your computer and use it in GitHub Desktop.
Save MinusGix/b2cd23851c90e24633ed73bc1b6e77ba to your computer and use it in GitHub Desktop.
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