vlang/ui

7GUIs and beyond

dumblob opened this issue Β· 38 comments

Please implement 7GUIs (and possibly the extended proposals) as soon as possible to get deeper understanding how complicated/easy the use of this UI library will be (before the API stabilizes) and to give others a good overview.

  • Counter
  • Temperature Converter
  • Flight Booker
  • Timer
  • CRUD
  • Circle Drawer
  • Cells

Looks good, will do.

Hopefully the community can help out with some programs.

Btw. I'd highly recommend making any information (both input and output) being treated by V UI as infinite streams of samples (similar to what e.g. Concur with it's Elevator Pitch and it's ==== Now here is the secret sauce ==== paragraphs does).

You'll get live view of everything (i.e. complete interaction!) for free (even e.g. layout definition itself can be a stream allowing e.g. reacting to input stream samples saying the user is dragging a handle bar and the layout will just stream newly computed value to the drawing machinery which'll immediately accommodate).

Making streams of samples (structs) a native feature of each smallest distinguishable part of UI (e.g. text, icon, position coordinates, width & height, etc. - but NOT of whole buttons/comboboxes/etc.) greatly simplifies all UI programming.

This is btw. what Red VID dialect and Concur do. Others like immediate mode UIs and React at least move in that direction. See also Concur for ImGUI (i.e. how to use the streams concept with an immediate mode backend).


I wrote this after seeing latest commits in this repository going rather in an old inflexible direction focusing on static views instead of fully live interactive stuff.

It's all work in progress, I'll be moving it closer to Flutter/SwiftUI with its declarative style, state management, and hot reloading. And perhaps we can adopt this model as well.

@dumblob I've added a task list.

It's all work in progress, I'll be moving it closer to Flutter/SwiftUI with its declarative style, state management, and hot reloading. And perhaps we can adopt this model as well.

That's a bit pity as both state management and hot reloading wouldn't be needed at all if streams were used everywhere (you'll see it yourself when implementing e.g. the Cells task from 7GUIs - compare it then to 17 LOC solution in Red).

Declarativeness is rather about syntax and not about architecture - thus declarativeness can be added to any architecture.

You really think this code is something to wish for?

Red [] L: charset "ABCDEFGHI" D: union N: charset "123456789" charset "0" 
repeat y 9 [repeat x 9 [col: either x = 1 [#"^(2028)"][#"A" + (x - 2)]
  append p: [] set ref: (to word! rejoin [col y - 1]) make face! [size: 90x24
    type:    pick [text field] header?: (y = 1) or (x = 1)
    offset:  -20x10 + as-pair ((x - 1) * size/x + 2) ((y - 1) * size/y + 1)
    text:    form case [y = 1 [col] x = 1 [y - 1] 'else [copy ""]]
    para:    make para! [align: pick [center right] header?]
    extra:   object [name: form ref formula: old: none]
    actors:  context [on-create: on-unfocus: function [f e][f/color: none
      if rel: f/extra/old [react/unlink rel 'all]
      if #"=" = first f/extra/formula: copy text: copy f/text [parse remove text
          [any [p: L N not ["/" skip not N] insert p " " insert "/data "
          | L skip | p: some D opt [dot some D] insert p " " insert " " | skip]]
        f/text: rejoin [f/extra/name "/data: any [math/safe [" text {] "#UND"]}]
       if f/data [any [react f/extra/old: f/data do f/data]]]]
      on-focus: func [f e][f/text: any [f/extra/formula f/text] f/color: yello]
]]]] view make face! [type: 'window text: "PicoSheet" size: 840x250 pane: p]```

But I'll do the research on streams, thanks.

You really think this code is something to wish for?

Well, not the density, but the expresiveness. Regarding density I thought you'll look at the original, i.e. formatted version mentioned in the article (I didn't want to change the title of the article in the link too much, so I wrote 17 LOC).

But I'll do the research on streams, thanks.

Thanks a lot. That makes me calmer as even if V UI would stay with the archaic approach, it'll be at least an educated and fully conscious choice πŸ˜‰.

Re: streams, if V implements Go-style channels then streams could just be infinite loops grabbing values from one source and throwing them into a channel πŸ˜ƒ .

I currently raised similar request/question on "streams/reactiveness" at one of the devs behind the pure Go UI lib Fyne and got a prompt answer they're currently implementing it. It's worth looking at as they took the simplest approach, which is very performant, still more or less easy to use and solves the problem rather idiomatically. Fyne supports both "using functions" and "using structs" approaches to building interfaces (the latter being closer to declarative approach and the former being closer to immediate mode though still being retained state).

See also my suggestions how to improve the programmer's experience which might not apply to Go (as Go is limited in different ways), but might apply to V as V has e.g. templates.

Let's see whether Fyne's "reactiveness" will apply also to layouting (e.g. interactive drag & drop of new widgets from stash to the user interface as well as removing already placed widgets and putting them back to a stash). @andydotxyz thoughts?

Thanks for the reference :).

I'm not sure what you mean regarding layout - responsiveness has so many different meanings ;).
Are you asking about a GUI designer (like fyne-io/fyne#227) or something else?

@andydotxyz

I'm not sure what you mean regarding layout - responsiveness has so many different meanings ;).

True, sorry for not being clear. Let's just take the following example.

You're assigned a task to create a GUI window using just Fyne abilities which supports full layout change during runtime. This app uses about 30 different kinds of widgets (buttons, lists, combo boxes, radio buttons, sliders, tabs, multiline text input, etc.) and has about 500 widget instances in total at the start. Each widget does something meaninful (either contains other widgets or does some computation or fetching from DB etc.). The widgets have different sizes (about half of them fixed, the other half stretchy) and some initial (e.g. random) layout and nesting of widgets. The goal is to ensure the user can toggle an "editing mode" and during this mode drag & drop any widget to any place and the layout must place the drag & dropped widget at the desired place and accommodate everything around to the new placement of the widget while obeying constraints of "swallow" widgets (e.g. tabbing widget - if the dragged widget shall overlap the boundaries, then the dragged widget will first be slightly repositioned to the nearest place where there is no overlap). During "editing mode" can the user also arbitrarily remove widget instances (not just hide them, but really remove) and add new ones (by drag & drop from a special list of widgets). There is also a requirement to use dataapi for each part of the state this app needs for UI.

Now, does dataapi and Fyne support this interactive use case gracefully? I'd imagine wrapping mouse input into dataapi container and then binding it to a "metalayer" build only using dataapi and then binding this "metalayer" to all used layouting primitives (i.e. actual widget instances). But I'm not sure it's possible and supported - therefore this question πŸ˜‰.

Are you asking about a GUI designer (like fyne-io/fyne#227) or something else?

Something else - see above.

Wow, this is a really complex use-case and I cannot imagine a common one. Fyne is optimised for, as I would imagine most user interface toolkits are), running developer-defined interfaces across various devices. There is no reason that this could not be done - assuming a flexible enough layout algorithm.

I don't think this is something that the dataapi would be designed to solve. The aim of it is to have a UI that is easy to bind to, and react from, data of some sort (backend, user input etc). I can't see the common use-case to have mouse cursor or other widgets be the data source for a binding.

I can't see the common use-case to have mouse cursor or other widgets be the data source for a binding.

Understood. It's good to know in advance, that bigger GUI apps (like Office or email clients or CAD apps or Blender-like apps or...) rather shouldn't use Fayne, because e.g. dealing with "fully user-customized toolbars" is not supported out of the box (probably still possible, but difficult). All these apps support UI customization, so I consider it standard, but it's fine that Fyne won't support them (at least not for now) as Fyne isn't (yet πŸ˜‰) meant for such huge GUIs.

If you wish to consider customisable toolbars then yes, of course it could be in scope - though we will need drag and drop support first.
I imagine that many widgets will offer some sort of user preference - such as how wide a slide-in panel is, or which are the 4 most wanted tabs when an app moves to mobile but has too many tab items to display.

The example you illustrated was, in my opinion, something quite different.

The example you illustrated was, in my opinion, something quite different.

I meant my example as a generalized concept of these specific use cases like adding/removing buttons from a toolbar (by drag & drop) and then adding/removing the toolbar (again by drag & drop). But if you see any difference in the concept, feel free to elaborate - I'd like to understand your view.

I noticed you mentioned layout as an issue, so let me clarify the task and assume we already have a magic layouting function πŸ˜‰, which'll get as input a list of all widget instances, the currently drag & dropped widget instance, and the absolute coordinates where the drop happened (assuming the widget instances contain all relations to other widgets - e.g. an Excel cell content depends on content of other two cells; or an Excel cell width is determined by the width of the header cell). This function will update offset and dimension in each of the given widget struct instances as well as the nesting information (it's a tree).

What I was talking about is though all the stuff around this magic function. This stuff around is to actually (dis)connect everything (user inputs, inter-widget dependencies, instantiation, disposal, ...) in a live ("reactive") manner and maintain all the state of the app - the magic layouting function is just a negligible fraction of the whole machinery.

I guess it's hard to know if this is going to be supported or not. Fyne is based on outcome based use cases (focused on cross platform) rather than generalised concepts.
The project aims to make common tasks really easy and enforce a consistent user experience across all apps.

The project aims to make common tasks really easy and enforce a consistent user experience across all apps.

Based on how such generalization works e.g. in Red (as mentioned above), I'd hypothesize that using such "generalized streams" (similar to dataapi future features as discussed with @steveoc64 in fyne-io/7guis#3 ) would account for the same "complexity" (i.e. making things really easy for the programmer) while maintaining consistent user experience. But that's just my (biased) guess πŸ˜‰.

Fyne is based on concrete use cases rather than generalised concepts.

Didn't know that. Thanks for clarification. I'll then look forward to see how Fyne (and others) will tackle the ever growing universe of concrete use cases (currently those are e.g. foldables which is in essence very close to the "task" I described above). Either way keep the good work!

I updated my comment to be a little more focused (now "outcome based use cases - focused on cross platform"). If you want to discuss more the design of our toolkit we'd happily welcome the chat in the #fyne channel of gophers Slack - or on our own GitHub issue tracker. Let's take the discussion off vlang's project :).

For those interested, these libs are somewhat close to what I envision under streams/flows/live_variables/reactiveness/signals of samples:

  1. Flyd

  2. S (explained example)

    1. might look more verbose than Flyd, but it's actually vice versa
    2. no built-in stream filtering (you have to write your own if-else logic if you don't want to react to every new sample in the stream)
    3. it seems easier to use together with "static" HTML/DOM templates
    4. it's more intuitive

To demonstrate the urge of needing streams/flows/live_variables/reactiveness/signals, feel free to take a look at what the following tools produce.

  1. Principle
  2. Origami Studio (animated examples)

Technical blog post about "live" widgets and how to implement them to achieve effortless "composition in time": https://potocpav.github.io/programming/2020/05/01/designing-a-gui-framework.html .

I highly recommend reading it as it shows yet another simple to implement in mainstream languages approach to liveness.

Author of the blog post divides the problem of UI library architecture into two orthogonal concepts. "Composition in space" (i.e. layouting, theming, etc.) and "composition in time" (liveness of all kind: reaction to user input, I/O input, timers, etc.).

Unfortunately currently V UI focuses almost exclusively on "composition in space" which is actually the easier concept of the two to tackle. So far V UI almost ignored the "composition in time" and as @kahsa said in #31 (comment) it's about time to open this painful topic.

@rcqls you spend a lot of time with V UI lately, so I think this might be of interest to you.

About year and a half ago few French guys got an idea how to tackle the complexity of GUIs in a very different way than any other existing UI lib (that's not an exaggeration). The idea was to apply pure ECS (a readable example in C) architecture to (G)UI. I myself find the results pretty amazing - read about it in the great Polyphony paper and see the code.

It's very small code-size-wise, it's readable, it's performant, it's parallelizable, it's multiplatform incl. web (no obscure dependencies), it's declarative (even more than the current V UI architecture!), it fits the V philosophy, it's rendering backend agnostic (it shall also support retained native UI widgets if someone will invest the time to create corresponding proxy structs and methods).

And now the main programmer of Polyphony sketched how the future would look like and the first thing he mentioned was rewrite it in a different language. To me V UI seems like the best candidate, but we'd need to accept the fact, that it'd involve research work (i.e. trial & error) and not just "pure programming".

Could you @rcqls maybe take a look at the Polyphony paper and sum up your thoughts on the idea of changing V UI architecture to ECS GUI (i.e. Polyphony-like architecture)?

rcqls commented

@dumblob Thanks for the link. After a quick reading of the beginning of the paper, I can't see how entities could be technically defined in v since it would require some kind of map[string]Component{} with Component a sum type of all kind of components. But dealing with Component as a sum type could be cumbersome. V does seem to have "dictionary" container with values of any type, like hash in ruby,, named list in R, and Object in js, map of Any in julia. Generally the reason is also that these kinds of containers are not really efficient because of the Any type value. Sum type is a possibility (with the constraint to predefined the long list of all component types) but dynamic casting should be applied whenever you use (connect or update) the component to the entity. Of course, it is a quick thought and answer and I may be wrong. P.S.: btw, why don't you participate in the development of v ui or an alternatif GUI on v ?

Interfaces seem like the natural fit here; every Component could have certain methods and/or fields that make it a Component. Then you can have a map[string]Component storing/referencing all Components in the app.

My concern is that the GUI ToolKit is not that simple to begin with.
So, if we keep extending it ad hoc without a unified design philosophy that runs through the whole thing, it may end up being as complex as GTK.

rcqls commented

Interfaces seem like the natural fit here; every Component could have certain methods and/or fields that make it a Component. Then you can have a map[string]Component storing/referencing all Components in the app.

I don't think so. Component as an Interface would have a worse problem with dynamic casting.

@rcqls

@dumblob Thanks for the link. After a quick reading of the beginning of the paper, I can't see how entities could be technically defined in v since it would require some kind of map[string]Component{} with Component a sum type of all kind of components. But dealing with Component as a sum type could be cumbersome. V does seem to have "dictionary" container with values of any type, like hash in ruby,, named list in R, and Object in js, map of Any in julia. Generally the reason is also that these kinds of containers are not really efficient because of the Any type value. Sum type is a possibility (with the constraint to predefined the long list of all component types) but dynamic casting should be applied whenever you use (connect or update) the component to the entity. Of course, it is a quick thought and answer and I may be wrong.

I actually don't think this is needed at all. Just look at how ECS is implemented in C here (here a unique instance of an Entity is nonexistent - what is considered as Entity is a number which has the property, that it's a valid index into every existing array of Components - and there is such array for every single existing Component; of course there is one more array of bool values of the same length saying whether the Component exists or not to manage their lifetime; and yes those are sparse arrays but there are solutions & alternatives to that as seen e.g. in Flecs) and in Flecs.

The same is possible in V and even easier. Sure, this does not implement Polyphony, but because Polyphony is pure ECS, it can be mapped to C easily (the paper also explicitly says Polyphony tries to be fully language-independent, so the principles are valid also elsewhere despite there is some "sugar" in Polyphony to showcase how some boilerplate can be cut down by using neat language features).

P.S.: btw, why don't you participate in the development of v ui or an alternatif GUI on v ?

Time, time, time. Family & jobs (I'm not programming for living) is my priority. But I don't have the heart to see V community investing mamoth effort in something which is already obsolete. And V UI might actually be exactly this case in the light of ECS GUI.

@kahsa

My concern is that the GUI ToolKit is not that simple to begin with.

Despite not knowing what you're referring to with "GUI ToolKit", I can only agree with you that developing any (G)UI library is not simple. Actually I'm certain it's on par (if not more) with complexity and "size" of developing a programming language. I was (one of?) the first person(s) who strongly discouraged V community from even thinking of yet another UI library development and I still am - especially considering the scarce resources which must first flow into the language itself and first after a "release candidate" will be released we might focus on unrelated libraries (such as V UI). There was even a period IIRC when V community loudly protested against major V devs spending quite some time with V UI instead of developing V itself.

But hey, here we are, there are hundreds/thousands SLOC in V UI repo and I can't just passively sit and look at the struggling V community without at least tossing my gathered knowledge into the issue tracker during the very few minutes of my spare time I have every day. I prefer doing something for V and the people around it instead of spending the short time with reading newspapers or hanging out with friends.

So, if we keep extending it ad hoc without a unified design philosophy that runs through the whole thing, it may end up being as complex as GTK.

No. My intention with ECS GUI architecture is much more radical. The current V UI architecture is not only messy but much more "undirected" - there is no vision, no unified design philosophy as you said, no clear bounds. At least there is a (huge) list of use cases & desired functionality. But I digress. So my idea is to completely rewrite V UI using ECS GUI architecture.

But despite ECS GUI being IMHO extremely promising as a hollistic approach to UI (and I'm saying this for the first time in my life about a GUI), it's very novel, so the development wouldn't be just plain "implementing an existing standard" but rather cooperating with ECS GUI authors and trying out new ideas (i.e. research) pushing ECS GUI further to achieve a complete system at some point. E.g. the ECS GUI paper completely deliberately omits any layouting or visual specification. This will need to be researched from zero.

rcqls commented

Are you sure that the c example is not too simple. In the article not all the entities are sharing all the components as it is the case in the c example. In my first understanding each entity has its own subset of components.chosen in the whole set of components. Otherwise it is memory consuming. So I misunderstand something.... Thanks for your feedback.

I think you're understanding it correctly. Some observations though:

  1. The first example was very naive - with fixed size arrays of pointers it doesn't make much sense because their size would need to account for all potential Entities during the whole app lifetime which would be wasteful as you said (also it would be quite wasteful if at some point many Entities were deleted and the arrays would not shrink). But let's consider this to be the absolute worst case scenario.

    For an average AAA 3D game (which has about 150 Components and 1 million Entities) this would yield 150*1000_000*64bit=1145MBytes of memory. That's not negligible for just bookkeeping (i.e. no actual values, but just mere existence of Entities). But considering only a few cells of this virtual matrix (~20%) would be actual pointers, it's actually a pretty decent result for an AAA game.

    A mid-complex (G)UI uses much less Components (about 50 as I read on one blog - unfortunately I didn't save the link; Polyphony has 22 Components) and much less Entities (maybe up to 10_000). That means 50*10_000*64bit=4MByte for such an app. Which is nothing (for 32bit systems it's just 2MByte). It's actually more or less on par with current V UI as V UI needs to instantiate a whole struct for each visual object/widget/...

  2. Nobody uses the naive technique from (1), but e.g. compressed maps or sparse sets or sparse matrices or more sophisticated structures etc. Feel free to take a look how Flecs solves it. That cuts down the overhead from 5x to about 1.1x. In which case it's considerably more memory efficient than the current V UI architecture.

If you have free time it could be motivating that you propose the drawing example of the paper in V code.

I'll think about it (might take weeks/months when it comes to finding time to write code - so do not wait for me and feel free to try it yourself any time sooner).

No. My intention with ECS GUI architecture is much more radical. The current V UI architecture is not only messy but much more "undirected" - there is no vision, no unified design philosophy as you said, no clear bounds. At least there is a (huge) list of use cases & desired functionality.

I'm sorry for misleading you.
I am in total agreement with you on that point.
I just don't know which model is best to use, as I am not up to date with the latest knowledge.
Currently, "simplicity" seems to be the main philosophy, but that will eventually reach only personal level libraries.
Of course simplicity is OK but in order to answer the needs of many end developers, a suitable system is needed, but the current system is not a good enough foundation for that.
However, I am not personally dissatisfied with the current system, as it is sufficient for my personal use.

I also think that easy extensibility of existing components is important for GUI development.
OOP was suitable in this respect.
By having the basic logic in an abstract class and defining the behavior in an interface to make it versatile, the end user could utilize them to get an original component with only minor changes to the advanced component.
That is difficult to do in V.
How do other non-OOP languages overcome this problem?
Isn't that one of the reasons why all the GUI libraries in languages like Go and Rust are not very exciting? Of course, this is not a survey result, but a subjective opinion.
So, in order to stand out from Go and Rust, V should also consider incorporating some kind of syntax that takes GUI creation into account, just as attributes and the like are being incorporated for web development.

I just don't know which model is best to use,

Me neither 😒. That's why we're having this discussion πŸ˜‰.

I also think that easy extensibility of existing components is important for GUI development.

Well, this might be misunderstanding. Extending existing Components ("C" in ECS) in an ECS GUI is a no-go. The point is, that Components resemble 1st normal form (in database terms πŸ˜‰) and extending them would almost for sure break this normalization. So in ECS one always "extends" by adding new Components and nearly never touches an existing Component. And even in cases when it would make sense to extend an existing component, it's better to introduce a new separate one. It's because existing Systems ("S" in ECS) closely depend on Components, their size, memory layout, etc. So making them bigger would certainly brake something at least subtly (e.g. invisibly like making the former performance worst case even worse).

How do other non-OOP languages overcome this problem?

Well, in case of ECS the problem doesn't even exist as demonstrated above πŸ˜‰. So no need to worry about anything like that. ECS is really fully composable on its own already at the "model level" without relying on any "implementation level" technological/language features.

A linguist would say, that despite natural languages are shaping our concept thinking (and thus the concepts are inherently dependent on the particular natural language features - this is the case of OOP), ECS wasn't shaped by any language, but objectively determined based on observation of patterns in game industry. So ECS is a pretty artificial concept, but it makes it also fully language independent πŸ˜‰.

Isn't that one of the reasons why all the GUI libraries in languages like Go and Rust are not very exciting? Of course, this is not a survey result, but a subjective opinion.

Hm, I actually don't think so. I think it's much more due to GUI libs being really hard and time consuming to develop. And both Go & Rust are too new to allow for a mature full-size GUI library - simply because there wasn't enough time for that πŸ˜‰ (assuming non-ECS GUI libs because it seems to me ECS GUI libs might be quicker to develop once the "1st normal form" of most GUI features is found by researchers). Additionally ECS is much more language agnostic than majority of other GUI systems I know of.

So, in order to stand out from Go and Rust, V should also consider incorporating some kind of syntax that takes GUI creation into account, just as attributes and the like are being incorporated for web development.

That's an interesting idea. For ECS there is not much boilerplate even in lower-level languages like C (as Flecs demonstrates). Despite that authors of Polyphony actually recommend using language features to make ECS programming more friendly and less "strict".

Still IMHO V is in a better position with regards to ECS than C/Go/Rust/Zig/... with its current set of features and quite minimal syntax, so I'd first start with using pure V and if there will be any point where repeating boilerplate would become apparent, we could think about a tiny DSL (like for SQL, etc.).

rcqls commented

@dumblob Thanks for this clarification about memory efficiency. However you need to multiply by the mean size of component. But the idea of matrix crossing entities and components (that can be sparse) is very informative. Also I have ended up a first quick reading of the article and ECS is very interesting. I'll try to propose a version of ecs in v with, for each component , a map[int] to represent it instead of array of components.

rcqls commented

@dumblob flecs is huge and then not simple to translate in pure v since a lot of c tricks seem also to be used. My first attempt to define a world struct embedding entities and components failed in v because map[string]<Interface> (where <Interface> is an interface mostly to describe different) is not easy to manipulate in v. Maybe it would require a lot of time to develop the basis of a ecs in v. Maybe you already proposed a first version yourself before asserting that it is feasible in v. I am not talking about of a ecs adapted for gui. Javascript is much more simple to develop with its very flexible concept of Object. However, I find the concept of ecs very interesting.

Editable data grid, like Excel or Access.
I think that's the most complex and needs to be highly functional component of GUI.
I think one of the touchstones is to be able to create a data grid that is easy to use for both developers and application users.

rcqls commented

@dumblob After some fight 😁, I managed to have a first version of vecs with world, entities and components. But it is of course very basic.

import vecs
import gx

fn main() {
	mut world := vecs.world()

	world.new_component("comp1",vecs.comp1_map(world))
	world.new_component("comp2",vecs.comp2_map(world))
	
	world.new_entity("tutu")
	world.new_entity("titi")

	vecs.comp1(world).set("titi",vecs.Comp1{})
	vecs.comp2(world).set("titi",vecs.Comp2{})

	vecs.comp1(world).set("tutu",vecs.Comp1{gx.rgb(10,10,10)})
	
	mut c1 := vecs.comp1(world).get("titi") ?
	c1.color = gx.rgb(20,0,0)
	vecs.comp1(world).set("titi",c1)
	println(vecs.comp1(world).get("titi") ? )
}

What was funny, I called world the global context before looking at flecs source code.

Wow, you're quick! This V ECS lib looks promising to me!

Today I got an email pointing me to this small list of features which are difficult to achieve in Unity (many have mentioned Unity to have one of the best - if not the best - UI toolkits to work with). It all boils down to the dynamicity & interaction (incl. advanced/free-form animations) as thoroughly discussed in this thread as well as in others.

ECS is a viable answer IMHO. I'll see if I'll find some time after I return from abroad in about a month.

Those advocating for Flutter-like functionality should definitely see this example.

And those who don't (incl. me πŸ˜‰) should take a look at these examples (this might be enormously interesting for V because VUI wanted to use native widgets which didn't happen yet - but these examples show it's possible to do and in a modern manner!).