~eliasnaur/gio

3 3

Support for "CustomEvents" and "Addons"

Details
Message ID
<fe3835f7-b4d4-4db9-81fb-dfd8ab06f2ed@www.fastmail.com>
DKIM signature
missing
Download raw message
Hi, 

I would like to discuss something related to "Custom Events" and a 
proper way to register "Addon" (or external packages that integrates 
with Gio). 

First, I would like to explain what I see as a issue, in that case two 
problems: 

1. The "Gio" vs "non-Gio" packages and Ops: 

Currently, we can use `key.InputOp` in order to receive information about 
keyboard, or even `clipboard.WriteOp` to write something in the clipboard. 
That is nice and it is very usufull, since without it we need to travel 
the data from the main-loop events to the widget. However, custom package 
cannot use the same technique. So, we can’t use `gyroscope.InputOp{}.Add(gtx.Ops)`, 
for instance, or even `haptic.BuzzOp{}.Add(gtx.Ops)`. In other words, 
every external package must have a special treatment, even using some 
global function (which must be set before the usage of the widget) or 
include something in the `struct` or equivalent. It’s impossible to
use external packages “out-of-box”, as simple as Gio core `key.InputOp`. 

2. The “chaos” of AppView (and friends): 
Currently, many packages needs the AppView and sometimes the `app.Window`. 
The issue of that is: each package has their own method of get such data. That 
collaborates with the first issue. So, in case of `haptic` we must use 
`haptic.NewBuzzer` function, just because it may need the `AppView`. In 
case of `gohyperlink` it also need to set the AppView, which can be done 
by adding a function into the `for … := range w.Events {}`. 

In general: there’s no standard and, again, a simple use of 
`gohyperlink.Open()` isn’t possible. The developer needs to setup it 
before, by adding functions or sets parameters. 

--------------------- 

How we can improve and fix that problem: 

1. Create some new `gio/io/addon` package, which can be used to register 
package as “addon”. The package (in this case `gyroscope` or 
`haptic` or `gohyperlink`) must include something like: 

``` 
import “gioui.org/io/addon” 
func init() { 
addon.Register(“name_of_addon”, my_addon) 
} 

``` 

That would accept some specific interface as `my_addon`. The `my_addon` 
can be some interface (undetermined yet) that can read and write Events. 

2. Gio will transfer any Events to all addons registered (if any). 

3. Gio will also support `op.CustomEventOp` (or equivalent) which can store 
arbitrary data as an interface. 

4. Gio will also broadcast the Events 
from the custom events, so the custom event can use `Write` and create 
events (and widget can get by `gtx.Events(…)`). 

The developer itself does not need to register the addon, since it will 
be done by the `init`, so you could use: 

```
func widget(gtx layout.Context) layout.Dimensions { 
     haptic.BuzzOp{}.Add(gtx.Ops) 
     // ...
} 
```

In that case, the haptic (which is already registered, by default) will 
receive the `BuzzOp` and can execute their functionality. The `haptic`
will also receive the `AppView`, since it’s listening to any Events, 
due to the fact that it’s registered using `addon.Register`. 

That also executes after the end of the frame, which provides a better 
experience. Currently, using `buzz.Buzz()` during the frame doesn’t 
seems optimal choose, and trying to avoiding it adds even more code. 

---------- 

I think it would simply the usage of external function and make the 
gio-x more powerful and simple to use and matches if the current `Op`
of Gio itself.
Details
Message ID
<CAFcc3FSi1wDW+sjPNHP-+vZtLF-YtjSqfPwKbAT5P9Q6cPKw7w@mail.gmail.com>
In-Reply-To
<fe3835f7-b4d4-4db9-81fb-dfd8ab06f2ed@www.fastmail.com> (view parent)
DKIM signature
pass
Download raw message
I think something like this could be very useful. I don't especially
want Gio's event router choked with application-level events, but I
think it makes a lot of sense to expose platform events like the
gyroscope or haptic feedback as events and operations similar to the
clipboard. I think it comes down to whether or not we can create this
extensibility without over-complicating the existing code to achieve
it.

Elias, do you think there's room to create this kind of extensibility
for other hardware/input event types?

Cheers,
Chris
Details
Message ID
<CC3AJMNWP2OG.26WOEZW1HYLSL@testmac>
In-Reply-To
<fe3835f7-b4d4-4db9-81fb-dfd8ab06f2ed@www.fastmail.com> (view parent)
DKIM signature
fail
Download raw message
DKIM signature: fail
On Tue Jun 8, 2021 at 18:55 CEST, Lucas Rodrigues wrote:
> Hi, 
>
> I would like to discuss something related to "Custom Events" and a 
> proper way to register "Addon" (or external packages that integrates 
> with Gio). 
>
> First, I would like to explain what I see as a issue, in that case two 
> problems: 
>
> 1. The "Gio" vs "non-Gio" packages and Ops: 
>
> Currently, we can use `key.InputOp` in order to receive information about 
> keyboard, or even `clipboard.WriteOp` to write something in the clipboard. 
> That is nice and it is very usufull, since without it we need to travel 
> the data from the main-loop events to the widget. However, custom package 
> cannot use the same technique. So, we can’t use `gyroscope.InputOp{}.Add(gtx.Ops)`, 
> for instance, or even `haptic.BuzzOp{}.Add(gtx.Ops)`. In other words, 
> every external package must have a special treatment, even using some 
> global function (which must be set before the usage of the widget) or 
> include something in the `struct` or equivalent. It’s impossible to
> use external packages “out-of-box”, as simple as Gio core `key.InputOp`. 
>
> 2. The “chaos” of AppView (and friends): 
> Currently, many packages needs the AppView and sometimes the `app.Window`. 
> The issue of that is: each package has their own method of get such data. That 
> collaborates with the first issue. So, in case of `haptic` we must use 
> `haptic.NewBuzzer` function, just because it may need the `AppView`. In 
> case of `gohyperlink` it also need to set the AppView, which can be done 
> by adding a function into the `for … := range w.Events {}`. 
>
> In general: there’s no standard and, again, a simple use of 
> `gohyperlink.Open()` isn’t possible. The developer needs to setup it 
> before, by adding functions or sets parameters. 
>
> --------------------- 
>
> How we can improve and fix that problem: 
>
> 1. Create some new `gio/io/addon` package, which can be used to register 
> package as “addon”. The package (in this case `gyroscope` or 
> `haptic` or `gohyperlink`) must include something like: 
>
> ``` 
> import “gioui.org/io/addon” 
> func init() { 
> addon.Register(“name_of_addon”, my_addon) 
> } 
>
> ``` 
>
> That would accept some specific interface as `my_addon`. The `my_addon` 
> can be some interface (undetermined yet) that can read and write Events. 
>
> 2. Gio will transfer any Events to all addons registered (if any). 
>
> 3. Gio will also support `op.CustomEventOp` (or equivalent) which can store 
> arbitrary data as an interface. 
>
> 4. Gio will also broadcast the Events 
> from the custom events, so the custom event can use `Write` and create 
> events (and widget can get by `gtx.Events(…)`). 
>
> The developer itself does not need to register the addon, since it will 
> be done by the `init`, so you could use: 
>
> ```
> func widget(gtx layout.Context) layout.Dimensions { 
>      haptic.BuzzOp{}.Add(gtx.Ops) 
>      // ...
> } 
> ```
>
> In that case, the haptic (which is already registered, by default) will 
> receive the `BuzzOp` and can execute their functionality. The `haptic`
> will also receive the `AppView`, since it’s listening to any Events, 
> due to the fact that it’s registered using `addon.Register`. 
>
> That also executes after the end of the frame, which provides a better 
> experience. Currently, using `buzz.Buzz()` during the frame doesn’t 
> seems optimal choose, and trying to avoiding it adds even more code. 
>
> ---------- 
>
> I think it would simply the usage of external function and make the 
> gio-x more powerful and simple to use and matches if the current `Op`
> of Gio itself.

Hi Lucas,

Great summary and proposal.

I think the interesting use case is custom events such as "dialog
dismissed", "profile picture clicked" that are specific to each Gio
program. If we can solve custom events, hardware sensors and output
would turn into special cases. Clipboard access seems to fit as well,
and pointer and keyboard input are not far off.

While I'm not a fan of init functions and global registries, they're an
even worse match for custom events. I don't think we want to register an
addon for every distinct event type a program may have.

Fortunately, registries don't seem absolutely required by your proposal.
For example, we could make clients do:

```
var hap haptic.State

...
case app.ViewEvent:
	hap.UpdateView(e)
case system.FrameEvent:
	hap.Frame(gtx.Ops)
	// or
	hap.Frame(gtx.CustomEventOps())
```

or similar.

Issues:

- Where will custom events be injected? gtx.Queue.Add(tag, events)?
- Can op.CustomEventOp be implemented in a garbage free or garbage
  minimized manner? Maybe CustomEventOp will only be used for the few
  gyroscope listeners, if custom events such as "dialog closed" always
  knows their target tag for gtx.Queue.Add.

Elias
Details
Message ID
<55a4137b-0537-477b-956e-67afca7a727e@www.fastmail.com>
In-Reply-To
<CC3AJMNWP2OG.26WOEZW1HYLSL@testmac> (view parent)
DKIM signature
missing
Download raw message
My point against `var hap haptic.State`, `hap.UpdateView(e)`, 
`hap.Frame(gtx.Ops)` (...) is that require more setup than any native 
Gio Op. You don't use `key.UpdateView(e)`, `key.Frame(gtx.Ops)` (...). 

The idea behind the `init()` was based on `image/*` and `hash/*`, for 
instance: 

The image/png register it using `init`: 
https://github.com/golang/go/blob/2ebe77a2fda1ee9ff6fd9a3e08933ad1ebaea0 
39/src/image/png/reader.go#L1033-L1035 The x/crypto/blake2 register 
itself using `init`: 
https://cs.opensource.google/go/x/crypto/+/c07d793c:blake2b/register.go 

In the case of Gio: the addon (haptic, for instance), use the `Register` to obtain 
information about the Window and FrameOps. So, Gio can provide everything 
without manually inserting it on `window.Event`. Also, it can send data and use 
Invalidate on the given Window...

I don't think there's any advantage of using `hap.UpdateView(e)`. If we 
consider that Gio could do something like: 
```
for _, addon := range addons.Addons { 
addon.Events(e) 
}
``` 

That will happen inside Gio, and removes the need to set everything 
manually since `addons` holds all addons that uses `Register`... But,
on the other hand, maybe it's not the performant way because it will give 
any Events (not only ViewEvent). However, it's fair easy to use, and for
the consumer of the addon (me that use Haptic, for instace), it works 
out-of-box. Also, we don't have the risk of setting `hap.UpdateView(e)`
and forgot to set `hap.Frame(gtx.Ops)` and other misuses. 


-----------------



-- 
  Lucas Rodrigues
  inkeliz@inkeliz.com

On Mon, Jun 14, 2021, at 12:06 PM, Elias Naur wrote:
> On Tue Jun 8, 2021 at 18:55 CEST, Lucas Rodrigues wrote:
> > Hi, 
> >
> > I would like to discuss something related to "Custom Events" and a 
> > proper way to register "Addon" (or external packages that integrates 
> > with Gio). 
> >
> > First, I would like to explain what I see as a issue, in that case two 
> > problems: 
> >
> > 1. The "Gio" vs "non-Gio" packages and Ops: 
> >
> > Currently, we can use `key.InputOp` in order to receive information about 
> > keyboard, or even `clipboard.WriteOp` to write something in the clipboard. 
> > That is nice and it is very usufull, since without it we need to travel 
> > the data from the main-loop events to the widget. However, custom package 
> > cannot use the same technique. So, we can’t use `gyroscope.InputOp{}.Add(gtx.Ops)`, 
> > for instance, or even `haptic.BuzzOp{}.Add(gtx.Ops)`. In other words, 
> > every external package must have a special treatment, even using some 
> > global function (which must be set before the usage of the widget) or 
> > include something in the `struct` or equivalent. It’s impossible to
> > use external packages “out-of-box”, as simple as Gio core `key.InputOp`. 
> >
> > 2. The “chaos” of AppView (and friends): 
> > Currently, many packages needs the AppView and sometimes the `app.Window`. 
> > The issue of that is: each package has their own method of get such data. That 
> > collaborates with the first issue. So, in case of `haptic` we must use 
> > `haptic.NewBuzzer` function, just because it may need the `AppView`. In 
> > case of `gohyperlink` it also need to set the AppView, which can be done 
> > by adding a function into the `for … := range w.Events {}`. 
> >
> > In general: there’s no standard and, again, a simple use of 
> > `gohyperlink.Open()` isn’t possible. The developer needs to setup it 
> > before, by adding functions or sets parameters. 
> >
> > --------------------- 
> >
> > How we can improve and fix that problem: 
> >
> > 1. Create some new `gio/io/addon` package, which can be used to register 
> > package as “addon”. The package (in this case `gyroscope` or 
> > `haptic` or `gohyperlink`) must include something like: 
> >
> > ``` 
> > import “gioui.org/io/addon” 
> > func init() { 
> > addon.Register(“name_of_addon”, my_addon) 
> > } 
> >
> > ``` 
> >
> > That would accept some specific interface as `my_addon`. The `my_addon` 
> > can be some interface (undetermined yet) that can read and write Events. 
> >
> > 2. Gio will transfer any Events to all addons registered (if any). 
> >
> > 3. Gio will also support `op.CustomEventOp` (or equivalent) which can store 
> > arbitrary data as an interface. 
> >
> > 4. Gio will also broadcast the Events 
> > from the custom events, so the custom event can use `Write` and create 
> > events (and widget can get by `gtx.Events(…)`). 
> >
> > The developer itself does not need to register the addon, since it will 
> > be done by the `init`, so you could use: 
> >
> > ```
> > func widget(gtx layout.Context) layout.Dimensions { 
> >      haptic.BuzzOp{}.Add(gtx.Ops) 
> >      // ...
> > } 
> > ```
> >
> > In that case, the haptic (which is already registered, by default) will 
> > receive the `BuzzOp` and can execute their functionality. The `haptic`
> > will also receive the `AppView`, since it’s listening to any Events, 
> > due to the fact that it’s registered using `addon.Register`. 
> >
> > That also executes after the end of the frame, which provides a better 
> > experience. Currently, using `buzz.Buzz()` during the frame doesn’t 
> > seems optimal choose, and trying to avoiding it adds even more code. 
> >
> > ---------- 
> >
> > I think it would simply the usage of external function and make the 
> > gio-x more powerful and simple to use and matches if the current `Op`
> > of Gio itself.
> 
> Hi Lucas,
> 
> Great summary and proposal.
> 
> I think the interesting use case is custom events such as "dialog
> dismissed", "profile picture clicked" that are specific to each Gio
> program. If we can solve custom events, hardware sensors and output
> would turn into special cases. Clipboard access seems to fit as well,
> and pointer and keyboard input are not far off.
> 
> While I'm not a fan of init functions and global registries, they're an
> even worse match for custom events. I don't think we want to register an
> addon for every distinct event type a program may have.
> 
> Fortunately, registries don't seem absolutely required by your proposal.
> For example, we could make clients do:
> 
> ```
> var hap haptic.State
> 
> ...
> case app.ViewEvent:
> 	hap.UpdateView(e)
> case system.FrameEvent:
> 	hap.Frame(gtx.Ops)
> 	// or
> 	hap.Frame(gtx.CustomEventOps())
> ```
> 
> or similar.
> 
> Issues:
> 
> - Where will custom events be injected? gtx.Queue.Add(tag, events)?
> - Can op.CustomEventOp be implemented in a garbage free or garbage
>   minimized manner? Maybe CustomEventOp will only be used for the few
>   gyroscope listeners, if custom events such as "dialog closed" always
>   knows their target tag for gtx.Queue.Add.
> 
> Elias
> 
Reply to thread Export thread (mbox)