-
Notifications
You must be signed in to change notification settings - Fork 568
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Add a "resource bundle" for files like images, fonts, and localization #397
Comments
I do agree that we should have something like this. Here are a few more thoughts. This intersects deeply with platform mechanisms for bundling resources. On mac, while we're currently generating a single binary as the result of "cargo build" (and I am very reluctant to break this use case), the real way to ship applications is through an app bundle. Similarly, on Windows it's possible to bundle resources into the .exe file, and there are ways to get at that. I'm not sure if it's still recommended to bundle all resources into the exe, and certainly bigger apps are split into multiple files (and there's a whole nother mechanism for distributing that, either msi or something else). In both cases, the app bundle has a lot of other important stuff (icons, manifest, digital signatures). So there's a ton of complexity here. I'm less familiar with the situation on Linux. It seems like there are a lot of choices; it's certainly possible to design a mechanism that feeds reasonably cleanly into traditional distribution packagers such as apt and rpm. But possibly it's better to target flatpak and/or snap. I'd certainly want input from people in the Linux community on this. As I said above, I don't want to break the "cargo run" experience, so building such a bundle should be opt-in, I think. Ideally we'd set this up so that it's still possible to "cargo run" for development purposes, and it would do a reasonably good job of finding the resources. |
electron-packager might be a good overview of the possible app bundle outputs |
You can bundle binary data inside executables on all three platforms. I remember seeing support for this in some older languages but I dont think Rust supports this. Maybe through some obscure linker magic? |
@mendelt rust does have ways to do that, there are the |
That said, idiomatic applications generally don't bake everything into the executable, and on macOS and Linux there are strong reasons to have external files, e.g. so the OS can find your icon. |
yea, macOS has a well described bundle format, and we should be building this when we build an apple bundle on mac, which I want to be as easy as |
@ratmice Thank you. Learned something today. |
@mendelt I think of |
@cmyr An application packager like that sounds like a very useful thing to have. I just wonder how much of it should be specific to Druid. |
With the renewed interest in this with the looking ahead to |
I use Flatpak pretty much for every app on my laptop and think it offers many advantages, so I would like to put it on the list of package formats to support. |
I know next to nothing about these, but being young and not widely used aren't super convincing arguments in this context. They certainly have some weight, but the whole existing dependency tree of druid has those same two properties. Given that we're designing druid for the future, we shouldn't be too afraid to use fresh solutions if they make sense. Are they too buggy? Do they lack critical features? Do they restrict application behavior or performance too much? Are the developers egomaniacs? - These are the kinds of questions I would ask. As I noted in #1047 I'm also a bit confused about the exact scope here. I think this issue here is only for collecting all the files needed for the app and not about package management, given that is a subset of #1047. |
I should clarify that I'm not arguing for not having Flatpak and Snaps as officially supported packaging solutions for druid applications. Instead I'm arguing against focusing our efforts on them early on. They are not some dependency, they are software distribution solutions so how widely used they are is important for how early we push to have solid support for them. I was responding to Raph's question Flatpak and Snap do currently have some issues associated with them (such as integration issues, update/package sizes, Snap's forced auto-updates, ect) but that is out of scope for druid's eventual support of them. I agree that this probably is better to be discussed under #1047. However I wanted to give some late feedback to Raph's question in this thread. |
I think we should keep them at least in mind, because Flatpak and Snap with their sandboxing, permission system and strict guidelines are a bit of a different beast than traditional packages.
Some examples that would come to my mind: Most (new) Gnome apps advertise their Flatpak, Gnome is building an entire distribution around Flatpak and elementaryOS is moving their store to it. Snap is mostly being pushed down Ubuntu users throats by only making snaps easy to install (no terminal required).
Best example that comes to my mind is that you can't open a directory through a dialog in a sandboxed Flatpak app. So regarding Raph's question: I agree with @ForLoveOfCats that we should have good support for disto package formats such as Also note that Debian for example only packages an older version of Rust which we don't even support, not sure if they would even publish an app in their stable release that would have to statically link its own version of Rust, but I might be wrong about this as I'm not particularly up-to-date about Debian. |
Resource BundlesResource bundles are a mechanism for accessing various resources (such as images, sounds, localization files, or fonts) that are used by your druid application. Importantly, this is not about creating packaged application bundles for distribution; that is a separate (though related) topic. This means that things like the app icon, manifest, or other platform-specific assets are out of scope. GoalsThe first main goal is to have a platform-agnostic API, in Additionally, in The basic idea is that we can know exactly what images or icons are included with your app, and at compile time we can ensure that those assets exist and can be read, and then at runtime we can provide an interface that lets you retrieve those assets in an ergonomic, type-safe way. druid-shell
As currently imagined, this API should be a thin abstraction over the file-system. At the most simple, it might look something like, trait Bundle {
fn resource_for_key(&self, key: &str) -> Result<Vec<u8>, BundleError>;
} Where This is a dumb API. All it does is allow the user to retreive files from some location on each platform. It doesn't put the files there, it doesn't validate them, it doesn't really do anything but take some key and look for it in some location. nesting and keypathsThe simplest possible version of this would involve saving everything in a single namespace/folder, but it's possible we will want to support nested collections/folders. In this world, first step / resources during developmentAs a first step (before we do too much work on creating app packages) it may make sense to have an initial implementation of this bundling idea that is just backed by a folder in druid (and druid-bundler)?All the fun stuff happens in druid. The main idea is to have some abstraction that lets the user retrieve assets in a type-safe, failure proof way. validating and setting up the bundleWhen you build your druid app (with In addition, we will have some mapping from the files in your druid project folder to the keys in your resource bundle. This is a detail to be determined, but it might involve things like appending a locale where appropriate. codegenOverviewThe most interesting part of all of this, design-wise, is using code generation to provide a nice typed API at runtime. At a high level, we will read your resources and write out a number of Rust source files that describe them. This will all be validated when the bundle is created, and so there will be a strong guarantee that the named resources exist, are of the correct type, and can be loaded. The exact structure of the generated code is tbd, but my current inclination is that we will generate a crate, and then expose various modules on that crate. This will let all the generated code live in one place, and be managed entirely by the bundling system; the alternative would be to generate certain specific files inside the user's crate, which poses challenges. A major motivation for the final design will be coming up with something that works with tools like rust-analyzer; we want the user to be able to reference their resources easily during development. In terms of what code exactly we generate, I currently imagine two separate things; generation of the definitions of Codegen for localized stringsWe will generate a different Codegen for other resourcesFor things like images, sounds, and fonts, we will use a mechanism similar to how druid's [ As an example: if you have an image "#-button.png", we will create a key like, const #_BUTTON: Key<Image> = Key::new("#-button.png"); and then at runtime you can retrieve the asset in question via the In the example above, ideally the thing returned is an actual This mechanism will support common asset types, and we will write the loading code for these. If the user wishes to store data of an unknown type, this will be possible as well, and they will either have to deal with the bytes themselves, or they will be able to provide a deserialization function. This might involve using a trait and having keys of different types; the exact details will be figured out in the work. A Sketchthe root directory of a druid project, ├── Cargo.toml
├── Druid.toml
├── src
│ ├── main.rs
# this is all generated
├── my-app-bundle
│ ├── Cargo.toml
│ ├── build.rs
│ ├── src
│ │ ├── lib.rs
│ │ ├── strings.rs The [dependencies]
druid = "0.6.0"
resources = { path = "my-app-bundle", package = "my-app-bundle" } And fetching some image asset in a paint method: fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
let image = env.get_resource(&resources::NEW_USER_ICON);
ctx.draw_image(&image, resources.NEW_USER_ICON.size, InterpolationMode::Bilinear);
// or something like that
} CachingLoaded assets will be cached; I haven't thought about this a lot except to say that I think we will have some tunable cache size (in bytes) and we will eject the last used items as we bump up against this cache. localized and DPI-aware assets ('asset group's)In some instances, you may have multiple different versions of a single asset; for instance you might include different versions of an image tailored to different DPI, or you might have different versions for different locales (for instance if you include an icon with a currency symbol, you would want to use a different symbol in japan than in australia). In this case, all of these different versions of an asset would be referred to be a common key, and Relationship with
|
I think we should try to avoid an extra crate, exactly for the reason you pointed out - it makes life on crates.io needlessly hard. It's also unclear to me what the benefit of a crate over a module is. |
I like the design and I see where you are going with the compile time assertion of asset existence. However, it precludes being able to dynamically load asses like I do in my app. I've had to do a lot of copying of core widgets and modifying them to be more dynamic (like Image). This approach seems like it would push druid further down the path of needing to create separate widgets and systems for more dynamic content. I, however, don't have a great suggestion on how to reap the benefits of the compile time assertions and still allow dynamism without making every widget handle two cases. |
Hi all, we recently finished implementing quite robust localization resource manager for Fluent - https://github.com/mozilla/l10nregistry-rs It is now used in Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1660392 It does more than most managers will need, but it ties nicely to We use the combination of l10nregistry-rs and fluent-fallback to power This is likely a good model to consider and you may implement a simpler manager (for example in case you don't want to support live hotplugging of language packs), but shaped after it. |
This came up in the #353 discussion. I'll let @cmyr explain it:
The text was updated successfully, but these errors were encountered: