Open PvdBerg1998 opened 5 years ago
I think this is an essential feature for designing any kind of complex UI. It takes me 30 seconds to recompile my 90 line main file when I make a small change to the view which is just far far too long for iterative development.
EDIT: I have done a lot of work with Flutter and it's hot reload is amazing. I think nothing that extensive is needed but just reloading the view on the fly would be great.
It might be worth thinking about clever imgui integration instead, at least in the short run?
I'm taking a stab at this and I'll upload what I have once I get a bit more working, but I'll try and enumerate what I've learned so far.
I think this requires two main components:
iced
to have, regardless of hot swap capabilities.
iced::Element
s.
iced
user producivity!)HashMap<String, &dyn Any>
that the app fills on initialization and downcasting to the exact function pointer type, but am running into an issue with Any::downcast
to a function pointer.iced
for support.iced
itself.Some additional notes:
ron
as the markup "language" and can currently generate/reload non-interactive stateless views. Most features I described above are not implemented.@hecrj What are your thoughts on an iced layout markup language? I'll happily file an issue with all the details/prototype I have if you're into it.
https://github.com/tangmi/iced-hotswap-prototype
Here's a working proof-of-concept of the following features:
iml::Element
: Column, Row, Text, Button, Slider (with minimal parameters)Some caveats:
@tangmi Hey! That's really cool!
I really like how it keeps update
code type-safe. I imagine we could build our own Application
trait wrapping iced::Application
to hide the Hotswappo
struct and view
and provide the state directly to update
. This should also help us use a different strategy for release mode (ideally generating Rust view code from the template and using the iced::Application
directly for no runtime cost).
I would also probably rename the various callback_name
to the actual widget method names (i.e. on_press
for a Button
, etc.), but I imagine the details of the language are not very important right now.
Overall this is very, very promising! It could ease development process a lot and help us build view editors!
automatically creates, owns, and maintains widget "ephemeral" state (i.e. button::State)
It will be very tricky to do this well without an important performance impact and logic errors (i.e. diffing).
At least for the time being, I would require the user to define widget state in the application state and provide it in the markup. Then, if the user adds a new widget in the template and the state field doesn't exist, we can fall back to the current naive tracker for hot reloading. However, I would make it a requirement for release builds (i.e. compilation error if the state fields do not exist).
Glad you like it! Thanks for the feedback!
I like the idea of hiding Hotswapo
behind a separate Application
trait opening up the possibility for transparent hotswapping in debug builds for the user.
For the markup side, I was imagining two "runtimes":
In both cases, the result would be a function something like fn view(&loaded_layout, &mut user_state) -> Element<'_, M>
. I might be handwaving the difficulty of implementation/maintaining cost this dual-action runtime, but I believe some combo dynamic/static layout file solution gets us both perf and flexibility.
@tangmi I was about to try something similar, nice work! For my needs, I was thinking of a different, possibly simpler, approach. I'll expand on that here in the hopes that it adds to this discussion.
Instead of generating any rust layout code, my priority lies in hot-reloading the styling of existing widgets. The plan was to define all styles and sizes in a single serializable object, I'll call StyleSheet
for now. I would then reference this StyleSheet
whenever I define a widget in an iced fn view()
. Similar to CSS classes.
The hot swapping logic would live in an async loop constantly looking for edits to a file, then triggering an iced Command
when an edit is detected. This Command
would use serde to import this (TOML?) markup file containing the serialized StyleSheet
object. This would allow me to rapidly iterate on the styling (widths, spacing, coloring, borders, etc) of my framed out UI. Once I'm happy with it, I can just drop this tweaked StyleSheet
object into my rust code to remove the need for the hot-reloading runtime.
This approach obviously doesn't allow for generating view
code on the fly, but I haven't found that to be an issue so far. Compartmentalizing groupings of reusable widgets into "components" that I can then drop into the top level UI has been painless, and makes the top-level code very readable when the entire gui isn't expressed in a giant nest of .push()
s. I've also found that iced's Elmish way of defining layout makes it dead simple to get the layout, if not the styling, where I want it to be very rapidly.
I'm also skeptical of the utility of generated view
code, because it quickly becomes customized - especially views that hold lists of things - with .filter()
s and .fold()
s. Once I've prototyped a gui and hook up all this logic, the next time I want to tweak my view
code via hot-reloading, I'd have to manually reconfigure all this logic, which seems to defeat the purpose!
In my mind, that's like generating HTML, when what I really want is to just hot-reload my CSS.
With all that said, maybe what I have in mind isn't the right approach, or representative of most users' needs! I think this is really important work for iced and I'm glad you're tackling it. π
@hecrj I'll keep poking at this when I can find time. I think the main takeaway from my investigation is that both a markup language and hotswapping can probably be developed as a library with no* changes in iced needed (great work on library design!).
*I think some mechanically consistent declaration of widgets and their state/attributes (not sure what this looks like!) can help simplify the markup language implementation and allow for custom widgets.
I think some mechanically consistent declaration of widgets and their state/attributes (not sure what this looks like!) can help simplify the markup language implementation and allow for custom widgets.
Could you elaborate? What do you mean by this?
(Be aware this is a half-baked idea)
I think I mean some consistent mapping from markup elements to code. E.g. If all widgets provide an "attribute setter" interface (e.g. Widget::set_attribute(attribute_name: &str, attribute_value: _)
), then it's pretty feasible to go from
Button(
content: Text("button"),
on_click: "ButtonClicked",
disabled: false, // <- I know this isn't how `Button` handles disabled currently!
),
to
let markup_element = todo!("deserialized from file");
let mut button = Button::with_state(widgets_state.get(markup_element.get_id()));
button.set_attribute("content", get_element(markup_element.get("content"));
button.set_attribute("on_click", get_callback(markup_element.get("on_click")));
button.set_attribute("disabled", markup_element.get("disabled"));
without any special understanding of Button
. We just need to know that widgets have attributes that can be set (compared to my prototype, which needs to know about every widget, which is an unbounded set if we include user implementations of iced_native::widget::Widget
).
...although I'm unsure how to make this typesafe! The widget might need to have some manifest declaring its attributes and the types of its attributes? Maybe through some code generation (this would require changes in iced)?
I think I mean some consistent mapping from markup elements to code. E.g. If all widgets provide an "attribute setter" interface
Could you implement a trait for each widget in the meantime, e.g.
trait Attribute {
fn set_attribute<T>(&mut self, attribute: String, value: T);
}
then leave it up to the user to implement this trait if they want to use a custom widget with hot reloading?
I think the attribute would need to take &dyn Any
(+ Clone
?), otherwise, I'm not sure what an implementation could look like
struct Foo {
val: i32,
}
impl Attribute for Foo {
fn set_attribute<T>(&mut self, attribute: String, value: T) {
if attribute == "val" {
self.val = value; // self.val expects `i32`, value is `T`. !!
}
}
}
Something along these lines?
I think something like that world work! To support non-num-primitives, I think it'd still need Any
... we're getting into weird territory, but maybe something like this?
Edit: this assumes we know the type of each attribute upfront... I'm not confident this is knowledge the markup engine would have without some widget manifest...
I've made some progress on (my version of) this. I ended up going the route of directly placing variables in the attribute setter calls, e.g.:
widget_xyz.spacing(stylesheet.spacing_outer)
The stylesheet is just a struct that contains these appearance variables, as well as a separate struct of colors, so the appearances can reference them. I'm still working on the file watching and serde side of things now, but it looks like the fundamentals are proved out.
This implementation does not allow dynamic renaming of appearance names, because these are set at compile time. So, once you have set widget_xyz.spacing()
to use stylesheet.spacing_outer
, you can't change it at runtime; all you can change is the value of spacing_outer
.
The immediate benefit of this is type checking. The compiler can guarantee that the type of spacing_outer
is compatible with .spacing()
's signature. An alternate implementation might instead use a str
to look up a matching appearance from a HashMap
. The risk with that is you can assign a nonexistent appearance name (typo, stylesheet change), and it won't be caught at compile time. It's possible this could be solved with a macro, or maybe forcing a panic if an appearance call returns None
while hot reloading is disabled.
Well, I have a proof of concept working! The iced UI updates immediately when saving the stylesheet. (Note the red text)
Usage in this case is:
.color(stylesheet.color(&stylesheet.text_color_h1))
I can change the color in this example by either changing the rgb values of the named "text"
color, or by changing text_color_h1
to a completely different named color such as "primary"
. I chose this example because it relies on an extra layer of abstraction: colors can be added and referred to by name dynamically. Here's an example of adding a new named color and changing the reference to it:
I'm looking forward to being able to tweak the UI without recompiling. π
@aevyrie If you don't mind, would you please provide some implementation details or show us your code?
@shujaatak Of course. Please excuse the state of the code, it's very much in flux. https://github.com/aevyrie/tolstack/blob/master/src/ui/style.rs
I'm not super happy with the verbosity required for a stylesheet - there is the Rust boilerplate of defining the new field in the stylesheet struct, then instantiating a value in the instance of the struct. As far as how the system works, there are NamedXYZ
structs for each type of styled object, like a button or container, that implement the Named
trait. This trait takes in the string of the style class you've defined in your stylesheet (e.e a named color such as "primary"), and the list to use as a style lookup. The string name is then resolved to generate an Iced style using the lookup, and panics if the style class name provided is invalid.
This is really just a prototype that evolved as I started using it to build out the application. I'm sure this concept could be fleshed out in a more robust way if there is interest in doing so.
https://fasterthanli.me/articles/so-you-want-to-live-reload-rust maybe related? We could even hot reload the whole application.
End result https://www.youtube.com/watch?v=o1iqV5k6-QU
@pickfire I think a dynamic library approach would still run into the problem that there is a compile step in the middle of your style/layout tweaking loop. It seems likely to be too slow.
But compiler could guarantee there is no issue with the data type, say if we use json there is no validation or whatsoever, at least when it compiles it usually works. IIRC there is something to speed this up, I think one can change value in rust code and reflected directly, I saw a crate for this but I forgot which is it.
@pickfire You are probably thinking of the dymod crate. I've tried the dymod method (expirement here), it has the following issues.
Renderer
implementation in the dymod. this means you already have to relink wgpu
and its dependencies, on my macbook that takes 9 seconds. solutions to both these problems are burdensome. problem 1 might require you to turn your appstate into a heavily encapsulated trait object to avoid exposing dependencies to the dymod. Problem 2 could be solved a number of ways, but not without reorganizing Iced's trait structure.
reasons to strongly prefer the dymod:
It seems like you should use the dymod method if you want to avoid registering widgets in some kind of interpreter beforehand like Qml.
Oh hot reloading is so needed. Hope it becomes a reality for iced.
What about making use of something like https://github.com/rhaiscript/rhai? It seems that it can be integrated with iced quite easily, even better is it allows syntax extensions so in the future it could even be possible to integrate iced specific syntax. Considering making a prototype that integrates with iced.
Alright, so I played with the idea a little bit and I think it should be possible to integrate iced with Rhai, but it will require changes in iced itself.
Problem 1. Rhai requires that types that will be registered implement Clone
, which almost all of iced's widgets don't. Most importantly, Element
doesn't.
Problem 2. Potential lifetime issues (?), but I didn't dig into that much (couldn't use any widgets anyways, problem 1). (This seems like it can be "just" fixed by making use of 'static lifetimes)
Problem 1 could be fixed (?) by using It looks like this can only really be solved by making Rc<RefCell<>>
wrappers. But this has the problem that we have to redefine each and every method again, to take Rc<RefCell<>>
wrapped version of the widget instead. This could be done with a proc macro, but it's still effort.Element
and other widgets cloneable.
I pushed a little "demo" with Text
hot reloading to https://github.com/yusdacra/iced_rhai, and what follows is a video of it.
I was able to (hackily) implement Rc wrappers. I have pushed it to the repository, below is a sample with Row
, Column
and Container
.
Another way this could be implemented without unsafe
code is, we would practically "copy" all of the widgets' "data" part, create methods, derive Clone etc. This would probably take more time (?) to do though.
We could also implement Clone
for the iced_native Widget
trait. I tried doing this with https://lib.rs/crates/dyn-clonable, it complained about Renderer
and Message
generics not being Clone
. Even if we could get Message
to require Clone
, Renderer
is too much for that.
Avoiding markup is one of the core goals of elm. Both Elm and Rust are less verbose than XML. It is in fact one of the selling arguments of elm.
If your only concern is the compile times, have a look at bevy. I think they use dynamic linking (dll) for development, which results in insanely fast compile times, and only use static linking for release builds. Here's a link, scroll down a few paragraphs. Maybe this could be explored with iced too, regardless of hot-reloading? This way, only iced would be pre-compiled once, and only the user code is compiled in every iteration.
Using markup/templates removes the dynamic behavior. Approximating dynamic behavior using yet another alien syntax for loops and branching sounds like a lot of effort without much value to me. Furthermore, using a template language adds a completely new barrier. And then it has to be thrown away once you realize that you need actual dynamic behavior, and you'll need to reimplement it in Rust.
Using rhai or some other real dynamic language seems much more reasonable to me. However, at that point, it would probably never be favorable to actually use iced with Rust directly. You'd most probably write everything in rhai instead of dealing with transferring objects from one language to another, which further can introduce subtle bugs. Is that the route you want iced to go? If not, rhaiced should perhaps be an independent project.
I personally choose to use Rust because of the powerful type system, the package manager, and the performance. At least two of these three selling points are lost when you use a dynamic scripting language. If all you desire is fast compile times, supporting dynamic linking for Rust development might be the way to go. Otherwise, once the user realizes that rhai performance (although neat) can not achieve compiled speed, they can't simply recompile with a different flag, but they will have to rewrite core logic in Rust. And deal with transferring objects across the language border.
If Rust is still too verbose compared to rhai, perhaps we should improve the Rust API instead of using a completely different language. I'm very pleased with the current iced design, but with enough love we could probably make it a little more concise.
My point is: let's not forget that there's a lot we can improve in Rust. Conciseness and compile speed are issues that can be solved that way. We don't have to add an opinionated secondary language to tackle these issues. I assume most of use actually like Rust, we choose it for a reason, right?
some sort of markup language for iced would be awesome. to keep the crates lightweight this could be in a sparate crate like: iced_markup. like iced_audio for audio related widgets.
- Markup/templating language: Something like XAML, JSX, Handlebars + HTML, etc. I think this is a very useful component for
iced
to have, regardless of hot swap capabilities.
A small request - please don't make it obligatory. My takeaway from QML/Qt and Gtk's XML templates was that it made me learn another language to do the same things I could do with the one I already knew. Especially Gtk's XML was hard to accept (also not documented well) and I happen to know XML/XSLT very well. In Qt's case, it split the ecosystem in two, with a significant group of people being angry that Widgets are no longer developed.
This is an interesting project which would provide hot reloading capabilities without any change to Iced: https://robert.kra.hn/posts/hot-reloading-rust/ https://crates.io/crates/hot-lib-reloader https://github.com/rksm/hot-lib-reloader-rs
Unfortunately I cannot make the hot-iced example work, it keeps crashing at every lib reload, I opened an issue with some sample Iced code.
I realize I'm the first to comment in a while, but I'd like to add my thoughts to the conversation.
I believe some sort of external language for creating the view is a good idea. Rust is an amazing language, but many of the things that make it amazing also make it very inconvenient to create UIs with. It excels at creating logic that is dependable, robust, and performant. Contrast this to UIs, where iteration and development speed are important. I suspect part of the reason there hasn't been a clear winner yet in the Rust GUI library space is because many of the language's philosophies do not bode well with GUI app development. Having the view logic built in a separate language can help mitigate this. The update logic should still be written in Rust since this is where it would be great, but the view field where no major calculations should be done can spare the maximum performance.
While I agree with the point in another comment that markup languages should not be forced or mandatory, I believe there is some merit in having an external crate providing this functionality to Iced. Many of the cases where markup languages split a community (cough QML cough) were because they were presented as the only alternative, and other parts of the library suffered because of it. If this functionality is provided as a separate crate and development on the regular Iced library continued as normal, it would give users a choice on what to do based on the requirements and tradeoffs of their application. Keep in mind that not all GUI applications (especially the view logic) need top-tier performance. The fact that Electron is as popular as it is shows this. Apps that do need this performance can use pure Rust with Iced or roll their own GUI. This would be a solution for apps designed for weak and/or embedded systems.
There are quite a few options for a markup language like this. There are the Rust scripting languages that have come up already in this thread. There are many interpreted ones like Rhai or Rune that provide near instant feedback on changes. Those could work, and they're extendable enough to suit Iced's needs. However, there is a bit of a performance penalty. For most apps it won't make a difference, but it is true that it can be difficult to migrate off of them. Still, it would be used only for the view logic, not for many of the things that would be written in Rust for the benefits it provides. An option could be to build a transpiler for one of these languages to Rust. That could alleviate some of the performance issues. If we stick it in a procedural macro or another way that it isn't to be intended to be edited by the user, it doesn't matter how messy it is as long as it compiles. Then, LLVM will do it's magic to give near native performance. We could also make a dedicated markup language for Iced, but that is difficult and wouldn't really give many more benefits than just using an existing one. Another option could be to build a RAD tool to build UIs visually. The file it spits out doesn't necessarily have to be human-readable. In fact, it could just be a serde serialization of Iced types, which if done right can make it trivial to update and add new widgets. A drawback to this would be that these tools tend to not scale well, and there's even less of an option to convert it to code. For small apps though, this could a realistic option.
One point that is being left out in this is ease of development. All of us know how Rust works and can use it effectively. However, there is a huge point to be made that integrating a simpler language such as Rhai can help people who do not know Rust use Iced. For instance, someone more skilled in UI design can build the views with a scripting language, while someone seasoned in Rust could build the fancy logic. This ease of development could be a massive selling point for the library. An well-designed interface will probably have to be made for one of these languages to facilitate passing State and Messages between Rust and the scripting language. I feel that this will be one of the biggest pain points for this, just as it is in passing info into shaders in graphics programming. I'm not sure the best way to tackle this, but I feel a good combination of metaprogramming and APIs could make it intuitive to pass this data around. This would likely be the problem of a 3rd party crate rather than Iced directly, but it's worth considering, especially if it ends up making some small tweaks to how data is treated in Iced.
Some sort of rapid iteration tool, whether it be a scripting language or a RAD tool, can make development with Iced faster and more accessible. Thank you for your time in reading through this.
I choose iced because the view is in rust, the great part you can build variable lists with iterators and you can do pattern matching for showing or not showing widgets. I think you should look at slint, they seem to do it the way you like. I when using a scripting language rust have to include every feature with could be possible to use in the scripting language in the binary.
There are many things I dislike about Slint, such as its lack of custom widgets and its pricing model. The great thing about these scripting languages is that they interop very well with Rust, which can allow doing the pattern matching stuff in Rust, then doing other things in the scripting language where you can quickly tweak and play around with them. Perhaps this would be best as an external library, so people have the choice of which paradigm they would like to use.
While there's definitely merit for defining ui declaratively in external ui files, I think this is simply a non-goal for iced (at least not as the main way, you can build systems like that on top of iced, much like the bevy has with scripting).
I think the most tangible way in the short term would be trying to improve compile times, and pointing users to ways to improve compile times, the bevy setup guide is an excellent resource, in particular https://github.com/rust-lang/rustc_codegen_cranelift would be very interesting to test, and also changing the linker to mold
(or lld
on windows) but I assume people are more aware of that. Also dynamic linking #849 would be really nice to have, it seems to really help in bevy.
In the long term, it'll be interesting to explore other approaches:
rust -> mir
part, even if it only supported a limited subset of rust would probably still be a huge amount of work.view
s, parsing code to try to recreate the view would be a lossy process but might be good enough for simple usecases (to be useful it would have to preserve code it doesn't care about or understand, sort of like toml_edit
does with whitespace and comments).Good points. For the Iced GUI editor, perhaps something similar to GTK's GTKBuilder could be implemented. As I understand it, GTK will spit out the code from an XML file directly with its properties, but you can go in with C code and change certain properties programmatically as needed. This opens up the possibility of having tools like Glade and Cambalache using completely visual interfaces, then modifying those widgets programmatically if there's something that has to be dynamically set within the program. The exact implementation wouldn't work very well with Iced, but perhaps some sort of variant could be explored. One issue is that you address these in GTK by ID, and Iced doesn't really have a widget ID system. Maybe an optional one could be added, though set to None
by default to preserve compatibility?
I think iced does have ids, I'm not exactly sure about the difference between id types or how they are used as I haven't used iced in a while, they are important for accessibility and other things like managing focus.
GtkBuilder
works by reading loading the ui files at runtime, which is worst than Slint, but even with a system to do this at compile time, again I don't think this belongs in iced, this goes right back to the ecosystem split problem (splits development resources, documentation, user forum, etc.), when I was suggesting a gui editor I meant one that operates on rust code directly.
I don't think this discussion matters because I doubt @hecrj would be interested in implementing something like this, in addition to this being too late for such a change (to do this well it would require a lot of effort and probably rewriting large parts of the library), he's against integrating fluent because he sees it as an unnecessary DSL, and that is a much smaller but similar change and it has a better motivation, since it's meant to also be used by non-coders. (Also I'm not saying this as a negative thing towards hecrj, he has a clear vision for the scope and identity of the library and that is important to have, I also generally agree with the direction of iced, including the lack of external ui files, there's a reason modern ui frameworks are moving away from them, e.g. jsx and other web frameworks, flutter, swiftui, etc.)
Ok, I wasn't aware of this general trend among GUI libraries. Perhaps something of a live preview could be developed that reads the view function and renders out the widgets, but that is quite the project.
I actually had this exact idea but it very quickly died down once I realised vscode doesn't allow for embedding images or html inside the editor (actually just checked and apparently there has been some recent progress on that recently), although I'm mainly using Zed now and it also lacks most extension capabilities currently.
I have done some more research, and I'd like to share what I've learned about that.
With the way views in Iced are currently done, it is not feasible to simply match for the strings of various widgets and render them, as I was originally thinking. This is because it is commonplace to have a lot of conditionals determining what is shown. An app like that would have to pretty much interpret the code with as much of Rust's functionality as possible. Not impossible, but a tremendous amount of work.
I have looked at the possibility of creating a macro that takes a block of Rust code and interprets it using syn
. This could work in theory, but it will likely be slow and involve a massive amount of effort. Another option to consider is using miri
. This was suggested by @bbb651, and it could be an option. It's likely the view logic would have to be in a separate crate to avoid compiling the whole project on reload. It also appears that miri
does not currently support linking a hot-swappable interpreted module with a regular compiled module. We could add that functionality either to the base repo or fork it. However, this would also be a lot of work, I suspect it will be buggy, and it may not even have the performance to justify using it even in development.
It is seeming a hot-swappable Rust module is the best option, as this thread was come to many times. There isn't currently a working prototype of it on all platforms. I will have some spare time so I can try. Another thing we can consider is implementing a way to change function parameters at runtime. This wouldn't allow changing logic or adding/removing widgets, but would allow playing with colours, spacing, text, etc. at runtime. I feel this could be a good benefit. I'm not 100% sure about how to implement it, but I feel this may not be the trickiest thing in the world. There was some promising work with external RON files to this effect earlier in the thread, and it would be useful if there could be similar functionality implemented in pure Rust.
@breynard0 please see my post above, and in particular this issue. Unfortunately there does not seem to be much activity behind hot-lib-reloader
lately, that's a pity because it looks very promising to me.
Maybe can use the inline_tweak library for hot reloading in specific's layouts, like hotlayout!(...) ?
@garcia-andy Nice find! I had some time to throw together a basic example. My attempt is in this repository.
A few issues I came across:
tweak!
macro uses std::column
internally, which clashes with Iced's column macro and throws an error. This is easily solved by simply adding a use
statement for this. The derive macro doesn't seem to cause this issue. I feel this won't be that hard of an issue to fix, we can make a PR to inline_tweak
's repository.iced
that can work too, maybe others have some ideas.Video of my implementation:
https://github.com/user-attachments/assets/f876c40e-6309-4d0c-9f7e-06ae008c62b6
Other than the hiccups I mentioned, I found that inline_tweak
worked quite well with Iced, which is great.
@garcia-andy Nice find! I had some time to throw together a basic example. My attempt is in this repository.
A few issues I came across:
- The
tweak!
macro usesstd::column
internally, which clashes with Iced's column macro and throws an error. This is easily solved by simply adding ause
statement for this. The derive macro doesn't seem to cause this issue. I feel this won't be that hard of an issue to fix, we can make a PR toinline_tweak
's repository.- Only literals are supported, though a fair amount of Iced configuration is in the form of enums. We could perhaps open a PR for this as well, though I'm not sure how hard it will be to implement.
- When a change is made, it does not take effect until the UI is interacted with since Iced is retained mode. This could end up being annoying, I'm not sure if there's a fix for this other than just clicking a button in the app or something. There might be something else within
iced
that can work too, maybe others have some ideas.Video of my implementation:
iced_inline_tweak.mp4 Other than the hiccups I mentioned, I found that
inline_tweak
worked quite well with Iced, which is great.
I had an idea. On debug mode, serialize the state structs to the tmp
directory, and try automatically load that on startup. Something like:
#[derive(Serialize)]
struct AppState;
iced::application("Iced app", update, view)
.persistence(std::path::Path::new(r#"/var/tmp/my-iced-app.state"#))
Also, use an external app with a Wayland compositor like Casilda, to keep the window open:
$ export GDK_BACKEND=wayland
$ export WAYLAND_DISPLAY=/tmp/casilda-example.sock
$ cargo run
The Cambalache app uses wlroots for this. For X11 / others, Iced could render to a local server and the viewer app connect to it (probably more complex than this).
The build time can be reduced with LLD (rust-lang/rust#39915) / mold and some precompilation like Bevy uses.
I had an idea. On debug mode, serialize the state structs to the
tmp
directory, and try automatically load that on startup. Something like:#[derive(Serialize)] struct AppState; iced::application("Iced app", update, view) .persistence(std::path::Path::new(r#"/var/tmp/my-iced-app.state"#))
Also, use an external app with a Wayland compositor like Casilda, to keep the window open:
$ export GDK_BACKEND=wayland $ export WAYLAND_DISPLAY=/tmp/casilda-example.sock $ cargo run
The Cambalache app uses wlroots for this. For X11 / others, Iced could render to a local server and the viewer app connect to it (probably more complex than this).
The build time can be reduced with LLD (rust-lang/rust#39915) / mold and some precompilation like Bevy uses.
That may work in Linux environments, but they would be forced to implement a different solution on MacOS and Windows. Cambalache and the like may focus only on GTK environments, but iced is maintained for all systems. The biggest problem is maintaining compatibility with different systems, while trying to have as little boilerplate code as possible. Likewise, any example code that works can be useful for future ideas, maybe using the principle of those ideas can lead to something.
For my part, I continue experimenting with dynamic linking and changing constant values ββwith inline_tweak to increase development speed a little, but there is still a lot to polish. Maybe in 0.14 or 0.15 we will see some progress on this.
I feel it's not a bad idea to implement feature-locked serde traits on Iced's types, though. If a Windows and MacOS solution can be found, the above idea could be used. Perhaps stored in an environment variable? A quick Google search tells me that Windows has a limit of around 32kb for them, but maybe there's something else like the Windows registry or something. Not an expert with Windows, maybe someone else has an idea of where you store them. Implementing the serde traits also opens the door for things like 3rd party markup languages and GUI builders. I know that's not the direction that Iced wants to go, but giving a feature-locked option for someone else to make something can't hurt.
I feel it's not a bad idea to implement feature-locked serde traits on Iced's types, though. If a Windows and MacOS solution can be found, the above idea could be used. Perhaps stored in an environment variable? A quick Google search tells me that Windows has a limit of around 32kb for them, but maybe there's something else like the Windows registry or something. Not an expert with Windows, maybe someone else has an idea of where you store them. Implementing the serde traits also opens the door for things like 3rd party markup languages and GUI builders. I know that's not the direction that Iced wants to go, but giving a feature-locked option for someone else to make something can't hurt.
Windows has a tmp
directory too. See std::env::temp_dir
Like discussed on Discord - perhaps it's nice to provide some sort of "quick iteration" possibility, for example using Lua.