Page MenuHomePhabricator

Use efl.part for efl.ui.model.connect
Open, HighPublic

Description

To introduce a default value on connect, it would be nice to actually support efl part interface on efl.ui.model_connect. Would have something like :

efl_ui_model_connect(priv->entry, "path"); //connect default "elm.text" to "path" property
elm_view_list_property_connect(efl_part(priv->fileview, "filename"), "elm.text");

This looks harder to understand. Not sure this would be an improvement.

cedric added a comment.Jun 8 2017, 3:21 PM

Hum, you mean the default behavior ? In C++ and any object oriented language it would look like :

entry.model_connect("path");
list.filename.property_connect("elm.text");

It does feel nicer to me than :

entry.model_connect("elm.text", "path");
list.property_connect("filename", "elm.text");

Arguably, I am not sure it would be that nice in C++ as I am not sure we can define all the part in advance. So there would be a fallback to :

list.part("filename").propery_connect("elm.text");

Even maybe with some dynamic cast necessary in some more case, will need to check this.

jpeg added a comment.Jun 8 2017, 11:20 PM

If it's a part, it should be an efl.part API. The examples above are wrong. filename is not a part. elm.text is a part. elm.icon is a part.

If "text" is a part defined in the EO file of list:

list.text.property_connect("filename");

Otherwise, a little bit ugly indeed:

dynamic_cast<efl::Text>(list.part("text")).property_connect("filename");

Side question, maybe part could be done with:

dynamic_cast<efl::Text>(list["text"]).property_connect("filename");
cedric raised the priority of this task from TODO to High.Jul 10 2017, 2:40 PM
SanghyeonLee removed a subscriber: SanghyeonLee.
SanghyeonLee added a subscriber: SanghyeonLee.
zmike edited projects, added Restricted Project; removed efl.Jun 11 2018, 6:54 AM
bu5hm4n edited projects, added efl: widgets; removed Restricted Project.Jun 11 2018, 9:15 AM
zmike added a subscriber: zmike.

A #Goal ticket should not be set to a milestone.

@felipealmeida wouldn't that also solve the connect problem of not knowing what are the available property on an object ?

If we want that, should I take care of it ?

I discussed with SangHyeon the idea of generating classes from edje files with parts information, what do you think?

The idea, at first, would be to create the class dynamically in C# when loading a edje compiled file. Using "dynamic" keyword. I haven't tried to see if it is really possible.

@felipealmeida
though it is possible, it will need more time to implement this, so not a proper target for now. it should be later works i guess.
and also that would be more related with "style"s not "part" properties as I remembered, like user can easily find there styles by looking at pre-built theme classes such as,
Efl.Styles.List.Default;
Efl.Styles.ListItem.Full;

if we can find part name also, it would be great, but still we can support part name by Efl.Part if we define part information on "Eo" class.

@cedric
using Efl.Part which defined class with connect makes easier to understand code by user than using part name strings, at least they can notify connection is happen between something to Efl.Part.
I hope we using this information in connect. but for this,
Efl.Part will be defined in each Item Classes not in List,
so they have to know which item class is created by factory, and also have to know precise class type not Efl.Gfx.Entity to access Efl.Part Information.

that is main reason why we make connect in create event in T7441 like below,

liFac.CreateEvent += (object, model, parent, style) {
if (style == "default") {
  var item = (Efl.Ui.ListDefaultItem) object;
  item.icon = imgFac.Create(model, object);
  item.label. model.PropertyGet("label");
}
else if (style == "title") {
  var item = (Efl.Ui.ListTitleItem) object;
  item.icon = imgFac.Create(model, object);
  item.label = model.PropertyGet("label");
}}

if there are another doable way with this item's Efl.Part info, I think we can go with that also.

I agree with @SanghyeonLee that we do not have time to generate things from edje. This is a neat idea, but for later. In general there is a lot that could be done by autogenerating style related class and have that more explicit.

As for efl part, this would change the code into :

imgFac.property_link("filepath");
lifac.label.property_link("label");
lifac.icon.factory_link(imgFac);

If there was two differents source of factory with a potential different behavior from each factory, we would have :

fac1 = new Fac();
fac2 = new Fac();
fac1.label.property_link("john");
fac2.label.property_link("bob");
liFac.factory_link(fac1); // This will associate fac1 as the default value (This won't take effect until the value is != from EAGAIN)
liFac.title.factory_link(fac2); // This will associate fac2 when the default property of value is "title" 
liFac.property_link("style"); // Default property on a multifactory is a property that allow deciding which factory to use to create the item

Without part, you would just have double strings. Also as you may see here, some of the part are fully dynamically named like an part in edge. "default" and "title" are "style" returned value. As you also can see, there is no need here for getting access to Eina_Value and it leads to the possibility to later have a fully declarative language that would do all of that code generation for you as this is declarative not imperative. This would make writing UI generator easier.

The idea was to have specialized factory so that you would have all of that explicit in the API, but technically we do not need to specialize them to make it work. Still for being comprehensive to new users, specialization is better I think.

SanghyeonLee added a comment.EditedMon, Nov 19, 8:39 PM

So the problem is... actually we consider the option you suggested before,
but we drop it because,

first of all,
we might need to expose a bunch of factories per every list and grid style to support Efl.Part in the factory, like,

Efl.Ui.ListDefaultItemFactory {icon, end, label} ~= Efl.Ui.ListDefaultItem {icon, end, label}
Efl.Ui.ListFullItemFactory {content} ~= Efl.Ui.FullItemFactory {content}
Efl.Ui.GridDefaultItemFactory {icon, end, label} ~= Efl.Ui.GridDefaultItemFactory {icon, end, label}

they do nothing but only have some Efl.Part information which is already we have in Efl.Ui.~~Item classes.

Seconds,
also this solution can not be applied in multiple style supporting,
so in multiple style supporting,
we have need to allow multiple factories, or allow multiple item classes and you may need to access this item instance directly.
furthermore, most case of list usage in tizen was multiple styles, like titles, different icon batches, different labels... so this will be very common case as well.

the one thing I would suggest here is have multiple factory on view, with some identifier on the model property.. but this idea was dropped last discussion as I remembered.

if the first problem is okay to you, and any solution exist for second case, we could discuss again about this usage well.
still I'm not proper to make factories per item styles cause we need to maintain both side when one item class is changed.

Didn't we drop styled item at this point as per task T5307. So if you do not need per item style class, you shouldn't need per item factory. Arguably, it would be nice to have all the part documented and exposed nicely directly in our API instead of being implicit by the style we set. But that would be a nice to have at this point as we do not have the time for it.

As for the multi factory, the example I shown just above demonstrate how that would work. The multi factory link with multiple factory using a property of the model to decide which one to call upon. If you need to link the property of a factory, you can do it on either the multi factory one, which would logically repeat all the additional link to every linked factory (except for the default value as this one is used for dispatching decision). Or you can do the link on each factory as you are the one creating them and providing them to the multi factory as per example above.

I do not understand what you mean by having multiple factory on view, do you mean the view as to be aware of the property and would be doing the job of the multi factory class by doing the dispatching itself ? This would lead, I think, to a lot of code duplication, as each View would have to deal with this pattern while with a multi factory class, you only need one implementation really.

SanghyeonLee added a comment.EditedWed, Nov 21, 1:28 AM

I'm sorry that I didn't see the details of samples,
but in that case, when create() called by view to list factory, it calls create() to their sub factories?
hmm... could be.
so the liFactory in your sample looks like.. kind of composition factory..
yes, that is similar features that I had in my mind..

and also T5307, i remember that discussion and looks doable to me.
one thing is,
we will have some bunch of items classes, (if we go styles in eo, we can just remove them and put it in list item styles)
and we can give them in styles in factory,
which may linked internal method of property link and factory link,

but if the user want to customize multiple factories,
how we can support styles and property_link and factory_link implementation easily?

If users want to customize a multi factory, what do you have in mind ?

  • The simplest customization is as per above example dispatch on one property.
  • If more complex logic, like aggregation of property to decide, then the solution is a ViewModel that does the aggregation and provide a new property used by the multifactory to make its decision.
  • If even more complex case, that I can't imagine, you would basically have to inherit from a Factory that you want to customize, propagate the call that make sense using super and implement additional logic as you want. Might be by adding new part, or making the Create function more complex. I don't know, depends on what you have in mind. Of course this option is the most complex, but I would hope it stays rare.

Does that cover your questions about customization of factory ?

I am intrigued by having multiple style of items implemented in class, and slightly worried by them being implemented manually, but do you have any task describing that work ? If not can you create one and gather information on the topic ?

Hmm
on the create(),
if we can support some kind of internal item caching by protected method, this will be very helpful for them. I think create() is too big function that might user need some helper APIs that can replace common actions in create() like send signals, pop_cache, evaluate size~.

on the property_link, if they customized this features, they would know which indexed items are which styles, so I hope there are no big problem to convert their own item classes. I think here some nice guide documents will be enough.

But as you mentioned, adding styles in eo is not high priority, then we must find some other way to using Item class easily with Efl.Part infomation.
also we are not a big fan of using styles property on this case,

because every time new item style comes, we will add new item class and factory style manually.
and user have to find the same style name of item in the factory, which doesn't look like intuitive..

give more idea about this matter,
if we still want to go the styles in eo, it should be ready with MVVM release,
or we could provide another way like, set deligated proxy item to the factory, and user set factory_bind and property_bind in those proxy item.
or we could provide the first way with create callbacks.

I agree, I think it indeed make sense to provide a Factory class that will help deal with the cache management of object. I have created T7485 to track this work.

The reason we want the style in .eo files is because we want to document their interface and make them part of the stable API in an explicit way. Doing it manually, kind of does that job, but with added work and risk of error.

Also I am not convinced we need to have a factory per style of item. Factory take any type of item, and it the efl_part of an item that get made available on the factory. This I think is simpler as you do not need to use a specific factory to use a specific item.

What do you have in mind with delegated proxy item for factory ? Could you write a small example on how you see them being used ? In general I want to avoid going the way of the create callbacks as this is a sure way to end up with non declarative code.

Oh... I missed this task, sorry for late answer.

the way of I think is user made an item instance, and factory check the basic settings of that item.
user can do anything on that item but we couldn't copy every settings of that item so I think,
we have to give new APIs for factory and only support this API sets.

Efl.Ui.ListDefaultItem itemProxy = new Efl.Ui.ListDefaultItem(listview);
itemProxy.SetFactoryProxy(true); // This API may not necessary but it will not make resize_obj so factory could call internally.
itemProxy.Icon.BindFactory(imgFactory); //I think we might need new bind APIs which doesn't need binding target cause we know the target as Efl.Part.
itemProxy.Label.PartBindProperty("");

Efl.Ui.ItemFactory itemFactory = new Efl.Ui.ItemFactory();
itemFactory.SetItemProxy(itemProxy);

Hum, I see. I do not have a real opinion on this possibility. It is kind of weird to me that you have to provide a build item with all the call made on the item that the factory has to read from after. The main issue I see with this pattern is that it push for some problematic lifecycle regarding the proxy item, the factory and the view. Logically, the proxy item lifecycle should be linked with the factory, not the view (as a factory can be used by multiple view at the same time). So I would advise to write the example code as follow :

Efl.Ui.ItemFactory itemFactory = new Efl.Ui.ItemFactory(window);
Efl.Ui.ListDefaultItem itemProxy = new Efl.Ui.ListDefaultItem(itemFactory);

itemProxy.BindFactory(imgFactory); // This set the default factory to be an image factory
itemProxy.BindProperty("filename"); // This set the default property to be linked with the filename property from the model

itemFactory.SetItemProxy(itemProxy);

I am also not a fan of the terminology Proxy and I have some hard time imagining how to make it work if someone modify the itemProxy after calling SetItemProxy. Having the factory do all the work of learning the binding is also harder this way as currently, you just need to implement the function, while with this we need to expose enough information to extract all the binding information.

SanghyeonLee added a comment.EditedMon, Dec 3, 12:10 AM

well we can lock the object or only accept the setting in "Set" function calls.
so Set action is final call of decorate Item,
and we don't track the changes, if they need to change things,
they have to call SetProxyItem again.
Similar Concept is used in QT with name of ItemDeligate.

http://doc.qt.io/qt-5/qitemdelegate.html

example1: http://doc.qt.io/qt-5/qtwidgets-itemviews-stardelegate-example.html
example2: https://stackoverflow.com/questions/16660292/qt-using-custom-qitemdelegate-for-qtableview

can we go like this?
using Deligate, or Decorate terms are possible I think.

I'm not sure this is best idea but it is the only way that I can think to using efl.part information in List Item with Simple List and ListView both side.

+
what about... Item itself being an factory?
like below,

Efl.Ui.ListDefaultItem myItem = new Efl.Ui.ListDefaultItem(...);
myItem.Label.BindProperty("name");
myItem.Icon.BindFactory(imgFactory);
// item.clone() or item.create() is given, so item is itself an factory
myListView.SetFactory(myItem);

in multiple case, as @cedric 's idea,

CustomFactory : Efl.Ui.LayoutFactory? or Efl.Ui.ItemFactory {
   Efl.GfxEntity create(){
      string itemStyle =model.GetProperty("style");
      if (itemStyle == "default")
        return DefaultItem.clone();
      else if (itemStyle == "group")
        return GroupItem.clone();
   }
   void SetDefaultItem( Efl.GfxEntity item) {
      DefaultItem = item;
   }
   void SetGroupItem(Efl.GfxEntity item) {
      GroupItem = item;
   } 
}

CustomFactory customFactory = new CustomFactory(...);

Efl.Ui.ListDefaultItem myDefaultItem = new Efl.Ui.ListDefaultItem(...);
myDefaultItem.Label.BindProperty("name")

Efl.Ui.ImageFactory imgFactory = new Efl.Ui.ImageFactory(...);
imgFactory.Path.BindProperty("path");
myDeafultItem.Icon.BindFactory(imgFactory);

Efl.Ui.ListGroupItem myGroupItem = new Efl.Ui.ListGroupItem(...);
myGroupItem.Title.BindProperty("group");

customFactory.SetDefaultItem(myDefaultItem);
customFactory.SetGroupItem(myGroupItem);
myListView.SetFactory(customFactory);

I think we had dropped the proxy idea in Korea. I do not like it very much because it requires the factory to know too much about the item's internals.

w.r.t using Efl_Part, I find it OK. But I don't really see much improvement in C, syntax-wise and makes for a confusing lifetime of references. For C# it does give better syntax IMO.

I am not a big fan of going with clone. This seems to increase the complexity of the code in the item by moving the factory generic code to be custom in the item I think. At this point the code in factory is completely generic and require no knowledge of what it builds at all. As long as it is an Efl.Ui.View, it is good with it.

I think I still fail to understand what we are trying to improve here. Syntax wise, going with efl_part seems nicer indeed in C#. Now, if we are trying to do so, that the Factory class have the style of the item exposed somehow, why can't we just say in the documentation that after setting the class on a generic factory, it will follow that class binding capability ? Arguably, there are a lot of case where we have fully dynamic properties and we will likely have that case at the end for most application. Trying to make the code more complex just for a documentation indirection, seems counter productive.

The only issue I seriously see with all this plan is how do you detect typo ahead of time and that is hard, because we are in a fully dynamic system and until you have loaded the object, set everything on it, you do not know if that specific property is not going to suddenly appear...

@felipealmeida yes we dropped it. the only one reason I brought it again is our basic agreement of using create event with Efl.Part is now being dropped again and I need any-idea to support item creation in factory with efl.part.
@cedric why can't we just say in the documentation that after setting the class on a generic factory, it will follow that class binding capability I'm not sure I understand correctly,
this line means, we don't expose the efl.part and user set it manually like this,

itemFactory.SetItemClass(Efl.Ui.ListDefaultItem.GetClass());
itemFactory.BindProperty(efl_part("label"), "name");
// or I'm not sure which is possible,
itemFactory.BindProperty(Efl.Ui.ListDefaultItem.Label, "name");

@felipealmeida can you check which is possible way in our C# binding of efl.part?

well both of you said about the idea item_proxy is too complex, but the truth is this concept is already been accepted in QT and widely used for long terms.
so the concept itself being very familiar with application developer who have some experience of other platform.
I'm not insist this way is right way and we must have to go,
but,
there are few conditions that I required in our item generations as considering c# development.

1. STYLE and` PART` information by efl_part(STRING) is not recommended

: Our target user is included not only professional developer, also novices of programming, non-computer science major, and university students who may need very delicate help of IDE and Languages. see the university student how they learn UI programming. now Java or more high-level language(include c# :)) with fine IDE tool like android studio, Xcode or VS. Of cause most user find the way of implement application by google, but if they want to change few styles, or see how many parts that item supported, documented thing make very difficult to know this kind of information, while encoded thing can easily find by type the word in IDE.

2. Item between Efl.Ui.List and Efl.Ui.Listview share same user-experience.

: This is important for user and ourselves both. basic purpose of Efl.Ui.List is support user who might want modeless simple applications. but these widget is also have good benefit to learn easily about list widget how it works, and how it visually looks, so user might learn about view-based widget with simple test, and they could upgrade it as model-based view with same user-experience. about the platform maintainer, you and me, and some 2nd bender developer's who might maintain their own profiles, sharing class and implementing ways including efl.part information, is high benefit to reducing maintaining fee, if these two widget is not shared basic elements, I'm pretty sure list will be abandoned like elm_list after we fail to support it in few release version.

3. Factory need to be well constructed, so easy to customize and easy to understand their acting process, that user can easily modify them to support multiple styles or other requirements.

:    as I mentioned about cache in factory, we should not make user to create whole bunch of code to fix few operation in the factory. this means, factory need to have stepwise methods, which very looks clear to know what this method take cares and user can notify which method do they have to re-write as their wishes.

That's it. if we could fulfill this three conditions and the actual usage is easy to read, easy to understand without documenting - document must be left our final inevitable choice, so that user could easily understand the code and usage, with only looking at simple sample codes and IDE helps, without document - then I will be happy and I hope we will be happy for that.

if we cannot fulfill this thee conditions, we discuss about the cons and pros, and make the line, which level we will support this, and which level we will give up.

In C#, I would expect that the efl_part syntax look like that :

itemFactory.SetItemClass(Efl.Ui.ListDefaultItem.GetClass());
itemFactory.label.BindProperty("name")

The part that I don't know is how do you implement efl_part support in C#. Basically if you implement your own factory and want to provide part, how is that working. I think this is a core problem on how do people implement in C# a factory as this is what your concern are. @felipealmeida could you share with us an example on how people would implement a Factory in C# (From the Async create patchset I have put for review on phab ideally).

I need to look more into the complexity of how we can implement clone, but it is a very tricky function to implement. Theoretically, it should create a new object and set all the properties that where set on the cloned object back to the new object. This means that every widgets class in the hierarchy has to implement a clone function that does that copy. That is where I see the complexity as it require to not forget any property anywhere in the hierarchy and that will represent a lot of code that can possibly have bugs.

We can limit the problem and say only the Binding will be copied over, but that will likely create confusion for the users. I guess you see my concern here with .clone(). Alternate solution we could actually generate the clone function from the .eo file itself by automatically calling all the @property .get and then the @property .set function of the object and its parth that we can (There is a limit, it only work for simple case, when there is a key for a property it is hard to autogenerate any code, so there would still be need for doing it manually for some object, but hopefully less and we can enforce generated code to break if some of the required clone code is missing to some extend). Arguably, this is still quite the heavy machinery there. As is my concern.

I must also admit that the above 2 lines of code are not functionally equivalent to using proxy as you will have the users set all the default state of a item in the class constructor. So it move the complexity there, in some case. I still think from a maintenance and functionality point of view this is ok.

Your point 1, really make a strong case for having Style support in .eo ASAP. If it is that important, we should implement T5307 ASAP.

Regarding 2., I understand your point, I think we should be able to reuse the same Item class for both list. Just with ListView, you end up with just a factory to use as a difference on the View side of things. Of course, you still have to learn about MVVM.

As for 3., ticket T7485 follow that request. We can discuss this step there in more details. As for implementing multiple styles, this is something that T7480 will provide also out of the box. The other requirement will be covered by either the post creation event strategy as T7473 describe and the full implementation of a factory in C# which I hope @felipealmeida as some time to clarify.

Between cloning and just using Create event I vote for the second. I think both approaches run from MVVM in the end.

The MVVM-way would be to set these properties in the ProxyModel instead of the item itself. And let Factory or Create event bind the ProxyModel properties to item.

@felipealmeida so you want the original way that we dicussed before in Aug? I think you agreed with @cedric 's opinion about dropping that..
I'm not sure i totally understand your opinion so it would be better to explain this with simple pseudo-code or c# examples.

@cedric I think your way is may not possible as I see,
itemFactory.label.BindProperty("name")
cause itemFactory cannot know which part is exist in different item styles.
Efl.Ui.ListDefaultItem can have part { label, icon, end}
and Efl.Ui.????Item can have part {1text, 2text, 1icon, 2icon}
itemFactory can never know about it.

more doable way if itemFactory is,
itemFactory.BindProperty(Efl.Ui.ListDefaultItem.label, "name")

make the style and item part as static, so user can directly using them in parameter of the bind method.
I do not prefer this way cause user cannot know which part is allowed in this bind method,
it may need document guide or try-and-error by user themselves,
but if there are no good idea for set property with item part, I can agree this way.

This comment was removed by cedric.

The factory doesn't technically need to know anything about the item at all, like in Edje, we can create Efl_Part on the fly and resolve them later. Still, I think the way I would implement this is :

  • Enforce Item class to be set at finalize time.
  • Create an item on standby for internal use of the class only.
  • Every BindProperty call is shadowed to the internal item, if it fails to apply on it, we should fail the call on the factory too.
  • The factory remember all the successful call and is ready to replicate them on every item being created.

Still draw back of this technique is that an IDE can't figure out a minimally supported list of part (as it is fully dynamic), but with the positive outcome being once code base for every factory. If we had style class, they could all be trivially created from that mother factory.

The main issue I see, is that the list of part needs to always be dynamic, even when we do have a few advertised part, there is always more part available. The style definition in .eo is only going to cover the minimally supported list of efl_part that a class expose when a specific theme or class is set. It won't cover all the efl_part that an object could actually provide. I think it is ok to go with high generic solution at this point and implement support for styles in .eo to autogenerate code for improving IDE support as a next step.