Skip to content

Instantly share code, notes, and snippets.

@sowbug
Created October 20, 2022 16:56
Show Gist options
  • Save sowbug/55d50723104aad66aba8b7237737f9d3 to your computer and use it in GitHub Desktop.
Save sowbug/55d50723104aad66aba8b7237737f9d3 to your computer and use it in GitHub Desktop.
One way to keep iced view()/update() close to the model code that it uses
use iced::Sandbox;
use iced::{button, Alignment, Button, Column, Container, Element, Settings, Text};
use std::cell::RefCell;
use std::rc::{Rc, Weak};
pub fn main() -> iced::Result {
Counter::run(Settings::default())
}
#[derive(Debug, Clone, Copy)]
enum Message {
IncrementPressed,
DecrementPressed,
}
/// All the methods we expect to see on an Iced entity.
trait IsViewable {
fn view(&mut self) -> Element<Message>;
fn update(&mut self, message: Message);
}
struct ThingViewer {
target: Weak<RefCell<Thing>>,
}
impl IsViewable for ThingViewer {
fn view(&mut self) -> Element<Message> {
if let Some(target) = self.target.upgrade() {
Container::new(Text::new(&target.borrow().name)).into()
} else {
panic!()
}
}
fn update(&mut self, _message: Message) {
todo!()
}
}
/// Creates a small struct that exposes the IsViewable methods and
/// implements them in terms of the "parent" struct that created it.
trait ViewerMaker {
fn make_viewer(&mut self) -> Option<Box<dyn IsViewable>>;
}
/// A factory that makes the IsViewable, except that this factory
/// is specific to one instance of Thing, not static for all
/// Things. Note that this assumes that it was already set up
/// with a me pointer to itself.
impl ViewerMaker for Thing {
fn make_viewer(&mut self) -> Option<Box<dyn IsViewable>> {
if self.me.strong_count() != 0 {
Some(Box::new(ThingViewer {
target: Weak::clone(&self.me),
}))
} else {
// This Thing was set up incorrectly.
None
}
}
}
/// This is the structure that existed in the original app, before
/// we wanted to add an Iced GUI to it.
#[derive(Debug, Default)]
struct Thing {
me: Weak<RefCell<Self>>,
name: String,
}
impl Thing {
// new() isn't public, because it's usually an error to
// instantiate without me set properly.
fn new() -> Self {
Self {
name: "hello. I was built using the IsViewer trait.".to_string(),
..Default::default()
}
}
// Wraps the result of new() in Rc<RefCell<>>, and sets up
// me to point to itself.
//
// Rc::new_cyclic() should make this easier, but I couldn't get it to work
// with an interior RefCell.
// https://doc.rust-lang.org/std/rc/struct.Rc.html#method.new_cyclic
pub fn new_wrapped() -> Rc<RefCell<Self>> {
let wrapped = Rc::new(RefCell::new(Self::new()));
wrapped.borrow_mut().me = Rc::downgrade(&wrapped);
wrapped
}
}
#[derive(Debug, Default)]
struct OtherStruct {
pub things: Vec<Rc<RefCell<Thing>>>,
}
impl OtherStruct {
fn new() -> Self {
Self {
things: vec![Thing::new_wrapped()],
}
}
}
#[derive(Default)]
struct Counter {
value: i32,
increment_button: button::State,
decrement_button: button::State,
o: OtherStruct,
viewers: Vec<Box<dyn IsViewable>>,
}
impl Sandbox for Counter {
type Message = Message;
fn new() -> Self {
let mut r = Self {
o: OtherStruct::new(),
..Default::default()
};
for viewer_maker in r.o.things.iter_mut() {
if let Some(viewer) = viewer_maker.borrow_mut().make_viewer() {
r.viewers.push(viewer);
}
}
r
}
fn title(&self) -> String {
String::from("Counter - Iced")
}
fn update(&mut self, message: Message) {
match message {
Message::IncrementPressed => {
self.value += 1;
}
Message::DecrementPressed => {
self.value -= 1;
}
}
}
fn view(&mut self) -> Element<Message> {
let other_view = self
.viewers
.iter_mut()
.enumerate()
.fold(Column::new(), |column, (_, item)| column.push(item.view()));
Column::new()
.padding(20)
.align_items(Alignment::Center)
.push(
Button::new(&mut self.increment_button, Text::new("Increment"))
.on_press(Message::IncrementPressed),
)
.push(Text::new(self.value.to_string()).size(50))
.push(
Button::new(&mut self.decrement_button, Text::new("Decrement"))
.on_press(Message::DecrementPressed),
)
.push(other_view)
.into()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment