fabulous-dev / Fabulous

Declarative UI framework for cross-platform mobile & desktop apps, using MVU and F# functional programming
https://fabulous.dev
Apache License 2.0
1.16k stars 122 forks source link

Reworked components #1087

Closed TimLariviere closed 3 weeks ago

TimLariviere commented 3 weeks ago

BEFORE

// Non MVU
Component("Key") {
    let! count = Context.State(0)

    Label($"Count is {count.Current}")
    Button("Increment", fun () -> count.Set(count.Current + 1))
    Button("Decrement", fun () -> count.Set(count.Current - 1))
}

// MVU
let program = Program.stateful init update
Component("Key", program) {
    let! model = Mvu.State

    Label($"Count is {model.Count}")
    Button("Increment", Increment)
    Button("Decrement", Decrement)
}

// No support for environment

AFTER

// Non MVU -- NO DIFFERENCE
Component("Key") {
    let! count = Context.State(0)

    Label($"Count is {count.Current}")
    Button("Increment", fun () -> count.Set(count.Current + 1))
    Button("Decrement", fun () -> count.Set(count.Current - 1))
}

// MVU
let program = Program.stateful init update
Component("Key") {
    let! model = Context.Mvu program

    Label($"Count is {model.Count}")
    Button("Increment", Increment)
    Button("Decrement", Decrement)
}

// Read-only system environment, will auto-refresh when env value changes
Component("Key") {
    let! theme = Context.Environment(EnvironmentKeys.Theme)

    Label($"Current theme is {theme}")
}

// Read-only user environment, will auto-refresh when env value changes
module EnvironmentKeys =
    let Count = EnvironmentKey<int>("Count")

let child () =
    Component("child") {
        let! count = Context.Environment(EnvironmentKeys.Count)

        Label($"Count is {count}")
    }

let root () =
    Component("Key") {
        let! count = Context.State(0)

        (VStack() {
            child ()
        })
            .environment(EnvironmentKeys.Count, count.Current)
    }

// Mutable user environment, will auto-refresh when internal env object value changes
type Settings =
    inherit EnvironmentObject()

    let mutable _isUserLoggedIn = false
    member this.IsUserLoggedIn
        with get() = _isUserLoggedIn
        and set v =
            if _isUserLoggedIn <> v then
                _isUserLoggedIn <- v
               this.NotifyChange()

module EnvironmentKeys =
    let Settings = EnvironmentKey<Settings>("Settings")

let writer () =
    Component("writer") {
        let! settings = Context.EnvironmentObject(EnvironmentKeys.Settings)

        Button("Log in", fun () -> settings.IsUserLoggedIn <- true)
        Button("Log out", fun () -> settings.IsUserLoggedIn <- false)
    }

let reader () =
    Component("reader") {
        let! settings = Context.EnvironmentObject(EnvironmentKeys.Settings)

        Label($"User is {settings.IsUserLoggedIn}")
    }

let root () =
    Component("Key") {
        let settings = Settings()

        (VStack() {
            writer ()
            reader ()
        })
            .environment(EnvironmentKeys.Settings, settings)
    }