Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature proposal: context properties #3508

Open
jpnurmi opened this issue Sep 19, 2023 · 5 comments
Open

Feature proposal: context properties #3508

jpnurmi opened this issue Sep 19, 2023 · 5 comments
Labels
a:language-slint Compiler for the .slint language (mO,bF) enhancement New feature or request

Comments

@jpnurmi
Copy link
Collaborator

jpnurmi commented Sep 19, 2023

It would be nice to have a way to propagate information to children without explicitly passing them as properties on each level aka. "property drilling".

enum ThemeMode { light, dark }

context Theme {
    property<ThemeMode> mode;
    property<brush> background;
    property<brush> foreground;
}

// Adapts its looks according to the theme in the context
component MyThemableWidget {
    Rectangle {
        background: Theme.background; // <--
        Text {
            color: Theme.foreground; // <--
        }
    }
}

export component MyWindow inherits Window {
    Theme {
        mode: ThemeMode.light;
        MyThemableWidget {}
    }

    Theme {
        mode: ThemeMode.dark;
        Dialog {
            MyThemableWidget {}
        }
    }
}

This is conceptually similar to Context in React and InheritedWidget in Flutter. In Flutter, inherited widgets are used for implementing many core framework features including theming, localization, layout direction, navigation, and many external packages for dependency injection and state management.

@jpnurmi
Copy link
Collaborator Author

jpnurmi commented Sep 19, 2023

Alternative syntax proposal with explicit Provider and Consumer elements:

enum ThemeMode { light, dark }

struct Theme {
    mode: ThemeMode,
    background: brush,
    foreground: brush,
}

// Adapts its looks according to the theme in the context
component MyThemableWidget {
    Consumer {
        property<Theme> theme;
        Rectangle {
            background: theme.background;
            Text {
                color: theme.foreground;
            }
        }
    }
}

export component MyWindow inherits Window {
    Provider {
        property<Theme> theme = { mode: ThemeMode.light };
        MyThemableWidget {}
    }

    Provider {
        property<Theme> theme = { mode: ThemeMode.dark };
        Dialog {
            MyThemableWidget {}
        }
    }
}

@tronical
Copy link
Member

I agree. I had a similar proposal here (see "Per-Window" Specific State). It's basically the same except that your proposal makes the context implicit at the use site.

FWIW, I really like your proposal. My main concern is what should happen if MyThemableWidget is used where the context is not available? I guess the compiler can automatically determine that MyThemableWidget is a required context and error out accordingly.

Such a context declaration could then also be imported/exported between .slint files I guess.

@ogoffart
Copy link
Member

That generally sounds like a good idea.
Previously i was thinking having property that propagates. See #2338

In this case, the syntax with such system would be:

struct Theme { background: brush, foreground: brush, } 

// Adapts its looks according to the theme in the context
component MyThemableWidget {
    context property theme<Theme>;   <- declare a context property
    Rectangle {
        background: theme.background; 
        Text {
            color: theme.foreground;
        }
    }
}

export component MyWindow inherits Window {
        context property <Theme> theme: SomeGlobal.lightTheme;
        MyThemableWidget {}
}

but i can see that there is a scoping issue as the theme property may be ambiguous. Having specific context or scope that need to be imported as you suggest might help.

One issue is that it gets more difficult to preview standalone component if they don't get these property. We already have the problem with default-font-familly and default-font-size on the window.

@tronical
Copy link
Member

One issue is that it gets more difficult to preview standalone component if they don't get these property.

One thing that we could do is to offer extra UI for users to change the contextual properties that are missing. In this example if previewing MyThemableWidget we know that need at least a Theme and we might need more. So we could offer property editor tabs to tune values beyond the default? (the default either being the default of the primitive type or a default declared by the context author)

If we don't want to add support for default values to struct (which would be fair), then the first proposal would allow for that.

@jpnurmi
Copy link
Collaborator Author

jpnurmi commented Sep 19, 2023

Thinking ahead, something like the first syntax with properties, default values, bindings, and even functions instead of plain struct data would be much more powerful.

There are many possibilities for the syntax. Here's yet another variant with an annotation. :)

@context
component Navigator {
    out property<bool> canPop: pages.length > 0;
    in property<[component]> pages;

    public function push(page: component) {}
    public function pop() {}
}

component BackButton inherits Button {
    text: "<";
    enabled: Navigator.canPop;
    clicked => { Navigator.pop(); }
}

component ToolBar {
    in property<string> title;

    HorizontalLayout {
        BackButton {}
        Text { text: root.title; }
    }
}

component MyPage {
    in property<string> title;

    VerticalLayout {
        ToolBar { title: root.title; }
        @children
    }
}

export component MyWindow inherits Window {
    Navigator {
        pages: [
            MyPage { title: "Foo"; },
            if bar: MyPage { title: "Bar"; },
            if baz: MyPage { title: "Baz"; }
        ];
    }
}

@hunger hunger added the enhancement New feature or request label Oct 4, 2023
@ogoffart ogoffart added the a:language-slint Compiler for the .slint language (mO,bF) label Jan 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:language-slint Compiler for the .slint language (mO,bF) enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants