Code Monkey home page Code Monkey logo

druid-enums's Introduction

Druid Enums

This allows deriving a Matcher for any Rust enum. The Matcher allows matching a druid::Widget to each variant of the enum.

Usage

It's not (yet) published on crates.io, thus it should be used through Git:

[dependencies]
druid-enums = { git = "https://github.com/finnerale/druid-enums" }

Example

Just a sketch, but you can find the fully working example here.

#[derive(Clone, Data, Matcher)]
#[matcher(matcher_name = App)] // defaults to AppStateMatcher
enum AppState {
    Login(LoginState),
    #[matcher(builder_name = my_main)]
    Main(MainState),
}

#[derive(Clone, Data, Lens, Default)]
struct LoginState {
    user: String,
}

#[derive(Clone, Data, Lens)]
struct MainState {
    user: String,
    count: u32,
}

fn main() -> Result<(), PlatformError> {
    let window = WindowDesc::new(ui).title("Druid Enums");
    let state = AppState::Login(LoginState::default());
    AppLauncher::with_window(window)
        .use_simple_logger()
        .launch(state)
}

fn ui() -> impl Widget<AppState> {
    // AppState::matcher() or
    App::new()
        .login(login_ui())
        .my_main(main_ui())
}

fn login_ui() -> impl Widget<LoginState> {
    fn login(ctx: &mut EventCtx, state: &mut LoginState, _: &Env) {
        ctx.submit_command(LOGIN.with(MainState::from(state.clone())), None)
    }

    Flex::row()
        .with_child(TextBox::new().lens(LoginState::user))
        .with_spacer(5.0)
        .with_child(Button::new("Login").on_click(login))
        .center()
}

fn main_ui() -> impl Widget<MainState> {
    Flex::column()
        .with_child(Label::dynamic(MainState::welcome_label))
        .with_spacer(5.0)
        .with_child(
            Button::dynamic(MainState::count_label)
                .on_click(|_, state, _| state.count += 1),
        )
        .center()
}

druid-enums's People

Contributors

bastikr avatar luleyleo avatar vladkorotnev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

druid-enums's Issues

Multiple fields in a variant currently fails to compile

Currently, if you have more than 1 field in a variant, the generated code won't compile. You would need to generate a lens to enable everything to hook up correctly. The easiest solution is just to return an error if a variant contains more than 1 field (which is easy enough). The harder solution would be to generate the required lens, but I'm not even sure this is desirable, as it hides some complexity.

The lens would transform (&mut A, &mut B) into &mut (A, B). It would do this by taking ownership of A and B by cloning, allowing the inner methods to modify them, and then replacing the contents of the original tuple with the new values. The cloning should be cheap, because all values used as data should be cheap to clone (either stack-allocated simple types, or types behind reference-counting).

Multiple layers of enums

Hello, I came across a possible limitation when multiple layers of enums are used.
It may be that I'm doing something wrong as well, so I'd appreciate if you could take a look into this example.

To run it:

git clone https://github.com/swfsql/druid-enums
cd druid-enums
cargo run --example login2

This is based on the login example, but I forced a situation where multiple enum layers were used:

enum AppState {
    Login(LoginState),
    Main(MainState),
}

enum MainState {
    LoggedMainState(LoggedMainState),
    UnloggedMainState(UnloggedMainState),
}

Where for the Unlogged state I refrain from labeling the username (as there would be none).

The problem is that after a login (both with or without an username), I can't click the increment button.
I think it's related to some sort of event propagation problem, but I haven't explored this area from druid..

Please let me know if I'm missing something

layout called before WidgetAdded

Currently crashes in debug mode because druid detects that layout is called before the widget is added. I will investigate a fix.

Todo list

  • complain about missing variants
  • default variants
  • tuple variants
  • generics (requires new Druid release)
  • documentation of the derive
  • generating documentation for the ...Matcher widget and its builders
  • add tests
  • ensure consistency with the guarantees Druid offers to widgets
    (e.g. LifeCycle::WidgetAdded as first event)
  • bullet proving error messages
  • nitpicking about implementation details
  • think about making Druid a dependency
  • allow shared data like List does
  • getting this into Druid

Some widgets are not being updated when switching enums back and forth

While the previous one was more of a nitpick, this one looks serious so we'd have to collaborate on that :-)

I'm currently using your solution in an embedded linux application for a device and it's been working great, however, I've noticed one thing that happens if you use it to switch views.

Basically, while updating from one enum element to the same one with different built-in data works, updating to a completely different one, and then the first one but with new data, shows the old data instead.

Can't click when nested inside a ViewSwitcher

Hey,

I'm trying to create an "overlay" based on an enum as described in this Zulip thread.

I want to create an enum and store it in my app state. When this enum isn't a OverlayState::None variant, use a ViewSwitcher to display an overlay corresponding to this variant. If it is an OverlayState::None variant, just display the normal page.

pub enum Overlay {
    Foo(FooOverlayState),
    Bar(BarOverlayState),
    None,
}

pub struct AppState {
    ...
    overlay: Overlay,
    ...
}

fn ui() -> impl Widget<AppState> {
    let view_switcher = ViewSwitcher::new(
        |data: &AppState, _env| data.overlay.clone(),
        |overlay, _data, _env| match overlay {
            OverlayState::None => Box::new(page_ui()),
            _ => Box::new(overlay_ui().lens(AppState::overlay)),
        },
    );
    ...
}

But we hit a snag. When the overlay is first presented, it allows you to click one and then all subsequent clicks have no effect. Hovers, however, continue to register.

Here is minimal reproduction of the issue

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.