Closed kisvegabor closed 4 years ago
Copy parameter in create functions
I would agree to get rid of this; there are very few cases where I've actually been able to make use of it. It would be a bonus to have a script that can automatically remove it from user code if the value is NULL
.
deep_clone(class) feature does no looks essential. I encourage cleanup of all old things, promoted by old (6.x) styles. New approach is "add secondary modifier class" instead of "copy + modify".
It seems like removing a feature to force people to use a given approach. It's not really bad to make people use LVGL as we want it to be used but it's good to know (explicitly say) if we do this for that purpose.
Usually, API cleanup is done not to force users do something, but to simplify maintenance.
I have noticed that the API is kind of missing uniformity right now. Many widgets have different ways of doing things and lack a consistent standard. Names are long but I guess that will be fixed by the CSS styling. Creating a Window widget for instance has add_text and add_button functions but no add_content function. To add content you need to create a page and set the window as the parent. Text and buttons are still a kind of content though so it is counter intuitive to add the content to the window in one and add the window to the content in the other.
The API needs simplification in my opinion and some of the API names could also use a renaming to make them more descriptive.
For a style like:
lv_obj_set_style_local_bg_opa(spinbox_holder, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
It would be better to have "set_style(widget_name, default, bg_alpha, TRUE or 50 etc, )". (alpha is opacity in graphic design). Then you create or add to a 2D array for that widget instance the style properties and their values. You would only store a state style for a widget that has a state.
You could store it in 3 ways.
After writing this one I realized this is the best way of doing it.
We need to try to make the API as clean and consistent as possible. It is really hard to keep track of inconsistencies. If you add a button as content in most widgets then there is no point to have a lv_msgbox_add_button function. We should add them the same way we add them to a page ( lv_obj_t * btn = lv_btn_create(h, NULL); ) which then also allows all the other styling to be done the same.
Every widgets structure would ideally look like this.
Maybe we should try to work out a solid API before continuing on other things because I have a feeling it is going to change how a lot of things are handled? I don't know enough about C to know what is the most resource efficient way of storing the data but I think that structures would be the most css like. Let me know if you want to get on a group chat to try and get an agreement going on how to best do the API and function naming. I could also open a new issue and add a list of more descriptive function names if that is easier.
PS. I am almost done adding comments to the widget example file and it is already making it much quicker for me to find things. I will upload it either later tonight or tomorrow.
@excitedbox
Based on your comment I started to think about which widgets rea really required. I found that about 10 widgets would be better to drop and leave the user to implement them as he needs. Of course, we can add examples of how to reproduce the old widgets.
Now widgets can have virtual and real parts. Having real parts indicates that the widget is created from other widgets. We should avoid it to have only the essential widgets in LVGL and provided examples to create complex widgets instead.
Here is my view about each widget:
Required: lv_arc lv_img lv_objmask lv_line lv_bar lv_slider lv_switch lv_chart lv_roller lv_dropdown lv_gauge lv_textarea lv_spinbox lv_cpicker lv_checkbox lv_canvas
To drop lv_win Easy to create from a header and content. lv_table, lv_btnmatrix They are very similar, can be joined to an lv_matrix object lv_page Scrolling merged to lv_obj lv_btn Its only feature is "checkable" (go to CHECKED state on click). Can be handled in the event function. lv_spinner It's not more than an animation on an arc. lv_cont Layout will be lv_obj property lv_list Easy to create from an lv_obj background, buttons and layout lv_label will be merged to lv_obj lv_msgbox Easy to create from a background a label and a button matrix lv_tabview Can be recreated easily if scrolling supports snapping. The only feature loss will be the moving indicator. (The selected tab still can have a bottom border) lv_calendar Very similar to a button matrix. With a good example it can be dropped.
Not sure lv_keyboard Easy from btnmatrix, but too common. lv_led Simple if only ON/OFF needed. With arbitrary brightness needs some calculations lv_imgbtn Background image is a style property so it's easy to mimic. However, can be tiled (repeat a center image) to set arbitrary width lv_tileview Difficult to reproduce to disabled scroll directions on tiles lv_linemeter Can be a simplified version of gauge
I was reading more about the CSS selectors and wondered how we can use them in LVGL. I found that with 2 types of selectors almost everything we need can be done. These are element type
and [data-value=]
. To handle them
uint8_t data
(or tag
) as lv_obj propertyThe manage the style with these selectors:
There can be simple conditions for data
too, e.g. <
, >
, <=
, <=
, ==
, !=
.
It'd allow features like:
<h1>
, <h2>
by creating styles for labels with different data
and set data
on the labels.I'd love to hear your opinion about these things.
About selectors:
I would not recommend use such things. And i would not recommend nth-* too, for similar reasons - it's too early to experiment with close-coupling (until architecture become stable)
I think it's important to consider the average LVGL user when making decisions like these.
While it is obviously possible to recreate most of the widgets with only a basic set, there is a significant segment of LVGL developers who are less experienced with GUIs and may struggle to create what looks to us like a fairly simple widget. I think it's worth keeping widgets like buttons and lists or at least have built-in utility functions that let users create them easily. I'd be quite comfortable building a simple window using a flexbox-like layout and some buttons, but someone else might not be. I might be able to, but wouldn't necessary be comfortable with building a tabview. I like how LVGL right now includes a lot of widgets which are very usable and customizable.
If, internally, we want to simplify widgets' implementations and remove the current duplication, I think that's fine. However, I don't think we should remove features outright and leave users to find them in examples or reimplement them themselves.
Good set of examples (with online demos) should help user how to implement popular patterns.
Also, probably lv_msgbox
needs special care (properly implemened confirmaion popup is popular demand).
Maybe what we need here is a clear architectural separation between the GUI framework which provides the basic widgets and then a higher-level set of APIs on top of that which add things like modal dialogs.
The message box is a good example of where this would become useful. Ideally there shouldn't be a message box implementation in the lower-level framework at all. Instead, it should be implemented as a function that creates a bunch of basic widgets (like buttons and lv_obj
s) and puts them together with some event logic. However, not much should change from the user's perspective.
Thanks for the feedback!
@puzrin
This breaks separation of styles/templates
Isn't it happen like this in the CSS world too? You (or the framework) set basic appearance for element types and then you can fine-tune them with classes?
@embeddedt In more detail, the followings gave me the motivation to think about reducing the number of widgets:
It seems to me if a widget can be reimplemented with 15 lines of code I thinks it's overkill to have all the set/get functions, alignments and a lot of other features that are not required for a given case.
After all, a lot of people can use HTML/CSS and they need to build things mostly from divs and texts.
So I find the popup too special too:
Having API for all these is not reasonable and the user doesn't needs all these features at once. Why shall the other large, potentially slower, error-prone parts be there? For example to create a text + buttons message box from scratch:
lv_obj_t * popup = lv_obj_create(parent, NULL);
lv_obj_set_layout(popup, <column>);
lv_obj_set_size(popup, 200, <auto>);
lv_obj_t * label = lv_label_create(popup, NULL);
lv_label_set_text(label, "Hello");
lv_obj_t * btns = lv_btnmatrix_create(popup, NULL);
lv_btnmatrix_set_map(btns, <map>);
lv_obj_set_event_cb(btns, <event_cb>)
Ideally there shouldn't be a message box implementation in the lower-level framework at all. Instead, it should be implemented as a function that creates a bunch of basic widgets (like buttons and lv_objs) and puts them together with some event logic.
Having an official implementation needs a full API and people still ask why this or that is not supported. IMO a good set of examples is better because no API is required (just copy/paste and use as you need) and they are easy to extend by the user.
In addition other examples educate people about the usage of LVGL and examples can be added by the users as a contribution.
What do you think?
Dear all,
Personally I have had very little exposure to CSS/Flexbox/Grid related GUI development and have very little idea how it all works at the moment. I would have to go away and learn all of this if LVGL were to take this route only. I am not sure if a huge percentage of C programming embedded developers who have spent a lot of time working on limited resource platforms would necessarily have been exposed to web based development either... ( May be I am just getting old :blush: )
I do agree there is room to consolidate the code with there probably being a number of functions which could be merged to reduce code duplication. The API may also have commonality which could be merged such that one function could serve getting/setting of similar properties on multiple objects as opposed to many different object specific functions, but this is all very subjective, some people probably prefer object specific functions, but others may not...
Also please excuse my ignorance, but is it the intention, in order to create a screen/layout in LVGL in the future we will be writing CSS/HTML code and there will be a built-in parser to process the files and generate the screen or have I missed something here?
I think @embeddedt 's suggestion of a higher-level set of APIs on top is a good one, this could also be made an optional module? Maybe the CSS/Flexbox/Grid API could be an optional module also? This would allow people with web based skills to choose one development path and those with more of an embedded background the other, allowing both style of developers comfortable access to what is essentially an excellent graphics library.
IMHO, I think if everything is taken in the CSS/Flexbox/Grid ONLY direction there might be a danger of alienating a large block of developers.
Kind Regards,
Pete
@kisvegabor
Isn't it happen like this in the CSS world too? You (or the framework) set basic appearance for element types and then you can fine-tune them with classes?
No (usually).
They to not use cross-injections of data, except very special cases. For example table lines coloring is special case where nth-* can be convenient (especially when you add/remove lines on the fly). But for staic tables it's not a problem to assign required (eved/odd) classes directly.
Problem with your approach (picking "cool features" from spec), is that spec can not teach you "programming patterns".
@pete-pjb
Don't worry, nobody plan to force you write html/css. We are discussing absractions "how to organaze and manipulate data", and how to scrape approaches from html/css. So, we use known terms (from html/css) to better express thoughs.
@puzrin thank you for clearing that up! 😊
I wouldn't remove any widgets. What makes LVGL so great is that it has a big selection of widgets and makes it easy for people with little experience to use. I think we really just need to simplify the API so it makes it easier to configure the widgets. Another reason to keep the widgets is to remove duplication of work. Almost every app has modals to display pop up messages so why should every developer have to create their own implementation. I totally agree that the focus should be on letting the beginner use lvgl because there are plenty of GUI libs for more advanced users (imgui, nuklear, guilite, embedded wizard, ugfx, etc.). That is also why I started following this project. Even with years of JS and php experience most other libraries had too steep of a learning curve to be worth it.
I think right now the widget set is very good and if the API is simplified it really shouldn't be that hard to maintain. It will be work on the first implementation but later there will only be smaller changes. That is why getting a future proof API now is important.
I feel like it will also lead to more adoption. When searching online for people using lvgl there is very few tutorials beyond the basic getting started. By removing widgets it would be even harder for a beginner to make a decent app. An easier API will get more people to use LV
I am just starting to get into lvgl and really want to help contribute more in the future. With more contributors it will take some of the load off. I know you guys must be really swamped doing all the coding, support, bug fixes, the website, and upgrades with so few people.
We could make a package builder page and every widget that isn't a dependency for others can be added by checking a box and then downloading a custom package. That would quickly let you add or remove parts without having to dig through a ton of code. Once the gui builder is done the compile can do the same thing.
Changing the API so that it requires less typing and has shorter but more meaningful names is much more important. Even just changing lv_obj_set_style_local_value_str and lv_table_set_cell_value would save hours.
What does everyone else think about using structures for the API? Are there any reasons that speak against it? Pros Cons?
I do agree there is room to consolidate the code with there probably being a number of functions which could be merged to reduce code duplication. The API may also have commonality which could be merged such that one function could serve getting/setting of similar properties on multiple objects as opposed to many different object specific functions, but this is all very subjective, some people probably prefer object specific functions, but others may not...
Yes this was one of the things I noticed last week. Many widgets using different function names and methods for doing what is essentially the same thing. That is why I suggested a style definition similar to CSS but not directly CSS. Just that instead of writting set_label_text in one widget and set_btn_text in another you write set_text in both for example. In CSS child styles are added with a . like div.dark.btn which looks similar to how structures are called in C so using them to define styles would seem familiar to web devs AND allow for shorter API calls because you can abstract all your styles into a header file and then call create_widget(btn, btn1.red) to create a button with your button1 red style applied. Still needs work but you get the idea.
This would be what your code looks like and your styles go in a .LVSS ;)
create_widget(window, window1.red);
window_header(window1, winheader.2col);
create_widget(btn, head1, btn1.red);
window_div(window1, wincontent.content1 );
create_content(div1, content1);
The problem with frameworks is they cause code bloat. Each framework only contains part of the tools you need so someone creates another framework that contains the things they want and releases it. So people start using 4, 5, 6 frameworks that each contain the part of what they need and a bunch of stuff that they don't. Then you also need to learn 6 APIs instead of 1. JS and CSS has over 50 UI frameworks, JS has 6 and CSS has 3 compilers such as babel. The web dev ecosystem has become a joke because nobody made 1 complete tool kit.
To make a user interface you need a pretty standard set of components which right now LVGL has most of. I agree the message box could be created with a few lines by someone who knows their way around but since it is such a commonly used component I think it should stay. Maybe It could be combined with the window widget but some of the other widgets it is not as easy as with the message box.
I guess we could look if some widgets could be combined and put in the documentation how to create those widgets using others? My vote is against doing it for most of them.
Edit: I went over the widgets list and I see what you mean. A few could be combined to save on code but I think the actual widget implementations should still be there.
That reduces the 15 widgets to 6 and if the documentation shows the code for how to create them nothing is lost. A concern I have is that if you don't have the experience you might not think of the possibilities and you also don't want to turn LVGL into the hack fest that CSS was before they added the features we have now.
I see your point to keep commonly used widgets. My problem is complex widgets with not exact requirements will always lack features and their implementation and API will be huge E.g. a message box, tab view has tones of possible features, however, it's clear what we expect from a button or switch. So in the end e.g. message box will be very complex (regarding code and API) and still won't support every case.
Based on the discussion I propose this:
- message_box
- text_only
- with_image
- with header
- modal
- tabview
- simple
- no tabs
- left_tabs
- ...
A specific implementation would look like this:
void create_msgbox_with_image(lv_obj_t * parent, const void * img, const char * txt, lv_event_cb_t ok_event);
The advantages I see:
I think it's even easier to use for the users as they see some a simple function not a widget with a large API.
As it was already mentioned several times this repo can be a place for "community widgets" too and we can build an assets manager on top of it to easily download only specific components.
What do you think? Does it sound acceptable?
Regarding naming in LVGL: some names are really long due to prefixes. My approach with naming was to make them work well with autocompletion:
lv_
)label
)set_text
)So after typing lv_label
you see all the functions related to labels.
Of course, ideas are welcome in this topic as well.
@excitedbox
Many widgets using different function names and methods for doing what is essentially the same thing. That is why I suggested a style definition similar to CSS but not directly CSS.
In window_header(window1, winheader.2col);
you don't tell what to do with header. Add style? Remove style? Hide? etc.
It should be window_header_add_style(window1, winheader.2col)
which is quite similar to the current approach: lv_obj_add_style(window1, LV_WIN_PART_HEADER, &style);
Or is your suggestion about moving the part
parameter to the function name? E.g. lv_win_header_add_style(win, &style)
.
Real-life question from the forum: Add radio button widget. It's a basic, common widget, but eas to create from the check box.
My view is to have a new widget based on the checkbox because radio button is very common, the expected behavior is well defined, easy to create from the checkbox, and would be great to have its own default theme.
What do you think?
@kisvegabor regarding your plan to remove/replace many existing widgets - Do you really expect users to completely re-write their code after updating lvgl major version?
The outcome of that would be that most existing applications would not migrate to v8.
Completely new applications might use v8, but even that is not sure. If I already maintain an existing v7 application, would I create my next application on v8 where I need to create widgets completely differently? Most people would prefer to learn a single API and not have a different API for each of their applications.
I understand that major version can have breaking changes. v6 --> v7 migration was already painful with the style system change. I'm afraid that what you are considering here might just require too much from existing lvgl users.
Do you really expect users to completely re-write their code after updating lvgl major version?
We support major versions for at least 1 year. So it's not mandatory to update to the next major version. If v7 already works in your current application then you can stick with it.
The more I think about it the more I'm convinced that we need to make a conceptual change to make LVGL sustainable and manageable.
EDIT: Micropython( and the possible other bindings) need to considered carefully in this regard. Having a lot of examples in C would be great and IMO would solve some of our issues but it'd bad for Micropython users who see only that a lot of widgets are disappeared. So if we take this "higher level API" or "examples" approach we should provide either a suitable amount of Micropython components/examples or create a C->Micropython converter script.
Having a lot of examples in C would be great and IMO would solve some of our issues but it'd bad for Micropython users who see only that a lot of widgets are disappeared. So if we take this "higher level API" or "examples" approach we should provide either a suitable amount of Micropython components/examples or create a C->Micropython converter script.
It has the advantage of a narrower API and smaller binding. We already had complaints regarding binding size.
However, having a script to translate from C to Micropython is tricky. It would work for simple cases, but would break in a million different ways when doing anything non-trivial in C. The Micropython binding script we have today, which is already quite complex, only handles headers (function prototypes and structs). Translating the body of a function from C to Micropython is much more difficult if you want to do it properly.
Creating and maintaining higher level Micropython widgets manually would be tedious and I don't know who would want to maintain this over time.
A different approach could be to describe the higher level components in some declarative way with strict syntax (XML for example with schema validation) and automatically derive both C and Micropython from that description. It would achieve -
@kisvegabor regarding your plan to remove/replace many existing widgets - Do you really expect users to completely re-write their code after updating lvgl major version?
I don't think that is the case. If we combine widgets as stated above there will be less rewriting of code in case of a change. Since 15+ widgets will be built using the code of only 6 or so, when there is a change you will have to modify fewer functions (in case of custom behavior) and only update your style definitions. The chance of a change affecting a widget that you use becomes higher but the work becomes less when it does happen.
The plan is to create a WYSIWYG builder so we will need to create templates of ALL widgets anyway but again it will be less work only using 6 functions. The number of overall widgets available to the user would remain the same but use templates to create them. This would also remove a ton of code (Each widget uses 500-700 lines of code). I think it is close to 5k lines of code that would get removed.
We can still decide what to do with the widgets later. Once we have the API figured out it will be easier to make an informed decision.
CSS Selectors I somehow missed the part you wrote about selectors. It is a good idea for simplifying things on the frontend but would require a lot of work on the backend which makes it harder to add custom functionality to widgets. I think if we find a simple enough data structure and implement the selector and value style using a parser we would have the best of both worlds.
My previous suggestion was kind of a mix of the two. Defining styles as you would using selectors and value pairs and instancing using function calls with only 2-3 args to determine which style and actions are applied. A kind of selectors should be implemented though so we can use the same "tags" for multiple widgets (color instead of lv_btn_color and lv_txt_color).
when building an app you would create a style file that has ALL the styles and layouts of your widgets. Using structs and nested structures maybe?
Your style definition could look kind of like this WITHOUT parser:
struct mybtn1 {
str widget_type;
str parent;
str layout;
struct pressed = { //nested struct for defining sub styles such as states. if no state then uses default state
str bgcolor;
}
}
mybtn1. widget_type = btn; //Could also be a number ID but more to memorize for users
mybtn1.parent = window1;
mybtn1.layout = ("1:2,1"); //string interpreted as a grid style
mybtn1.pressed.bgcolor = green;
}
// widget type could also be passed on it's own making a style reusable. whatever makes more sense we will have to see. maybe both?
Sadly Structures cant be declared and defined at the same time so that doubles the work. Unless we change the way the styles are stored in them we have to decide if it is better to use a different method and loose the CSS style calls (btn.style.child). I don't like arrays because the definitions can cause mistakes and mismatches.
Your Main file looks like this but longer:
create widget(screen1, mycontainer, NULL); // This is a container such as page is right now
create_widget (mybtn1, NULL); // this creates your widget
event(onClick, mybtn1.pressed, mybtn1action(arg1)); //mybtn1action would be the name of the function that contains your event code.
each widget would only need a create call and an event call (event_type, widget, action). An event could also be used to add or update a widget style. Either inline by adding to the current struct or by changing which nested struct is called.
Your functions file contains your event functions
mybtn1action(filename){ // variable arg definitions can
file = savefile(filename);
}
The CORE functions are in another file:
create_widget (arg1, ...) { } // takes the arguments decides which widget to create and how it should look.
event (arg){ // takes arguments and decides which widget function has been executed and how to respond
SWITCH
CASE on click
on_click (arg){ )
CASE on drag
on_drag() (arg){ )
CASE on drop
on_ drop() (arg){ )
}
This would abstract everything into 3 files and the user only needs to touch 2 of them (style and function) as long as he doesn't need custom behavior.
1 thing I recommend not doing is using ID numbers for any widgets or widget types/parts. As it is right now every time I have to use part or type I have to look it up and sometimes try multiple ones to see which one is the right one. It is more to remember and in some cases it doesn't save anything when prefaced with type. nobody can remember what widget number 12 is when it is 3 am.
Webkit is written using classes for rendering the CSS so that gives them a clean way to organize nodes (Selectors)
Micropython can mostly use the same design language if you implement your css parser prototype and it will get converted to whatever data structure we decide on. For adding custom behavior we need to make sure that we simplify the current API to work with Micropython. My suggestion above seems to do that just fine. An event(event_type, widget, action) call can be used to execute python functions since the only thing LVGL is being passed is the name of the function to execute.
What about creating our own data structure such as a list or node tree? We would need a small function set for add, edit, remove, get, list, sort. Then we can use whatever is the most efficient data structure to store it without having to care what it looks like because people would only need to work with the 6 or 7 function and the arguments to pass their data. Something like Clist or list.h
A different approach could be to describe the higher level components in some declarative way with strict syntax (XML for example with schema validation) and automatically derive both C and Micropython from that description.
I'm not sure this is a good idea. While it makes sense from a core developer's perspective, it adds a barrier to entry for new contributors.
I think the most important thing here, as @amirgon mentioned, is to keep the public API as similar as possible. That way we minimize the amount of negative change the end-user sees. Ideally they should only see improvements and new features, and not the removal of existing things.
IMO, building tooling on top of the API to make it more accessible is preferable to revamping the API (that's where things like lv_css
and the WYSIWYG builder would come in handy).
If we want to restructure things like the message box and move them into lv_examples
, I think that's fine, as long as the user interacts with it in the same way (we can probably create an lv_msgbox
compatibility module that will cover the majority of the old code). API renames are fairly easy to deal with. What users don't like dealing with are core architectural changes.
Another thing I'd like to point out is that the current plan means that we're now going to have released 3 major versions in a row where some set of APIs has been revamped:
That's great from a technology standpoint. LVGL is clearly evolving to meet new demands. We've definitely come a long way in the 2 years I've been part of the project. What I'm concerned about here is how users see it. I feel like API changes look nice on the surface, but we end up with a lot of users choosing to stick with 5.x and 6.x versions for old projects because they don't have the time to upgrade and rewrite their code. I've finally noticed a drop in the number of 5.x-related questions, but there are still lots of people using 6.x.
One idea is to create revamped, external widgets in v8 but keep the legacy widget implementations in-tree until v9. We could mark them as deprecated and only apply bugfixes (no new features). That would extend the total support time to 2 years - I think that's more than enough. It would also promote more rapid adoption of v8. In fact, doing it that way means that the change doesn't even have to be in a major release.
Sorry for the wall of text; I guess that's what happens when I don't check my email early enough. :laughing:
I think we are all pretty much in agreement. Our opinions only differ on small details. The reason I think an API change should come sooner is to avoid adding more things that need to be changed over later. It also makes adding new features quicker speeding development. Another reason is that the adoption is still relatively small. If we wait till the project has more users we risk pissing more people off with the changes. LVGL just made the big jump so I think it is best to make the change now. Overall the changes I am suggesting are mostly going to affect the naming to shorten function calls. If people want to put their styles in the main code that would still work. The parsing can be built on top of that later and would be totally optional as well.
In the end people would have 4 options. Messy code all in 1 place. Clean code with 3 include statements at the top. Using the Style language and parser. And beginners can use the WYSIWYG builder.
I think that you are right about not removing things from the users perspective which is why I suggested the combining alternative. I think what is there now plus the radio group makes up a core set of widgets that should be the default either as full widget or just as a styled template. And anything additional can be added as a plugin. A radio group is one of the few big input from controls so I would suggest making it as a group object instead of hacking together some logic on top of the checkbox.
A different approach could be to describe the higher level components in some declarative way with strict syntax (XML for example with schema validation) and automatically derive both C and Micropython from that description.
Ideally, we shouldn't need to use tricks (such as function pointers, structure definitions, etc) in the examples. Theoretically, every basic C "feature" (loops, if, variable and function declaration, function call) has its micropython counterpart. however, it's still really tough to write a parser to it.
I agree with @embeddedt regarding the XML-like description "adds a barrier to entry for new contributors". Having an XML parser where we can describe events too is also not that trivial because it needs to handle function calls, variables, arithmetic operations, etc.
One idea is to create revamped, external widgets in v8 but keep the legacy widget implementations in-tree until v9. We could mark them as deprecated and only apply bug fixes (no new features). That would extend the total support time to 2 years - I think that's more than enough.
That sounds very reasonable. As every old feature (layout, page widgets, etc) will be kept until v9, the "old" widgets should work in v8 without much modification.
@excitedbox This complete API revamp seems like a huge topic. Could you open a new issue for it and summarize your idea?
ok. Give me a few days to try and work out a better solution than what I have so far. I was just looking at the core and noticed that we can trim a lot of duplicate code there as well.
void lv_arc_set_bg_angles(lv_obj_t * arc, uint16_t start, uint16_t end)
void lv_arc_set_angles(lv_obj_t * arc, uint16_t start, uint16_t end)
both use the same code for the entire function. By adding an argument for bg or fg you would only need 1 function to draw both saving ~30 lines. This applies to some other widgets and their getter/setter functions as well. I am going to investigate how widespread it is and if it is worth changing once we decide which widgets to keep.
@excitedbox Great, thank you!
Probably something like Document.querySelectorAll() needed.
Reasons.
Complicated component (popup dialog) is: layout
+ styles
+ controller
. Now it's difficult to modify such things. But, widget could accept layout
+ styles
("view template") and say "controller should sit on nodes, marked with known classes [idenifiers]".
@kisvegabor splitting widget to [ "view template" + controller ] may help to customise widgets without zillions of options.
In addition to previous, events delegation would be convenient sometime https://medium.com/@bretdoucette/part-4-what-is-event-delegation-in-javascript-f5c8c0de2983.
In context of C that means:
a) bubbling b) way to filter OR validate event source (because miltiple nodes can send "click", but concrete listener needs only specific ones)
Yes that is kind of what I have in mind. Just need to figure out how to best implement it. I looked at a bunch of other GUI frameworks for inspiration and it seems Microsoft has one that is crazy small. Using under 14kb of flash and 4KB of ram plus what you need for canvas. But it runs on top of RTOS so I imagine that they already have a bunch of functions for handling events.
In Case anyone would like to take a look. The source is also available on github. https://docs.microsoft.com/en-us/azure/rtos/guix/overview-guix
@puzrin
Probably something like Document.querySelectorAll() needed.
So use e.g. Document.querySelectorAll("btn .red")
which gives a list to all buttons with red
style and then you can do with objects whatever you want. (E.g. add/remove styles).
If so, it sounds good and simple. We have great freedom on the filter
parameter and can do a lot of interesting things.
bubbling It works if you set it explicitly by
lv_obj_set_event_parant()
to delegate the events to the parent. "Bubble" might be a more common name though.
You can filter events manually with if(e != LV_EVENT_CLICK) return;
@excitedbox
Using under 14kb of flash and 4KB of ram plus what you need for canvas.
Wow, that's a great one. The example was a little bit hard to follow but it indeed has a lot of features.
See for example how a drop_list is created. It takes a bunch of arguments in the create function and there is no a lot of set/get functions (which provides a lot of freedom and causes a lot of bugs).
You can filter events manually with
if(e != LV_EVENT_CLICK) return;
Common use is "subscribe to clicks only from objects with selector .red". Test event type only is not enough. Need verify source type and "tags" (assigned class names in html).
Using under 14kb of flash and 4KB of ram plus what you need for canvas.
I think the figure they're giving is a bit misleading though. After their overview page makes that claim, it then goes on to say that fonts, images, and strings are not being counted as part of that size. So, a real-world application would probably use a substantially larger amount of Flash than what they're suggesting there.
Need verify source type and "tags" (assigned class names in html).
In our case we could probably have a reasonably efficient way of checking if a certain lv_style_t
is in an object's style list.
yes their Api calls are even longer than LV but their data structure must be pretty good. They are not counting the canvas and color uses 2-4 bytes per pixel. So a 620x480 lcd uses another ~25kb for that. Would still be interesting to run a test.
I have 2 or 3 examples with shorter API calls. I will present my findings in a few days with a few suggestions and then you can decide what is best. I am researching complex data structures and reading guides on writing user friendly APIs right now.
By the way ST bought TouchGFX from Zendesk it seems and wiped the github repo 2 months ago. It is still available for STM32 users. That one uses 10KB ram and 20KB flash minimum.
Microsoft must also just have bought GUIX because I just went to the ThreadX website and all the microsoft stuff wasn't there 4 months ago when I first looked at it. They were charging sever thousand though just for the gui library and non of the extra modules.
Kind of sad that any time one of these Open Source projects gets big it is sold to some big company and they ruin it for profit. If you help donate code for QT you can actually end up having to pay for a license to use your own code.
@excitedbox TouchGFX used to be open source? Even before ST bought it, I thought one had to pay in order to use it.
@puzrin
Common use is "subscribe to clicks only from objects with selector .red". Test event type only is not enough. Need verify source type and "tags" (assigned class names in html).
It also could be tested in the event function, right? if(e == LV_EVENT_CLICK && lv_obj_has_style(btn, &style_red)
.
In the name of simplification, I think we can leave this complex cases to the user.
@embeddedt
I think the figure they're giving is a bit misleading though.
I also don't know if the unused functions were dropped during linking or not? Having an empty app with only one widget type can be really small compared to one where a lot of features are used (and therefore linked).
TouchGFX used to be open source? Even before ST bought it, I thought one had to pay in order to use it.
It was and it is closed. Now you can include it as a library file.
@excitedbox
So a 620x480 lcd uses another ~25kb for that. Would still be interesting to run a test.
620 x 480x 2 = 595 kB. This part is not clear in GUIX though. They say the size of the canvas is not counted. So how they use e.g. an ILI9341 chip with a common STM32F4?
(LVGL solves it by refreshing in chunks into a smaller display buffer)
By the way ST bought TouchGFX from Zendesk it seems and wiped the github repo 2 months ago. It is still available for STM32 users. That one uses 10KB ram and 20KB flash minimum.
As far as I know, TouchGFX has a very primitive drawing engine. It mainly images rendering a pixel manipulation on the user level. (Correct me if I'm wrong) LVGL has anti-aliasing (rectangles and lines), rounded rectangle, border, shadow, gradient, masking etc.
As I saw GUIX has only anti-aliased lines and simple hardcoded style values. E.g. GX_STYLE_BORDER_THIN
and GX_STYLE_BORDER_THICK
.
I'm thinking about the consequences of dropping complex widgets and found these:
typedef struct
{
lv_obj_t obj;
bool state;
lv_style_list_t knob_style;
lv_style_list_t indic_style;
} lv_sw_t;
lv_obj
. However, it's not so reasonable to have these features for the really basic widgets (as listed here) which:
So in light of that, it seems logical to have a <div>
-like object where layout and scrolling works.
What do you think?
As far as I know, TouchGFX has a very primitive drawing engine. It mainly images rendering a pixel manipulation on the user level.
Yes; the demo which showcases the library features uses a bunch of premade images. I think they're exploiting the fact that the STM32 chips have DMA2D hardware that can copy them to the framebuffer quickly. That way they can claim very low CPU usage and very high frame rates.
That approach works well for MCUs with lots of flash where everything is generated on the PC and then a simple engine is used to render them. It probably saves quite a bit of RAM. The downside, of course, is that this depends on flash that has acceptable read speeds and hardware-accelerated copies to RAM. LVGL has the advantage that it works everywhere and lets you customize a lot more without needing to "bake" images into your product.
Adding the library prefix is known as smurf naming or smurf coding (because the smurfs always added smurf to every word) and is frowned upon because it causes really long names. I agree it can be useful in C because you don't have namespaces to prevent collisions but you do have scope.
The API and the code don't have to interact and scope can keep things from colliding. for instance if you have a create_widget() function and pass in the widget type and array of properties (style, state, events, actions) you can keep names short. In the create function you use a switch to call your longer unique draw functions and fill in the arguments with data from the property array. This way you put distance between the user and the core code of the library. There can't be collisions because everything is stored as a value/pointer and the draw functions are all inside another scope.
This will all be explained more clearly in my API post.
@embeddedt
Yes; the demo which showcases the library features uses a bunch of premade images.
When I saw this demo I was thinking about how to effectively real time render screenshots from other screens and apply perspective distortion. Aaaand it's an image
That way they can claim very low CPU usage and very high frame rates.
I made some comparison a few months ago and found that LVGL is faster for image rendering even without DMA2D because LVGL renders into internal RAM first (a lot of access per pixel) and writes ext RAM only once in flush
.
@excitedbox
This will all be explained more clearly in my API post.
Okay, let's discuss it in a separate issue. :slightly_smiling_face:
Hi,
Scrolling and the new layout is almost fully working (just minor polishing is required) so I started to think about the "widget to external component" question again. Based on the thoughts so far here is my proposal about each widget.
So I suggest keeping "only" 19 types (+2 new) from the 33 widget types, and removed or replaced with a component example 14 types.
Keep as it is
Remove and replace with component (11 pcs)
Remove without replacement:
Meld into other objects:
New objects:
I imagine the component examples as an lv_components
repo with folders like list
, led
, tabview
, etc.
E.g. in list
we can have different lists such as
list_simple
: just texts below each otherlist_complex
: list element with complex layoutlist_horizontal
: horizontal list from textslist_key_nav
: list controlled by keys I'm very curious the hear your opinion about it!
So I suggest keeping "only" 19 types (+2 new) from the 33 widget types, and removed or replaced with a component example 14 types
@kisvegabor I still would like to ask for some clarifications about this change.
From a user point of view, if I want a simple tabview like we have today, what do I need to do? Learn an example and write my own? Or will there still be some public external component API that could save me this effort?
Regarding Micropython, do you plan to translate these non-core component examples into Micropython as well? And other bindings in the future?
Or do you still consider a script to translate C into Micropython? Or do you expect the Micropython user to translate examples by his own from C to Micropython for each missing component?
@amirgon Great question. First of all, I'd like to briefly summarize the motivation behind this change to see why I think it's important to make such a not user-friendly decision.
There is a lot of question and request about why a widget does not support this and that. Or in a special case, some feature is not working properly. And it's true. We can't support every possible type of list
s, msgbox
s , win
s, etc. and all the combination of features, and keep them small and bug-free. So the idea is to have a small set of "core" widgets (the basic building blocks) that have a well-defined feature set. The complex widgets should be rather examples instead of having one good-for-everything widget. The example doesn't need to have a really sophisticated API. Just a really simple one to do what it was designed for. The examples should be very simple so a C user can easily even copy or modify them.
But in Micropython I see two ways:
list
:
lv_obj_t * list = lv_list_create(parent);
lv_obj_t * e1 = lv_list_add(list, "Element 1");
lv_obj_set_event_cb(e1, event1);
And that's all. No change to horizontal mode, no set_anim_time
, no scroll bar mode, just a simple list.
Fo a special list element, e.g. an element with a switch on the right, there can be just this function:
lv_obj_t * e2 = lv_list_add_sw(list, "Element 2", sw_event_cb);
Or for the keyboard just
lv_obj_t * kb = lv_keyboard_craete(parent);
lv_keyboard_set_text_area(kb, ta); //Write to this text area
No map change, no mode (num/text) change, just a simple keyboard. For numpad input the can be an lv_numpad
component.
Tabview:
lv_obj_t * tv = lv_tabview_create(parent, TOP, width, height, header_height);
lv_obj_t * tab1 = lv_tabview_add_tab(tv, "Name")
...
lv_obj_t * tab2 = lv_tabview_get_tab(tv, 1);
No rearrangement if size changes, no runtime tab repositioning (top/bottom/left/right), no tab rename, no moving indicator. Of course, there can be another component that extends the default tab view with a fancy indicator.
Message box (just examples not real suggestion):
lv_obj_t * mb1 = lv_msgbox_create_txt(parent, "Hello");
lv_obj_t * mb2 = lv_msgbox_create_txt_btn(parent, "Hello", "Ok", ok_event_cb);
lv_obj_t * mb3 = lv_msgbox_create_complex(parent, "Title", close_event_cb, "Hello", &img, "Ok", ok_event_cb);
So basically we can recreate all the "dropped" widgets in a more simple form.
Finally, I believe this structure has a lot of advantages:
What do we lose? I think almost nothing. Some widgets are moved to another repo which might be a little bit inconvenient. Finally all (reasonable) lost features should have a component example or a way to reproduce a feature with a few lines of straightforward custom code.
I think all existing 7.x widget APIs should remain, even if they're implemented by a separate module built on top of the examples. Otherwise, users will be reluctant to upgrade to 8.x, as it will give them the task of rewriting code that depends on the old widgets.
I'd like to briefly summarize the motivation behind this change to see why I think it's important to make such a not user-friendly decision.
@kisvegabor - I understand the motivation, and in general I think it is a good direction.
It has another advantage specifically for Micropython - program size.
Since in Micropython everything is dynamic, we need to compile all components and wrap each of their functions so they would be available for the user.
Removing components and making the API smaller in general would also help reducing program size, which is already a problem on the Micropython bindings.
The complex widgets should be rather examples instead of having one good-for-everything widget. ... Of course, there can be another component that extends the default tab view with a fancy indicator.
Why do you want to make them "examples" and not "base classes" that can be extended by the user?
Personally I strongly prefer "base classes" over "examples".
"Base classes" would prevent code duplication and make the code much easier to maintain.
Technically it's possible to use a C component as a base class of a Micropython object and allow overriding and extending functionality, but we would need to craft the C API carefully to allow it.
- Create (some) examples in Micropython too. But the questions of course are the effort of implementation, maintenance, etc. I think the C-Micropython converter is too complex and uncertain.
I agree about the C-Micropython converter.
The only way this could work is that when someone pushes a change to an example, the same person would be required to push the same change to the Micropython version.
This can be enforced by some CI automation that runs tests on both C and Micropython and verifies they yield the same results.
2. Use the existing binding generator on these components too. Se the components should have a minimal API.
This is possible but has some disadvantages:
I think all existing 7.x widget APIs should remain, even if they're implemented by a separate module built on top of the examples. Otherwise, users will be reluctant to upgrade to 8.x, as it will give them the task of rewriting code that depends on the old widgets.
@embeddedt I completely agree. This would dramatically ease the migration from v7 to v8 for existing code.
However, it would not solve the problem in the long term for new components and new features that would only be designed with the new paradigm.
I'm looking for a way to make this change friendly for both C and Micropython users, for new v8 components.
"Base class" means that this is a public API with basic functionality. If I want to change or enhance this functionality I don't need to copy/paste/change code. Instead, I inherit from a base component and override or extend the functionality I need.
In the practice, I suppose it means adding custom signal and event processing to objects. To make it user friendly it'd be nice to allow more signal, design, and event callbacks to an object - one for the basic functionality, and one for extension(s). It'd help to avoid manually calling ancestor's functions. The API could look like this:
lv_obj_add_signal_cb(obj, func);
lv_obj_add_event_cb(obj, func);
lv_obj_add_design_cb(obj, func);
lv_obj_add_signal_cb_front(obj, func); //Front to make it the first in the list (called first)
lv_obj_add_event_cb_front(obj, func);
lv_obj_add_design_cb_front(obj, func);
This way, in the tabview indicator example, just an extra event is needed on the value change event, where the indicator is moved. However, if an MP user wants a different indicator effect, he still needs to craft it from scratch. A C user can copy-paste the indicator's code and slightly modify it.
Program size - The advantage I mentioned earlier about reduced code size with this change - goes away. We would need to compile the C code and bindings for every such example to make them available for the Micropython user.
With an lv_component_conf.h
every component could be enabled, disabled.
I think all existing 7.x widget APIs should remain, even if they're implemented by a separate module built on top of the examples. Otherwise, users will be reluctant to upgrade to 8.x, as it will give them the task of rewriting code that depends on the old widgets.
Personally, I see no problem with dropping those widgets and provide replacements as components. However, if you all vote for keeping them in v8, I'm convincible. :slightly_smiling_face:
However, I need to note that, I think it will lead to a quite messy codebase:
roller
in lvgl
which uses the page
and another roller
in components that use the new scrolling.Finally, people won't be able to use the new features in v8.x because most probably they rely on v8 features. So if they keep the old v7 widgets they can't use the power of v8. Then why update to v8? We support v7 for 1 year.
I see more reasonable to add a longer period (e.g. 3 months) when both v7 and v8 are actively developed and maintained instead of mixing the 2 things.
I think it will lead to a quite messy codebase.
I agree. For that reason, I think that we should have a separate module which "fakes" v7 compatibility using the new APIs. If necessary, we can have some duplication between the examples and the compatibility module (e.g. it might be better to re-use the v7 list than to make a new one that dynamically chooses between the list examples).
I think it's important to have a clean and simple migration path before dropping support for v7. Maybe the approach I listed below would be a good combination of our thoughts.
feat/new-scroll
.feat/new-scroll
into dev
, and stop accepting new 7.x features (only bugfixes). We will have two layout system which should NOT be mixed.
lv_cont
seems like a good component for the compatibility module; it doesn't really depend on any object feaures besides the ability to set coordinates.
For that reason, I think that we should have a separate module which "fakes" v7 compatibility using the new APIs. If necessary, we can have some duplication between the examples and the compatibility module (e.g. it might be better to re-use the v7 list than to make a new one that dynamically chooses between the list examples).
The dropped widgets need to be rewritten anyway mainly because of the dropped drag features. E.g. list
shouldn't be based on page
but a base object.
I think it's important to have a clean and simple migration path before dropping support for v7. Maybe the approach I listed below would be a good combination of our thoughts.
I agree with the list. The only thing I'd change is creating a dev/v8
branch now and an rc/v8
later. feat/new-scroll
is not really about scrolling only anymore.
Hi, I have some features and changes in mind that I'd like to discuss to see which are viable.
Style states
Now every style property is related to a state. In CSS every class can have a state (state ==
pseudo-class
).This concept in LVGL gives more flexibility but
The advantages are:
red_btn
,red_btn_pr
,red_btn_focus
,red_btn_focus_pr
CSS selectors
If we moved states to style level from property level we would have more space (in properties every bit matters as there are a lot of property) to add advanced stuff.
nth-child
would be huge. It could be used to style table or button matrix cells. Do you other selectors which can be interesting for us?Copy paramter in create functions
The copy parameter in the create functions exists from the beginning of LVGL but now I'm not sure if it's really required. Disadvantages:
Please share what you think about these topics.