1. 23

This is a talk I gave last week at the SF Rust meetup, and there’s been a fair amount of interest in it. The slides are also available.

  1.  

  2. 3

    Still watching, but I was confused at first because I thought “Counter example in druid” meant it was a counterexample to show why a new approach was needed (and I thought that was funny, because it looked very reasonable), but it was actually an example of a counter. :-D

    1. 1

      Playing around with this declarative ui thing using v9 ideas/the One Name Rule:

      let mut window = Window::new("Hi");
      
      // Window holds the app's state.
      // You want as many discrete objects as possible.
      struct Count {
          n: i32,
      }
      window.register(Count { n: 0 });
      
      let button = window.add::<Button>();
      let label: Id<Label> = window.add::<Label>();
      // Same as (PhantomData<W>, u64)
      
      window.on(event::Press(button), |count: &mut Count| {
          count.n += 1
          // The mutable reference means that the Count object may have changed.
          // So any dependencies will need to be updated.
      });
      window.set0(label, |widget: &mut Label, count: &Count| {
          // This closure uses &Count, so we infer `label` to be a dependency.
          widget.text = format!("Count: {}", count.n);
      });
      window.set1(label, |count: &Count| -> String {
          // We can do this better.
          // &mut Label is very broad, so instead of modifying the entire object,
          // we could modify just one particular sub-object.
          // `Label` implements a trait that allows it to handle `String` objects being thrown at it.
          format!("Count: {}", count.n)
      });
      window.set2(label, |text: &mut String, count: &Count| {
          // We could modify the sub-object by reference; not allocating is nice.
          text.clear();
          write!(text, "Count: {}", count.n);
      });
      // It's also acceptable to have a 0-argument closure.
      window.set3(button, || String::from("Increment"));
      // It doesn't need to be a closure, and it doesn't need to be a String.
      // (These sorts of niceties would be specially provided by Widgets.)
      window.set4(button, "Increment");
      // (These set functions could all be overloaded to just `set`, but maybe not both set0 and set2.)
      
      window.run();
      
      1. 1

        Interesting ideas, I want to look into this more closely. How would you describe the advantages and disadvantages of this approach as opposed to what druid does?

        1. 1

          IIUC druid must diff everything. This could be pretty bad for something like MS Word, where you’ve got a lot of state on screen, and the user is typing events at a rapid pace. With this other approach, the system can see more specifically what you’re changing. It could still run a diff, on a much smaller state, or just not bother & update always. With this setup the flow of control becomes less obvious, and there’s the possibility of an endless loop if diffing isn’t done.

          1. 2

            It diffs from the top of the tree, but absolutely doesn’t need to touch all state while diffing. This is what the lenses and immutable data structures are about - they should be able to deliver a fine grained delta (at modest cost, usually O(log n)) when not much has changed. But we don’t know yet just how well this will work.

            In any case, I think it’s possible to build a UI more along the lines you suggest, and I would like to understand better how that would work.