Iced: Dynamic Window Title

Created on 29 Mar 2020  路  13Comments  路  Source: hecrj/iced

I integrated coffee into my application, and now I am giving iced a test drive as well. Nice work!

I noticed a small difference between the frameworks: iced has the window title set by a a title() method defined in the Application trait (coffee has this in the WindowsSettings type that is passed into run()).

The iced::Application::title() documentation says:

This title can be dynamic! The runtime will automatically update the title of your application when necessary.

but it's not clear to me how I could update the title dynamically or even just set it to a value depending on conditions at run() time. Could you provide a quick example?

question

All 13 comments

It works like view, you simply return the title for the current state of your application. The tour example does it here:

https://github.com/hecrj/iced/blob/5b2c71a708f907f2b3c73bcdf56fbca5cd80adde/examples/tour/src/main.rs#L34-L36

In my case I want to pass state in from my builder at construction time. I don't know how to inject, say a value my builder has parsed from the command line into the Application instance state, since Application::run() appears to create its own Application instance. Perhaps I don't have the right mental model?

Perhaps an example would help. With coffee my builder injected initial state through run():

    // Builder impl ...
    fn build(self) -> Result<Self::WindowImpl> {
        Ok(Window {
            size: self
                .size
                .unwrap_or_else(|| sd_to_rect_size(&ScreenDimension::Quarter)),
            size_state: self
                .window_size_state
                .unwrap_or_else(|| WindowSizeState::IntermediateResizable),
            title: self  // <- title is a field initialized to some value determined at runtime, before `Application::run()` is called, and is passed to the type which calls `Application::run()` as state.
                .title
                .unwrap_or_else(|| app_name().unwrap_or_else(|| String::from(msg::UNTITLED))),
        })
    }

in the returned builder object:

   // ...
    fn open(&self) -> Result<(), Error> {
        let window_settings = WindowSettings::try_from(self)?.0;
        Ok(MandelMvc::run(window_settings)?) // <- `title` is a field in `WindowSettings` already set to user preference
    }

But in iced, the title field is not available in WindowState. Basically, in iced, how should I inject initial conditions?

In my case I want to pass state in from my builder at construction time.

This is planned, but not possible currently. See #118 for more details.

Ah, good to know, thank you.

Could you share some details about your particular use case? I gather the title of your application depends on some argument provided to the executable?

I will try to get this sorted in the next day or so.

In my case, I am providing information given to me by the user via the command line (e.g. the start co-ordinates & zoom factor of a Mandelbrot zoom) in the title bar as information.

In general, though, I would like to be able to pass custom state into run(), have that fed to Application::new(), and my new() impl would parse the object and populate the fields of my Application impl. This would allow me to set the initial conditions but maintain the run time isolation of the current model (ie. no globals).

In this example, with this change, my Application::title() impl would simple return self.title and all would be well. But in general, I can see injecting much more involved state/initial conditions at instantiation time.

I am providing information given to me by the user via the command line (the start co-ordinates & zoom factor of a Mandelbrot zoom) in the title bar as information.

Any specific reason why you can't ask for this information using widgets?

In general, I would like to be able to pass custom state into run(), have that fed to Application::new()

Yes, this is what I plan to implement with a Flags associated type.

Any specific reason why you can't ask for this information using widgets?

In this case, I do not plan to interrogate the user once the experience has been invoked.
But in general, as part of a larger solution, I would want to be able to inject initial state like keys, tokens, URLs etc. and even callbacks/closures programatically, allowing an iced-based solution's UX to be conveniently pre-populated with the right values and/or behaviors for the user.

I just opened #246, which should allow you to provide any kind of initial state to Application::new. Let me know what you think.

Yep, that looks perfect, thank you for the quick turnaround! :bowing_man:

I may have spoken too soon! :) In test driving the new API, I'm finding Flags--or more accurately, Flags being included as a field in iced::Settings-- does not compose very well.

For background, I am loosely coupling iced to my application. I would instantiate the application using the Builder pattern to ensure the configuration options from my domain API never constitute an invalid or unsupported combination of features in the dependency (in this case, iced).

I would like to encapsulate iced::Settings into my struct along with additional fields that my Application::new() constructor will understand and utilize. Unfortunately iced::Settings<Flags> wants to encapsulate my settings. This is causing a headache with the borrow checker, because, for example, during initialization, iced::Settings (which includes Flags) needs to be consumed both to initialize the iced::window::Settings, and, again as Flags needs to be used for my own field initializations. Extracting my non-Copy Flags fields from the iced::Settings beforehand invalidates the iced::Settings struct. So there is a lot of Cloneing, Option::replace()ing, otherwise superfluous Copy derives and other things being put into my code to work around these issues.

From my perspective, it would have been nicer for iced::Settings to have been oblivious to Application::Flags, since, by definition, that field is opaque to iced (providing cleaner layering). Any time the user wants augment what needs to be passed in, they would simply set the Flags associated type to their encapsulating custom type, and would do the work within Application::new() to destructure everything including providing a proper iced::Settings (on pain of compile failure).

Unfortunately as it stands, there's a wrestling match where iced::Settings wants to encapsulate my private type, but (for reasons stated above) it would be much more ergonomic for my type to encapsulate iced::Settings.

I should add I'm far from an expert on this API, and I could be using it wrong. I'd be interested if you have any thoughts on this.

Thanks, @hecrj!

iced::Settings isn't really meant to be composable. It's mostly an explicit struct for providing arguments to Application::run.

I believe you should store your settings in your own preferred data structure and only map it to iced::Settings when calling Application::run.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aentity picture aentity  路  4Comments

jiminycrick picture jiminycrick  路  3Comments

johannesvollmer picture johannesvollmer  路  4Comments

rowungiles picture rowungiles  路  4Comments

hecrj picture hecrj  路  3Comments