[HN Gopher] Iced: A cross-platform GUI library for Rust, inspire...
___________________________________________________________________
Iced: A cross-platform GUI library for Rust, inspired by Elm
Author : ducktective
Score : 377 points
Date : 2021-08-27 10:33 UTC (12 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| no_wizard wrote:
| Any idea if mobile support is coming? That's starting to feel
| table stakes as time goes on I think, even more important than
| desktop which can always fall back to web
| ComputerGuru wrote:
| The most recent time I evaluated Iced (Aug 2020), it didn't
| support any of my Linux machines due to broken/non-existent
| OpenGL support. With the progress the wgpu renderer has made, I'm
| pleased to report the situation has improved, although on Linux
| my initial attempt landed me here:
| https://github.com/hecrj/iced/issues/1013
|
| I've had no issues with Iced on any other platforms, fwiw.
| ZenPsycho wrote:
| until this supports accessibility, please don't use this
| mftb wrote:
| I think the example in the readme is presented and worded in a
| confusing way, "Finally, we need to be able to react to any
| produced messages and change our state accordingly in our update
| logic:". Since your referencing the Elm architecture, it's 1.
| send msg, 2. update 3. view. This is presented, 1. 3. 2. The code
| looks right, but the example seems out of order and consequently
| confusing.
| skohan wrote:
| How does it work? Does it render itself, or delegate to native UI
| components?
| fosefx wrote:
| The project is kept modular. There are renderers for web [1]
| and native [2]. The project's ECOSYSTEM.md [3] is addressing
| this in more detail.
|
| [1] https://github.com/hecrj/iced/tree/master/web [2]
| https://github.com/hecrj/iced/tree/master/wgpu [3]
| https://github.com/hecrj/iced/blob/master/ECOSYSTEM.md
| qwertygnu wrote:
| I'm dislike the not-so-recent trend of
| companies/projects/libraries that co-opt common words as their
| name. "Rust" and "elm" and "iced" wouldn't normally have anything
| to do with computing, but it still feels like a small intrusion
| on our language.
| alpaca128 wrote:
| You mean like Apple, BASIC, Lisp, Explorer, Safari, Chrome,
| Android, ...? This practice has existed from the start and I
| don't see it as an intrusion into the language. We humans are
| able to consider context (which is necessary for language
| either way), and that mostly takes care of it. When someone
| says "I've got rust on my PC's power supply" or "my python
| escaped" it's still very obvious it's not the programming
| language.
|
| And an outsider might have trouble following the conversation,
| but that would happen no matter what words are used. Whether we
| say "Elm" or "C++", the target audience knows what's meant and
| others wouldn't be able to follow either way.
| jhgb wrote:
| LISP and BASIC were initially acronyms.
| ryanianian wrote:
| > it still feels like a small intrusion on our language.
|
| All languages evolve over time, despite official bodies trying
| to reject foreign influence. (IIRC the French government has a
| department that tries to limit loan words from other languages,
| but good luck with that given the internet.)
|
| So what's the actual problem with it? Only downside I see is
| google-ability and occasional conflicts when two projects use
| the same name. Both of those exist with any naming Scheme (ha!)
| and imho haven't really ended badly for projects or their
| users.
| andrekandre wrote:
| struct Counter { // The counter value value: i32,
| // The local state of the two buttons increment_button:
| button::State, decrement_button: button::State, }
| impl Counter { pub fn view(&mut self) ->
| Column<Message> { ... } }
|
| this looks like it holds state in the ui layer... wouldn't it be
| better to keep state in some separate state/model object (struct)
| and bind the ui to that instead?
|
| not doing it this way couples a lot of logic/state to the ui
| framework which means its much harder to switch ui frameworks
| later, not to mention testing the logic independently of the ui
| choice...
| andrekandre wrote:
| to whoever downvoted, instead of downvoting, maybe its better
| to make a counter argument?
|
| im asking because i hope to get some insight not a silent
| downvote
| ensiferum wrote:
| I agree with your point. In fact it's the approach I'm doing
| myself in my current UI lib (which has a very specific use
| case but the point still applies).
|
| https://github.com/ensisoft/gamestudio/blob/master/uikit/win.
| ..
| dhbradshaw wrote:
| I've been playing with the iced examples, particularly the
| styling example. The code is beautiful and the result is clean
| and responsive and fast. Love this.
| danieltanfh95 wrote:
| App developers discover game programming patterns, colorized 2021
| arsome wrote:
| Slowly they'll meet with the Electron people and eventually
| we'll reinvent Delphi and Visual Basic.
| cultofmetatron wrote:
| congrats on plugging one of the biggest holes in the rust
| ecosystem. Looks like an api I'd love working with. Id love to
| imagine an alt universe where someone builds an entire desktop
| off this the way gnome was formed from gtk.
| amelius wrote:
| Just looking at the example, I notice that you have to use Enums
| to pass messages around. E.g. "IncrementPressed" in the example.
| Wouldn't it be much easier to use closures for that? Managing
| Enums sounds like an unnecessary administrative burden.
| Dowwie wrote:
| Enums are a natural fit for event handling. If you write an api
| that can manage 50 different kinds of events, a single enum
| consisting of 50 variants would be able to facilitate that. A
| closure isn't identifiable to the request handler. It's just a
| thing that does stuff, when you're ready to call it. How do you
| know what stuff to do with it?
| amelius wrote:
| What I mean is: binding an enum to an action, which then
| sends a message containing the enum, deciphering it and
| executing code based on its value, and not forgetting to
| define the enum somewhere, when ... you could simply bind the
| closure to the action.
|
| So instead of: Button::new(&mut
| self.increment_button, Text::new("+"))
| .on_press(Message::IncrementPressed)
|
| you would write: Button::new(&mut
| self.increment_button, Text::new("+"))
| .on_press(|| MY_CODE_HERE)
|
| For comparison, in JavaScript you can write:
| button.onclick = function(){ ++counter; }
|
| That's quite a direct way of formulating what you want to
| happen. All in a single line and no Enums involved.
| WJW wrote:
| The enum of all possible events is still there, it's just
| spread over dozens of different callback properties
| belonging to several different objects. Splitting out via
| an enum to a big `handle` type function makes it possible
| to detect whether you have handled all possible events at
| compile time instead of having to raise an exception at
| runtime.
| jbverschoor wrote:
| Just require a callback to be non-null.
| verdagon wrote:
| If we had a closure, we wouldn't ever need to "handle all
| possible events", we would just do a virtual call. It's
| guaranteed to be handled.
| varajelle wrote:
| I see little value in seeing that enum of all possible
| event on one place. On the other hand, I want to see what
| is the action performed by that button and there is value
| in having small event handler close to the thing they
| handle.
| nybble41 wrote:
| The iced package requires the messages to implement the
| Debug, Clone, and Send traits, none of which are available
| for closures. I was able to implement something similar to
| your example[0] but it only supports plain function
| pointers for the callbacks. The compiler wasn't able to
| derive a sufficiently general Debug trait for the function
| pointer due to an issue with the lifetime of the argument,
| so I had to implement that myself as well.
|
| _[Edit: Ignore this next paragraph; as steveklabnik
| pointed out, this change has already been implemented and
| "Message(|c| c.value += 1)" is accepted by the latest
| stable version.]_ Incidentally, as long as there are
| situations where only function pointers can be used and not
| closures it would be _really_ nice to have some support for
| anonymous function pointers in Rust (with the fn type and
| not just the Fn trait) so that one could write e.g.
| "Message(fn |c| c.value += 1)" instead of "Message({ fn
| f(c: &mut Counter) { c.value += 1 } f })". Or just infer
| the fn type for "closures" which don't actually close over
| any variables without the need for an extra keyword.
|
| [0] https://github.com/nybble41/iced-
| counter2/blob/master/src/ma...
| steveklabnik wrote:
| I'm not at my computer but I thought non-capturing
| closures already will coerce into a fn.
| nybble41 wrote:
| It seems you're right. That didn't work the last time I
| tried it--which I admit was some time ago--but it does
| work in the latest stable version of rustc.
| steveklabnik wrote:
| Looks like support was added about four years ago, but it
| is true this wasn't the case a whole back. Glad it's
| working now though!
| jayd16 wrote:
| Pretty funny to read the arguments about how enums are
| the one true way and here we see function pointers are
| already supported.
| Jweb_Guru wrote:
| Bare function pointers are, but not state-capturing ones
| (see my other comment for why that is). Without state
| capture, closures really aren't more ergonomic to use
| than enums in most cases, and are often less so.
| Jweb_Guru wrote:
| No one has addressed the actual issue here, unfortunately.
| There's a good reason why you'll often find message passing
| done with enums in Rust where other languages use closures, and
| it's not ideological.
|
| In Rust, if you want to access shared state, you need to hold
| onto a reference to it. If a reference captured by a closure is
| unique, the compiler conservatively prevents you from not only
| accessing that variable, but _anything that could transitively
| lead to it_ , and if it's shared, the compiler prevents you
| from _mutating_ anything that could lead to it. Additionally,
| you can 't move or destroy the object until there's no chance
| _any_ value transitively referenced through it is still
| borrowed--which across threads, often means pretty much _no_
| borrow length will suffice. In a UI framework sending stuff
| across threads, that 's a totally unacceptable state of
| affairs. By contrast, enums that declaratively specify what you
| want to modify have no such issues, because the state only
| needs to be accessed when you actually want to run the event.
|
| There are three ways to work around the lifetime part, but
| they're not ideal. The most common is to use something like Rc
| or Arc, which allow reference counting (a primitive form of
| garbage collection). Reference counting _always_ makes your
| objects shared, so it guarantees you 'll have to tackle the
| mutation problem somehow, which can be unsatisfying; it also
| has a runtime cost (which can be quite significant) and can
| cause memory leaks if you have cycles in your data (though this
| is rarely an issue in UI frameworks). Secondly, you can
| architect your application around things like arenas, which
| give you "scoped" access to lifetimes--these can be really
| efficient with the right use case, especially pointer-spaghetti
| graphs, but can restrict a lot of the patterns you'd write in
| Rust, since the arenas have to outlive _everything_ you want to
| do with the objects. Finally, you can separate your shared data
| from the closure--which works great but now you are restricted
| to "bare" functions or functions that only capture unrelated
| state. The closure can't specify the state it wants to update,
| it has to be directed to the object--which is much less ideal
| (since now the closures aren't universal in any way, as they
| need to be passed in the state they want to modify).
|
| You can work around the mutation part in two ways, but neither
| is that great. One is, again, to ban closures from capturing
| any environment state, so the data is still stored separately
| and has to be passed in, which has the limitations mentioned
| previously.
|
| The other is to use "interior mutability" which uses some
| combination of runtime checks and other restrictions to
| guarantee that you're only accessing the element one at a time.
| Almost invariably, these mechanisms are slower at runtime,
| restrict your types somehow (e.g. making them non thread safe),
| can cause your program to panic if you misuse them, or are just
| plain annoying to use (or some combination of all of these). So
| when you can avoid this option, you do!
|
| So basically: enums make a ton of sense in Rust because they
| totally avoid these borrow checker issues. Your intuitions
| about using closures for this stuff in functional or garbage
| collected languages don't apply here--patterns that make a lot
| of sense in them don't work at all in Rust. That's one big
| reason there are so many Rust UI frameworks rather than just
| wrapping the C ones or using the same patterns you see in
| functional languages--the usual patterns simply don't work
| well.
|
| That's not say there aren't some upsides to the approach from a
| reasoning standpoint. A big one is that it enforces "single
| writer" control flow--just one place is responsible for all the
| state updates, so you don't have to worry about synchronization
| weirdness due to lots of places updating the state on their
| own. IME, this helps a lot with reasoning. But not everyone
| agrees that that outweighs the convenience of being able to use
| a callback, which I totally get--it's just that the "convenient
| way" isn't really convenient in Rust at all. And it's this that
| causes all these libraries to be designed this way (at least
| that's been my experience), not theoretical concerns about
| serialization and stuff which I agree is basically a nonissue
| in practice.
| verdagon wrote:
| Thank you for this thorough and balanced explanation! This is
| probably why all the GUI frameworks in Rust (that I've seen)
| require message passing like this. You have a really good
| handle on the borrow checker's benefits and limitations.
|
| I'm trying to make a language (https://vale.dev/) that
| addresses the borrow checker's limitations (by making it
| automatic and opt-in) to handle cases like these, if you want
| to drop by our discord (https://discord.gg/SNB8yGH), I'd love
| to get your thoughts!
| jerf wrote:
| Consider the Enums simply as defunctionalized closures.
|
| https://blog.sigplan.org/2019/12/30/defunctionalization-ever...
|
| There are significant advantages to this sort of
| defunctionalization in a system like this. It is a bit more
| administratively at the point of enum definition, but the
| flexibility and simplification of the _other_ parts of the
| program will be a win for almost any non-trivial UI
| application, not to mention the future-proofing it gives you.
| amelius wrote:
| Could you give a concrete example of the benefits? I prefer
| to avoid abstract discussion since it might not apply to 99%
| of my code.
|
| And by the way, defunctionalization sounds a lot like
| transforming my code into a bytecode interpreter which
| interprets my code. This is something I don't need and which
| would make my code slower and more difficult to read.
| b3morales wrote:
| One key difference is that the defunctionalized
| representation is _serializable_. An example feature that
| enables is that you can now have a GUI for your interface
| construction, letting you preview the layout while you
| build it. The controls are hooked up to the message names,
| which can be be deserialized at runtime and used for
| dispatch. Attaching live functions, especially closures, to
| them is not really feasible.
| amelius wrote:
| Do you think that at one point Rust could automatically
| transform my code into defunctionalized form, and thus
| make it serializable?
|
| What you describe sounds like QT Creator, by the way.
| b3morales wrote:
| > Do you think that at one point Rust could automatically
| transform my code into defunctionalized form, and thus
| make it serializable?
|
| For a pure function some kind of bytecode representation
| is certainly conceivable, but as soon as you add captures
| it gets much more complex.
|
| > QT Creator
|
| Precisely, yup; or Android's Layout Editor/Apple's
| Interface Builder.
| verdagon wrote:
| You might be serializing your enums, but unless you
| serialize a lot more (button IDs, view IDs, etc), the IDs
| in your enums will be meaningless. I don't think you're
| suggesting we serialize the entire GUI?
|
| This also seems like the wrong layer to serialize.
| Normally we would want to serialize the model, not the
| view. Admittedly, the line is a bit blurry here (and for
| most small programs) so take my words with a grain of
| salt.
| b3morales wrote:
| > I don't think you're suggesting we serialize the entire
| GUI?
|
| I am, exactly -- to enable WYSWIG construction/editing of
| the GUI. Just added another comment saying this below,
| but consider Android Layout Editor/Apple Interface
| Builder (or someone else mentioned QT Creator, which I'm
| not familiar with but has the same idea).
| amelius wrote:
| You might be right, but I feel that by filling out Enum
| structures I am helping the tools, whereas the tools
| should be helping me.
|
| And as a terminal-aficionado I don't want to use
| Design/Creator tools.
|
| So ... what I really want is a simple API with closures.
| And there should be no problem with that, as the
| Design/Creator tools can simply emit more complicated
| code if they want (e.g. closures that use enums).
| verdagon wrote:
| I'm not sure this is how WYSWYG really works.
|
| There's only one MyFancyTextEditor widget definition, a
| blueprint so to speak. It's IDs refer to other things
| inside that blueprint.
|
| But at runtime, we might instantiate two different
| MyFancyTextEditors, one for view A and one for view B.
| They probably shouldn't share IDs, lest we run into
| confusion.
|
| I think WYSWIG is more like defining a class, whereas at
| runtime we'd be serializing instances.
|
| Feel free to correct me if I'm wrong, I admit I've never
| been down this line of thought!
| b3morales wrote:
| Here's an example snippet from a xib file for an actual
| iOS project of mine: <button
| opaque="NO" contentMode="scaleToFill"
| contentHorizontalAlignment="center"
| contentVerticalAlignment="center"
| buttonType="roundedRect" lineBreakMode="middleTruncation"
| translatesAutoresizingMaskIntoConstraints="NO"
| id="ykl-6F-b5r"> <rect key="frame" x="0.0"
| y="0.0" width="375" height="0.0"/> <state
| key="normal" title="Done"/> <connections>
| <action selector="doneEditingTitle"
| destination="POI-12-VR5" eventType="touchUpInside"
| id="14R-fR-990"/> </connections>
| </button>
|
| A xib is an XML description of a GUI layout. Note the
| `<connections><action ...>` element inside the `<button>`
| -- this is defining what happens when the button is
| tapped. The connection becomes active at runtime when the
| file is rehydrated into live objects. The `selector`
| string identifies the name of the method to be called.
| (This exact mechanism relies on ObjC, but the principle
| can be applied to other systems.)
| verdagon wrote:
| My point was that when we hydrate, we don't use the same
| IDs as what was in the serialized data. Otherwise, we'd
| have conflicts when we hydrate the same definition
| multiple times.
| b3morales wrote:
| I'm sorry, I'm not following your point -- what are the
| IDs used for, in your example?
|
| (For the XIB system, they have no purpose at runtime;
| they're solely for the archive. Objects get their own
| identity as usual when they're instantiated.)
| verdagon wrote:
| My bad, I didn't really explain that part.
|
| In practice, when we receive a GUI event, we then modify
| something in our surrounding environment, usually some
| model, controller, or other view. To interact with any of
| those in Rust, we need to refer to it by some sort of ID.
|
| This is the same ID you're referring to, which objects
| get when they're instantiated, at least for views (models
| and controllers likely do something similar).
|
| My central question is: why would we want to serialize
| those IDs?
|
| Also, WYSWYG might not be the best example, as it's a GUI
| that produces another GUI, hence some confusion. If you
| think about this in terms of a simple app that maintains
| a Customer database, you'll see what I mean; we wouldn't
| ever want to serialize a button click event there,
| especially since the hydrated IDs aren't stable and we
| don't know what they refer to unless we serialize the
| entire app UI state.
| b3morales wrote:
| Maybe I'm missing something particular about Rust? When
| the archive is loaded at runtime, we don't need explicit
| IDs: we have a normal object graph, with references
| between things. The instantiated widgets have identity as
| objects in memory. The string `id="ykl-6F-b5r"` isn't
| relevant -- or used at all.
|
| You can create as many instances of `Button` as you want,
| and they are different, and their targets (the other
| widgets they message when tapped) are different because
| they were either created alongside or, if they were live
| before the archive was deserialized, a reference was made
| to them as part of the deserialization process.
| verdagon wrote:
| I think what you might be missing is that, in (safe +
| idiomatic) Rust, we actually can't have references
| between things (unless we want to make our entire GUI
| immutable, which would be a tad silly).
|
| Since we can't use references, we need to "refer" to
| other objects via IDs.
| b3morales wrote:
| Thanks, I'll have to look into how GUIs in Rust work
| then; that sounds utterly bizarre to me :)
|
| I thought you could take references to things in Rust.
| Also, in particular, I don't understand why you'd want to
| use closures -- inherently reference-y -- when everything
| else is (I guess?) a non-reference plain value.
| kortex wrote:
| > I don't think you're suggesting we serialize the entire
| GUI?
|
| Yup that's exactly what people do in some GUIs. Tons of
| GUI frameworks and systems rely on varying degrees of
| defunctionalization and serialization. Microsoft COM and
| XServer for example. I don't know exactly how granular
| they get but I believe it's quite granular in what you
| can do over RPC.
| verdagon wrote:
| I thought COM was a way to talk between
| applications/libraries, not between GUI elements?
|
| Message passing at that level is fine IMO, but between
| GUI views/controllers sounds like a bad tradeoff though.
| jerf wrote:
| The problem with closures is that they are opaque. The only
| way to interact with them is to call them. Making them the
| foundation of your system eliminates a lot of useful
| architectural patterns, such as putting filters on the
| event stream, being able to prioritize some of them based
| on certain criteria, being able to ship them between
| different servers if necessary, creating a generic logger
| that can operate on the enumeration values without having
| to have each closure log things, implementing various
| security checks (you really don't want a security system to
| be stuck only being able to run a bit of code to see if
| it's safe), just a whole lot of things that can't be done
| if all you have are closures.
|
| If you don't care about your values leaving your local OS
| process, which is a pretty common use case, there's a
| hybrid approach you can take too, which is to put closures
| inside your data structures that describe the value,
| instead of passing raw closures around. You get the
| benefits of being able to examine the values without
| executing the handler and being able to filter, decorate,
| etc. but while retaining all the advantages of being able
| to implement handlers inline. Depending on your local
| language, various slick implementations may allow this to
| be one degree or another of transparent, such as
| implementing some sort of interface/trait for all these
| values, or implementing __call__ in Python, etc.
|
| "And by the way, defunctionalization sounds a lot like
| transforming my code into a bytecode interpreter which
| interprets my code. This is something I don't need and
| which would make my code slower and more difficult to
| read."
|
| It depends on the language you're in. Most OO languages
| have some way of doing this that doesn't add any speed
| issues to speak of, it's just a slight rearrangement of
| code.
|
| I'd also say this is an issue of scale. I don't bother with
| this in small programs and may just glue everything
| together with closures, but as program size increases the
| odds that you'll want to do something that you can't do
| with closures directly increase. But the languages I tend
| to work in don't make this much of a hassle usually,
| either. I think you may be overestimating the expense.
| verdagon wrote:
| By opaque, do you mean encapsulated? Encapsulation is
| pretty important to software architecture, in that it
| enforces decoupling between components. Rust has private
| visibility for this very reason.
|
| The filtering you're talking about sounds interesting,
| but the GUI layer is (imo) _definitely_ the wrong place
| to do it. When 's the last time you wanted to filter,
| prioritize, secure, or distribute UI events?
|
| Imagine if we transformed every method call in our app
| into enums, it would be an unwieldy and unreadable mess.
| I'm not saying you're suggesting that, just highlighting
| that there are definitely some drawbacks here.
|
| So, I don't see why we're being forced to use enums in
| this case.
|
| I wonder if this a conscious decision by the Iced folks,
| and if they first tried with closures and ran into some
| trouble.
|
| Your hybrid idea is interesting! Is that doable with Rust
| in practice? I'd imagine the borrow checker might enforce
| it can't read from outside its scope.
| Skinney wrote:
| There are several benefits to using enums as opposed to
| closures:
|
| 1. All your actions are in one place. Figuring out what can
| happen as a result of a series of user interactions can be done
| without navigating through a component hierarchy.
|
| 2. The history of a user interaction is simple to serialize. In
| case of error, you can dump the user interaction history for
| this session (just a list of enum values, right?) to disk. This
| can then be submitted in bug reports for easy re-production of
| an issue.
|
| 3. It's simple(r) to create a time-travelling debugger that can
| move backwards and forwards in time, since it can simply re-
| play the series of messages to a specific point.
|
| 4. Since you've seperated state changes from the UI, writing
| unit tests that ensures that a series of user interactions
| results in a specific state is pretty easy. You just send in
| the enum values representing a specific use interaction then
| check what state you've ended up with.
| verdagon wrote:
| > 1. All your actions are in one place. Figuring out what can
| happen as a result of a series of user interactions can be
| done without navigating through a component hierarchy.
|
| I'd imagine it would be easier to just call a method on
| whatever we want to update, instead of lowering all our
| methods into enums. Why wouldn't we do that instead?
|
| Also, serializing usually isn't enough to get the benefits
| you describe. You need true determinism for that, and that's
| very difficult to achieve. If you ever e.g. query the current
| date without recording it you've just introduced
| nondeterminism.
|
| If we could really achieve deterministic replayability
| easily, then I might be okay with the drawback of turning
| methods into enums.
| Skinney wrote:
| > I'd imagine it would be easier to just call a method on
| whatever we want to update, instead of lowering all our
| methods into enums. Why wouldn't we do that instead?
|
| Easier how? The difference in lines of code is minimal.
| It's not going to make any meaningful difference in how
| much time you spend on crafting a solution.
|
| > Also, serializing usually isn't enough to get the
| benefits you describe. You need true determinism for that,
| and that's very difficult to achieve. If you ever e.g.
| query the current date without recording it you've just
| introduced nondeterminism.
|
| It requires some thought, but it isn't terribly difficult.
| Most user interactions with a gui doesn't require a side
| effect, and those that do can usually be written in a way
| that it gets represented in the enum. Even when
| nondeterminism creeps in, it rarely cancels out all of the
| benefits of having a history of user interaction. Perfect
| nedn't be the enemy of good.
| verdagon wrote:
| I'm afraid that when dealing with nondeterminism, you
| *do* need to be perfect. If even one call is
| nondeterministic, you've corrupted your entire replay.
|
| This is a common challenge with e.g. RTS games, and it
| means we have to very carefully discover and avoid
| nondeterministic functions.
|
| For example, did you know that C#'s string's hash code
| calculation is nondeterministic across runs? The same can
| be said for any floating point calculations, iterating
| Rust's/Go's default hash maps, etc.
|
| Also, IME most interactions with GUI do have side
| effects, GUI apps tend to be very stateful.
| scns wrote:
| Using Enums makes you build a Finite State Machine and
| therefore all effects/state transitions explicit and
| concentrated at one place. This enables you to see what this
| particular piece of code is able to do. Methods could obscure
| what might happen. Using inline closures would make it hard if
| not impossible to get an overview what might happen.
| jbverschoor wrote:
| Message passing is a lower level construct. It's nice in the
| way that you can have multiple subscribers, but for __most__
| application you won't need it.
|
| I mean, we're not really doing this anymore, right?
| while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0) {}
| Narishma wrote:
| What's wrong with that?
| chubs wrote:
| Worth mentioning: cryptowatch's great desktop app uses this, it's
| worth trying as an example of how efficient iced is, i like it:
| https://blog.cryptowat.ch/sponsoring-rust-gui-library-iced/
| O_H_E wrote:
| Genuine question: Does somebody understand how this compares to
| Druid https://github.com/linebender/druid
| danachow wrote:
| Well for one this seems to do it's own rendering. Druid depends
| on a native widget toolkit.
| adamnemecek wrote:
| Druid does its own rendering using piet and uses some native
| controls.
|
| Here's an example of Druid rendering a button. https://github
| .com/linebender/druid/blob/master/druid/src/wi...
| raphlinus wrote:
| I wrote a little on this last year, mostly still valid:
| https://raphlinus.github.io/rust/druid/2020/09/28/rust-2021....
|
| I think there are approximately five viable Rust toolkits:
| Iced, Makepad, sixtyfps, egui, and Druid (in no particular
| order). Any of these five could evolve to being a real
| solution, with enough investment. Of course, it's possible
| another could arise, or that I've given short shrift to one of
| the other contenders (areweguiyet has a list, but that resource
| is not super well maintained).
|
| There are some things easier to do in Iced than Druid
| (especially integration with 3D, which is something we
| currently don't do at all). On the other hand, we're pushing
| pretty hard on infrastructure: proper text handling, input
| methods, subwindows, etc. Currently, Druid relies on platform
| capabilities for 2D drawing, but my main work right now is a
| new GPU renderer. And we hope to work with 'mwcampbell on
| accessibility in the next year (integrating AccessKit).
|
| We've got a great community, but tend not to toot our horn very
| loudly, so people might not be as aware of the progress we're
| making. By contrast, sixtyfps is a commercial product (in
| addition to a GPL release) and has weekly updates.
| O_H_E wrote:
| Oh wow, did not expect an answer form Ralph himself. Great
| fan of your work on Xi.
|
| Thanks for the detailed answer. As an aching Linux user, I
| appreciate your focus on infrastructure and accessibility.
| woodruffw wrote:
| An unfortunate name clash with one of the best x86 decoders I've
| ever used, also written in Rust[1].
|
| [1]: https://github.com/icedland/iced
| benatkin wrote:
| Looks like the GUI library's crate came out first:
|
| https://crates.io/crates/iced/versions
| https://crates.io/crates/iced-x86/versions
|
| I'm not sure there's a naming conflict here. There is little
| overlap between x86 decoding and a GUI library. If _software is
| eating the world_ , then it's similar to Giant Bicycles and
| Giant Food Stores.
| woodruffw wrote:
| I didn't mean to imply priority: a clash is just a clash, not
| a sign that either project isn't entitled to the name. It's
| just an unfortunate namespace problem: language package
| managers (overwhelmingly) flatten things into a single-level
| namespace, and so we end up with things like this.
|
| > There is little overlap between x86 decoding and a GUI
| library.
|
| I work on a graphical tool that visualizes x86 binaries! It
| uses iced (the decoder), and it could very easily end up
| using iced (the GUI library).
| ucosty wrote:
| But then you'd be obliged to call it Ice Cubed
| benatkin wrote:
| The main thing I disagree with you on, is that there is a
| namespace collision because of the package manager, or that
| there is a namespace collision because both use Rust. If
| there is a namespace collision, it should be because
| they're both tools used by developers. The package manager
| issue is solved easily by adding something to the package
| name. As for the language, Rust is shaping up to be more
| like C++ than Ruby or Python, where the main community is
| more about the language and the standard library and less
| about the myriad different uses of it.
|
| I think both can keep their names long-term.
| tomcam wrote:
| Had no clue this existed. Looks like the Swiss Army knife of
| reverse engineering. Thank you.
| qw3rty01 wrote:
| This is the project that the dnSpy author moved to!
| option_greek wrote:
| Very nice. If it works on mobile too, could end up being a
| competitor to flutter.
| ecmascript wrote:
| Looks cool, how does the DOM updates work here? Is it possible to
| do whatever kind of update you want?
| clarkmoody wrote:
| There is no DOM, since this is not Web tech.
|
| GUI state mutations are triggered by messages passed around the
| system. Messages can be generated from user interactions for by
| asynchronous subscriptions (network socket, etc).
| tmccrary55 wrote:
| It sure looks like it's using the DOM in the inspector.
|
| Which is cool but a custom rendered UI would be cooler.
| clarkmoody wrote:
| Iced can compile to WASM and be run in a browser, but
| that's just a single target. There is no concept of DOM in
| Iced itself.
| ecmascript wrote:
| I get that, but how does the updates in the DOM work in
| the example? Is it possible to extend it somehow?
| tmccrary55 wrote:
| Ah I see what you mean.
| jatins wrote:
| That looks exciting! Congrats on the ship.
|
| Have you done any benchmarking on how this compares with Electron
| on things like binary size, memory usage? Those are usually most
| common complaints people have with regards to Electron so would
| be nice to see the improvements this provides.
| clarkmoody wrote:
| From my experience, compressed binary bundle is < 10 Mb and
| memory usage is < 100 Mb for a generic app. Of course memory
| usage will vary wildly between application domains. With Iced,
| it's fairly constant, since Rust offers pretty solid memory
| leak protection.
| ducktective wrote:
| Music to my ears...
| OJFord wrote:
| This looks great too, but just to point out if you're looking
| for something like Electron (but rust, or smaller or whatever)
| then Tauri is a much closer fit, using web stuff (HTML, CSS,
| JS, wasm, frameworks, whatever you want) for the UI in the same
| way.
| JohnKacz wrote:
| Thanks. I had not heard of Tauri.
|
| https://github.com/tauri-apps/tauri
| jatins wrote:
| Tauri, while an improvement on some aspects, is still a
| webapp in a shell. But it has the problem that you have to
| deal with browser specific quirks.
|
| From what I understand, this will avoid that problem by
| compiling to whatever native platform you are building for?
| Also it'd not just be a webapp in a shell.
| imbnwa wrote:
| If by browser specific quirks you mean having to deal with
| Webkit as Windows is Chromium now.
| OJFord wrote:
| Well, Tauri (or more specifically the 'ri' part:
| https://github.com/tauri-apps/wry/) uses different
| engines on different platforms, so I assume there's the
| potential for quirks.
| OJFord wrote:
| Yes, correct I think on all counts. I was just responding
| to the Electron comparison, saying if _that_ 's what you
| want, maybe look at Tauri, _because_ it 's also 'a webapp
| in a shell'.
|
| For some people in some senses, there's a big advantage to
| the 'webapp in a shell' model, because HTML/CSS is
| considered good, or WhizzBang.js is the familiar tool for
| building UIs, or whatever.
|
| I didn't mean to say it's better (or worse) than this -
| it's just quite a different (more similar to Electron)
| approach, that'll suit some and not others.
| api wrote:
| Almost everything is smaller than Electron.
| amelius wrote:
| Yeah but don't forget that Electron is literally packed with
| features. For example it can draw SVG, it can play video, it
| can do 3d stuff, do networking stuff ...
| axelroze wrote:
| Cries in InteliJ (java) and 1GB+ ram usage.
| api wrote:
| IntelliJ is a lot more than a GUI. There's a ton of
| language modeling and parsing and lookup stuff going on in
| there. It has tons and tons of features too.
|
| Electron is hundreds of megabytes for "hello world."
| thibaut_barrere wrote:
| It is pretty cool! I would love to use that for audio programming
| GUI.
| lsllc wrote:
| Previous discussion (April 2020):
|
| https://news.ycombinator.com/item?id=22766639
| z3ugma wrote:
| The more I use Elm; and the more I look at other reactive
| frontends like React+Redux and Vue, the more I love thinking in
| The Elm Architecture (https://guide.elm-lang.org/architecture/)
| aka Model-View-Update as a pattern for GUI development.
|
| To wit: Here's a way to implement it with Python and Tkinter for
| an old school reactive GUI.
|
| https://maldus512.medium.com/how-to-setup-correctly-an-appli...
| goostavos wrote:
| For Python + WX, there's also re-wx[0] (shameless self-
| promotion)!
|
| I wanted React with Elm-like architectures in WX enough that I
| got annoyed and built one ^_^
|
| [0] https://github.com/chriskiehl/re-wx
| patwoz wrote:
| Can someone explain the difference of redux to elm? I don't see
| a difference on the first look. I'm a React/Redux Developer.
| sli wrote:
| Quickly? Redux is a state container that supports Flux
| actions and Elm is a language with a companion architecture
| that's recommended. The comparison would be between Elm and
| Javascript/Typescript+React+Redux, as it provides the ability
| to implement those featuresets out of the box, including
| Messages, which is the TEA answer to Redux and Flux actions
| (more or less).
|
| Personally, I find working with Elm's state management
| features is like heaven compared to dealing with
| Redux+Typescript's intensely noisy types.
| scns wrote:
| TEA was the inspiration for Flux/Redux IIRC.
| seanwilson wrote:
| > compared to dealing with Redux+Typescript's intensely
| noisy types.
|
| Anybody have any experience on how this compares to
| ReasonML + React?
| tmountain wrote:
| They're really similar. Elm works hard to abstract away the
| JavaScript universe and create really clearly defined borders
| regarding being inisde Elm land or outside of Elm land.
| Specifically, interacting with external libs, etc. involves
| retrofitting them to work via a port, which lets side effecty
| stuff come in as a standard message without having to deviate
| from the basic design. Redux may do something similar (I
| forget), but the gist of what I'm saying is that Elm works
| really hard to adhere to its core primitives to limit
| cognitive overhead for most tasks.
| mejutoco wrote:
| Redux is to React what the 'update' function is to Elm
| architecture (first code example in the following link)
|
| https://guide.elm-lang.org/
|
| Answering your question, the difference is Elm has a more
| expressive type system than javascript, and will catch a lot
| of errors for you, with excellent error reporting from the
| compiler.
|
| The layout of the code becomes less important, because the
| compiler will catch most errors (actually recommended in elm
| guides not to split in too many files until overwhelmingly
| necessary)
| Narice wrote:
| Elm is awesome! I would really like an equivalent for
| software/game dev
| S04dKHzrKT wrote:
| The Nu game engine might interest you. There's also FuncUI
| for app dev which is F# + MVU running on Avalonia.
|
| https://github.com/bryanedds/Nu
|
| https://github.com/fsprojects/Avalonia.FuncUI
| axelroze wrote:
| The Elm Architecture is not that super novel. Actually it
| came from game development from the Components way of
| thinking. Take a look at this book. It's a classic for
| gamedev: - https://gameprogrammingpatterns.com/
| tempest_ wrote:
| The language itself seems interesting but from the outside
| looking in it appears a bit stagnant.
|
| Is the community active at all in 2021 or is it just the
| result of having basically one person working on the
| language?
| sdeframond wrote:
| I would say it is quite active, although still small.
|
| I think there is a slow shift toward community built tools
| such as lambdera. The language itself is pretty stable (and
| good, IMHO).
|
| Most of the news are available on the discourse
| discourse.elm-lang.org
| 1-more wrote:
| check out the elm slack! Very active, but that's behind a
| signup wall so that's ahrd to see from the outside.
| https://elmlang.herokuapp.com
| G4BB3R wrote:
| Elm seems stagnant because Evan is not so transparent and
| prefer work in a private branch and to release work in
| batches instead of having a release every 6 months, this
| makes people think it is dead. Elm is still being used in
| production by dozens of companies with huge codebases
| (100k~400kloc), and the community is very active, there are
| amazing projects like elm-pages, elm-review, elm-ui (from
| mdgriffith), elm-spa, elm-charts, lamdera and others.
| z3ugma wrote:
| Most of the activity happens in the (public) Elm Slack
| community, so it's not indexed in search engines and hard
| to find in StackOverflow, but it's a really effective place
| to get advice and help with a problem in a more personal
| way
| figbert wrote:
| I had my first encounter with The Elm Architecture through the
| Go Bubble Tea library
| (https://github.com/charmbracelet/bubbletea). What a
| revelation. Such a drastically different way of thinking about
| UI.
| ptx wrote:
| Hmm, I'm not so sure that Tkinter example really solves the
| problem of performing background work without blocking the UI.
| Sure, the UI thread doesn't block, but instead the UI state (as
| the user continues interacting with it) can drift out of sync
| with the model when the controller thread is busy with other
| work.
|
| The real issue in that example seems to be that the event loop
| needs to wait for both UI events and read/write readyness on
| the serial port. This could probably be handled either with
| createfilehandler[1] (although it doesn't work on Windows) or
| by dedicating a thread to handling the serial port and posting
| messages to the Tkinter event loop as suggested at the end.
|
| [1] https://docs.python.org/3/library/tkinter.html#file-
| handlers
| z3ugma wrote:
| I think your last suggestion is dead on - think about the
| browser event loop where Messages can be triggered all the
| time from clicks, hovers etc. If you have 4 threads and all
| those threads are pushing Messages then the update thread
| only has to push the state change notifications and doesn't
| have to do much blocking of its own
| conradev wrote:
| Does this use the text input stack provided by the platforms? Or
| is it parsing raw keyboard events?
|
| I only ask because if it doesn't use platform text input, that is
| a non-starter for mobile.
|
| It also would mean having to reimplement platform behavior for
| text input, like being able to hold Shift on macOS while moving
| the cursor to change text selection.
| ensiferum wrote:
| Why would a widget library need to deal with input stack
| directly? For portability it'd provide an API where the caller
| will dispatch abstract events such as mouse events and keyboard
| events and already translated character events.
| cmrdporcupine wrote:
| If you think it's easy to make an abstraction around keyboard
| events, stroll through the Chromium and Blink source tree
| some time and try to understand all the nuances.
|
| Keyboard and key event input is much harder than it first
| appears. From multiple platforms to multiple languages to
| multiple keyboard interface devices to multiple layouts, and
| you cannot assume that the operating system provides a
| workable abstraction on its own.
| ensiferum wrote:
| Not sure if that's an apples to apples comparison.
| Generally speaking a widget library should be able to
| provide and interface for taking the keyboard input. It
| doesn't need to interface with the OS for this. The caller
| does and uses for example an IME or the native OS API to
| receive the os level input. I have in fact written widget
| libraries this way. Just wondering if I didn't hit some ude
| case that would require the widget to rely on some input
| system directly.
| athrowaway3z wrote:
| Not a direct answer but a good read/overview.
|
| https://www.cmyr.net/blog/rust-gui-infra.html and the precursor
| https://www.cmyr.net/blog/gui-framework-ingredients.html
| ericls wrote:
| This is an important point for me as well. Not sure on other
| platforms, but on web, it does support composition.
| conradev wrote:
| Yes! This is because web browsers use the platform text input
| stack
|
| Browsers are actually a great example of my favorite approach
| to cross-platform UI, because they sprinkle platform-native
| widgets throughout the canvas that they render, controlled by
| a platform-agnostic programming language. Which reminds me
| that people were trying to use webrender[1] to build native
| apps in Rust.
|
| [1] https://github.com/servo/webrender
| arghwhat wrote:
| Not using appropriate text input integration is a no-go in any
| platform, be it mobile or desktop.
|
| Self-implemented keyboard input pretty much only works for the
| simplest use case in english-native countries.
| est31 wrote:
| See also: sixtyfps, which has been started by two KDE
| contributors. https://github.com/sixtyfpsui/sixtyfps
|
| https://news.ycombinator.com/item?id=26958154
|
| https://news.ycombinator.com/item?id=24919571
|
| It's great to see GUI frameworks being designed in Rust,
| countering the trend of doing everything in Electron.
| ComputerGuru wrote:
| sixtyfps is licensed under the GPL, so it's icky for anyone
| that isn't developing a GPL app and doesn't want to or can't
| shell out for the commercial license. Iced is licensed under
| the MIT license.
| infogulch wrote:
| It looks like there are actually 3 different pricing/license
| options. https://sixtyfps.io/#offering
|
| - GPLv3 - free
|
| - "Ambassador" - free commercial license (after approval), in
| exchange for marketing as being built with sixtyfps and
| authorization to use your logo and feedback
|
| - Normal commercial - paid
|
| I kinda like the addition of the "Ambassador" tier. Seems
| like a fair exchange for an in-development framework, and
| would be a good option for people that are building a new
| product and don't yet have the revenue to justify the cost
| during the prototyping phase.
| ComputerGuru wrote:
| I wasn't aware of the "Ambassador" option and that changes
| the calculus for deciding between the different frameworks
| on the basis of the license, although the opaque "after
| approval" could mean anything and tbh gives me pause.
| Thanks for enlightening me.
|
| For the record, the rust community has mostly settled
| around dual-licensing under MIT/Apache for what you can
| call "foundational" crates ("libraries"), which are both
| far more liberal than the GPL.
| riquito wrote:
| > For the record, the rust community has mostly settled
| around dual-licensing under MIT/Apache for what you can
| call "foundational" crates ("libraries")
|
| That's just your opinion, there's no such thing as a
| blessed license scheme by the community. GPL is a
| perfectly valid choice if you feel like it
| ruined wrote:
| sixtyfps is dual-license. you can contact them and negotiate
| a license for commercial use. i think this is a better scheme
| than straight mit which accommodates no demand for financial
| support to the developers.
| adamnemecek wrote:
| You should also check out the femtovg project, a 2D rendering
| API that sixtyfps relies on.
|
| https://github.com/femtovg/femtovg
|
| It's a decent starting point for trying to build your own
| toolkit.
|
| I have recently added a wgpu backend but for now it lives in my
| fork https://github.com/adamnemecek/femtovg
|
| run the demo with `cargo run --example wgpu_demo --release`.
|
| Also join the femtovg discord https://discord.gg/V69VdVu
| api wrote:
| Looks nice, but like almost all promising new UI toolkits
| accessibility is not even mentioned.
| zellyn wrote:
| I think everyone is waiting for AccessKit to be ready...
| mwcampbell wrote:
| Sorry folks, that's not my day job, and I haven't had much
| energy lately to work on it on the side.
| rauljara wrote:
| Thanks for the work you did do!
|
| It's out there, so whether you finish it, someone forks it,
| or it just serves as inspiration for another project,
| you've contributed.
| paulgb wrote:
| For those (like me) just hearing about AccessKit, I assume
| it's this: https://github.com/AccessKit/accesskit
|
| Neat! I really like the Rust ethos of creating modular cross-
| platform adopters (e.g. winit, getrandom), and from a brief
| skim this seems to fit that model.
| jamil7 wrote:
| Very cool, didn't know about AccessKit.
| slingnow wrote:
| It's almost like they want to make sure it works for the vast
| majority of people before they spend time and effort making it
| work for a much smaller segment of their userbase.
| b3morales wrote:
| Accessibility is not a bolt-on extra feature, it's core to
| being able to make a product that suits all your users.
| Everyone needs, or will need, accommodation sometimes.
|
| When you have an accident waterskiing and end up on crutches,
| you'll be glad for the accessibilty of doors that open with a
| button press. When you poke your eye hiking and need to wear
| an eyepatch for a few weeks, a screen reader might not seem
| such a niche use case.
|
| These are even kind of extreme examples: frankly, even stuff
| like dark mode that we take for granted at this point is an
| accommodation to how some people need or want to use their
| computers. Migraines are a good example of an (astonishingly)
| common condition that benefits from flexibility around having
| to stare at a screen.
|
| None of us gets younger as the days go by, either, and that
| comes with changed needs. I'm still thirty years from
| retirement and I find that the 10 point font I used to prefer
| isn't good enough anymore. Bumping up text size is a
| quotidian action for me and millions of other people.
|
| ADDITION: Ah, finally found the quote I was looking for.
| Maybe not directly relevant, but thought-provoking:
|
| > SMS texting was invented [to] figure out a way for deaf
| people to communicate with one another without speaking. ...
| Now text messages are universal.
|
| https://blog.ai-media.tv/blog/why-designing-for-
| accessibilit...
| scns wrote:
| One blind programmer has worked on the Rust compiler.
| Indirectly he is making a library like this possible. Should
| we leave him out, because he is a minority?
| ZenPsycho wrote:
| so they should tackle accessibility (20%) before mac (8%) and
| linux (2%) support
| nybble41 wrote:
| The odds are good that the fraction of this project's user
| base--developers writing GUI applications in Rust--who
| would be interested in Linux support is much more than 2%.
| ZenPsycho wrote:
| i thonk you've forgotten what the point of developing
| software is
| ginko wrote:
| People keep bringing up accessibility whenever UI toolkits are
| mentioned, but wouldn't it be simpler to just have a CLI
| interface for whatever your program is doing?
| yoz-y wrote:
| Depends, accessibility is a spectrum. Most people don't need
| a fully voice/gesture based interface but can get by with
| good contrast controls and font sizing (as in, the fonts need
| to be able to get huge). Accessibility is also being able to
| use the interface only with a keyboard, or only with a mouse
| or specialized controller with 2 buttons.
| pqb wrote:
| Funny you mention that, it reminds the post I have read few
| seconds ago: https://news.ycombinator.com/item?id=26224197
|
| I am person, who in last few days tried out to use VoiceOver
| and move responsibility on it to navigate me and read
| contents, when I could code while I had closed eyes.
| Ironically the most annoying interface was CLI, because I had
| to extend my focus on contents of the terminal. Imagine
| running docker-compose up and you are going to spammed with
| reading all logs. Next, usually I had problem to find some
| relative point that I could mark a "milestone" in my head,
| which will be further point from I will be reading.
|
| Also, not all commands are read after executing them. Such
| command is silly pwd. Everytime I write it down I had to move
| the VO cursor to read the contents. Also many characters I
| would recognize as a programmer VO reads differently. Nano
| editor was complete unusable for me. While in vim I had
| notable problem to recognize indentations characters
| (sometimes they are read and sometimes they don't) and to
| know in what mode am I actually. brew update+upgrade was a
| nightmare for me. All kind of separators like #### are read
| as "number", "number" spam. The same situation was with
| "clear" command. It was read a hundreds of "space", "space",
| "space"... I was starting to think I am going to the Moon or
| I play Portal 2 [0].
|
| I could continue writing down my disappointment about the CLI
| interface that was among my favorites in past but now after
| this week with VoiceOver I think it is terrible to be blind,
| because most of time I had to use my eyes to help myself to
| recognize where I am and what I need to click to move to next
| element. I was very surprised by the rofi-like find of
| elements that are on the screen (named as Rotor feature,
| CapsLock+U). With VO I don't any longer needed to use mouse,
| which for anybody who has problem with wrists is also a nice
| thing to consider.
|
| [0]: https://www.youtube.com/watch?v=NiaxOkyKbDI
| dagmx wrote:
| How would that work for something that needs to display an
| image or other media?
| clarkmoody wrote:
| That's right. Accessibility is one of the last things to figure
| out after all of the other tricky things: proper event handling
| for sane keyboard navigation, efficient layer clipping /
| redraw, advanced text layout / style, animations, expanded
| compatibility across platforms, etc, etc.
|
| And then accessibility brings with it the integration with
| different OSes via disparate APIs, so it will take some effort
| to offer a cross-platform API to the library user.
|
| You'll notice that Iced is not >= 1.0 yet, so you shouldn't
| expect it to be fully-baked.
| resoluteteeth wrote:
| On the one hand that's understandable, but on the other hand
| it appears to be almost 2 years old already and if people
| start to actually use it in its current state the lack of
| accessibility will become problematic.
| marcellus23 wrote:
| Accessibility seems like the kind of thing that would need to
| be baked-in from the start right? Bolting on accessibility
| after everything else has already been built seems like a
| recipe for a half-assed solution.
| clarkmoody wrote:
| Luckily large refactors are relatively painless in Rust.
___________________________________________________________________
(page generated 2021-08-27 23:00 UTC)