Open fstuff-dev opened 2 years ago
Sounds good. Let me know if you have any LVGL related questions! :slightly_smiling_face:
Sounds good. Let me know if you have any LVGL related questions!
Yep. ! i have a little question about layouts ! Can the function defined in lv_flex/lv_grid be applied to any obj ? if so in your opinion can be integrated in the LvObj Class ? The same for style functions that are implemented in lv_flex/lv_grid that works with style ? can be part of LvStyle Class ?
Thanks
Can the function defined in lv_flex/lv_grid be applied to any obj ?
Yes, they can.
if so in your opinion can be integrated in the LvObj Class ?
IMO if a function starts with lv_obj_*
it can be integrated into the LvObj
class. Similarly, lv_style_*
function can go to the LvStyle
class.
Great ! 👍
@kisvegabor You can view some example on how to use Flex on lv_cpp_example in the repository. https://github.com/lvgl/lv_binding_cpp/blob/master/lv_cpp_examples/FlexEx.cpp This use also the standard "new" for create objects. :+1:
Hi @fstuff-dev ,
Are you planning at some point to generate the C++ API automatically from the C API? Or are you already doing this? Otherwise, any future change to LVGL would require manual fix to lv_binding_cpp
Hi, I already use a python script to automatically generate cpp API from C code ! It's in a early stage but it works ...
I already use a python script to automatically generate cpp API from C code !
That's great! I didn't find the script in the repo, maybe I missed it?
Do you plan to run the script as part of your build process?
This could be useful if someone wants to use a different/modified version of LVGL, other than the one you converted and submitted.
As I said the script is in a really early stage, so at the moment i don't like to make it public ! In the near future I will clean and make it usable at user prospective, so i will make public 😊
@amirgon If you want to see the script you can find at https://github.com/fstuff-dev/lv_binding_cpp_generator/tree/earlystage . As the link says is "earlystage" so don't use it. Some Class in /core folder are added manually !. As i said i will write a clean script and complete script that i will upload in the repo ! :+1:
As i said i will write a clean script and complete script that i will upload in the repo !
Ok, great!
On the long run I think it would make sense to generate the code as part of the build process, instead of comitting generated code into the repo. What do you think?
I think that is the best choice !
A few questions:
lv_obj
by calling lv_event_get_target
, but how do you get the CPP object from that?LvObj
, what would happen if the underlying lv_obj
is deleted either directly or indirectly (when its parent is deleted)? A few questions:
- In an event callback, how do you get the CPP object from the event?
You can get
lv_obj
by callinglv_event_get_target
, but how do you get the CPP object from that?
The library sacrifice the "user_data" to make C and CPP object match. But if someone have better idea will be appreciated.
- How do you handle object lifetime?
Suppose I inherit from
LvObj
, what would happen if the underlyinglv_obj
is deleted either directly or indirectly (when its parent is deleted)?
That's one of the problem to resolve !
The library sacrifice the "user_data" to make C and CPP object match. But if someone have better idea will be appreciated.
No, that make sense. We use user_data
on the Micropython bindings for the same purpose.
Do you save the CPP object into user_data
automatically? Do you provide some standard way to convert user_data
back to the correct CPP object or is the user supposed to do the casting? Do you verify that the casting is correct? (casted to the right type of CPP object)
Do you save the CPP object into
user_data
automatically?
The matching between LvObj
and user_data
is done automatically inside derivated object constructor. For example:
LvBtn::LvBtn(LvObj* Parent) : LvObj(Parent) {
if(Parent)
cObj.reset(lv_btn_create(Parent->raw()));
else
cObj.reset(lv_btn_create(lv_scr_act()));
setUserData(this); // Matching the C and Cpp object
}
i don't know if using LvEvent
and Cpp event dispatcher is the best choice or simply keep the regular callback method using function pointers. You can use standard lvgl callback system passing static Cpp method. For example:
static int pressed = 0;
/* Callback for button pressed */
static void ButtonPressedAdd(lv_event_t *e) {
pressed++;
}
int main() {
LvBtn* btn = new LvBtn();
LvLabel label = new LvLabel(btn);
btn->addEventCb(ButtonPressedAdd, LV_EVENT_PRESSED, label);
while(1) {
....
}
}
Do you provide some standard way to convert
user_data
back to the correct CPP object or is the user supposed to do the casting? Do you verify that the casting is correct? (casted to the right type of CPP object)
No at moment, but user can check the type using <typeinfo>
provided with C++11. Or we can implement some helper function for check the type to simplify the usage of <typeinfo>
i don't know if using LvEvent and Cpp event dispatcher is the best choice or simply keep the regular callback method using function pointers. You can use standard lvgl callback system passing static Cpp method. For example:
It looks good to me. It mimics the LVGL's API, so the users can intuitively know how to add/create events.
i don't know if using
LvEvent
and Cpp event dispatcher is the best choice or simply keep the regular callback method using function pointers. You can use standard lvgl callback system passing static Cpp method.
If I want to use my non static member functions of my class as callback functions it would become a bit more cumbersome and require some casting boilerplate.
Maybe worth adding an example of using LvEvent and event dispatcher to make it clear how they can be used (or did I miss an already existing example?)
Yes I can do it ! I've removed the example some time ago but i will upload and I'll give some other examples !
If I want to use my non static member functions of my class as callback functions it would become a bit more cumbersome and require some casting boilerplate.
In relation to object member function callback have look at this.
No at moment, but user can check the type using
<typeinfo>
provided with C++11. Or we can implement some helper function for check the type to simplify the usage of<typeinfo>
Header <typeinfo>
is not available for embedded systems! Technically you can enable it however it will use most of your flash memory same as exceptions.
Regarding casting to lv_obj_t*
have you considered operator overloading? For example:
// in object class:
operator lv_obj_t*() const {
return raw();
}
// this allows you to dereference pointer and pass it automatically to functions which requires it.
LvBtn::LvBtn(LvObj* Parent) : LvObj(Parent) {
cObj.reset(Parent ? *Parent : lv_scr_act());
}
// or any other lvgl function
auto btn = new LvButton();
auto lbl = lv_label_create(*btn);
And have you thought about templates?
// I have class basic_object which contains most of the object methods, every widget inherits from it.
template <typename Derived>
class basic_object {
public:
using type = std::remove_cvref_t<Derived>; // this ensures we have plain class.
basic_object(lv_obj_t* parent = nullptr) : m_obj(parent ? parent : lv_scr_act()) { lv_obj_set_user_data(m_obj, this); }
operator lv_obj_t*() const { return m_obj; }
operator type&() { return reinterpret_cast<type&>(*this); }
// every void function is implemented like:
type& set_x(lv_coord_t x) {
lv_obj_set_x(*this, x);
return *this;
}
type& set_size(lv_coord_t w, lv_coord_t h) {
lv_obj_set_size(*this, w, h);
return *this;
}
};
// and wiget inherits from it like:
class label : public basic_object<label> {
public:
label(lv_obj_t* parent = nullptr) : basic_object(lv_label_create(parent?parent:lv_scr_act()) {}
template<typename... Args>
label& set_text(const char* fmt, Args&&... args) {
lv_label_set_text_fmt(*this, fmt, std::forward(args)...);
return *this;
}
label& set_text(const char* txt) {
lv_label_set_text(*this,txt);
return *this;
}
};
it allows to chain object members and correct derived class is returned.
static auto lbl = label().set_x(100).set_text("hello");
If in object i would return basic_object&
i could not use set_text
aftrer set_x
.
If I want to use my non static member functions of my class as callback functions it would become a bit more cumbersome and require some casting boilerplate.
In relation to object member function callback have look at this.
No at moment, but user can check the type using
<typeinfo>
provided with C++11. Or we can implement some helper function for check the type to simplify the usage of<typeinfo>
Header
<typeinfo>
is not available for embedded systems! Technically you can enable it however it will use most of your flash memory same as exceptions.
That's right !
Regarding casting to
lv_obj_t*
have you considered operator overloading? For example:// in object class: operator lv_obj_t*() const { return raw(); } // this allows you to dereference pointer and pass it automatically to functions which requires it. LvBtn::LvBtn(LvObj* Parent) : LvObj(Parent) { cObj.reset(Parent ? *Parent : lv_scr_act()); } // or any other lvgl function auto btn = new LvButton(); auto lbl = lv_label_create(*btn);
Nice and really useful !
And have you thought about templates?
// I have class basic_object which contains most of the object methods, every widget inherits from it. template <typename Derived> class basic_object { public: using type = std::remove_cvref_t<Derived>; // this ensures we have plain class. basic_object(lv_obj_t* parent = nullptr) : m_obj(parent ? parent : lv_scr_act()) { lv_obj_set_user_data(m_obj, this); } operator lv_obj_t*() const { return m_obj; } operator type&() { return reinterpret_cast<type&>(*this); } // every void function is implemented like: type& set_x(lv_coord_t x) { lv_obj_set_x(*this, x); return *this; } type& set_size(lv_coord_t w, lv_coord_t h) { lv_obj_set_size(*this, w, h); return *this; } }; // and wiget inherits from it like: class label : public basic_object<label> { public: label(lv_obj_t* parent = nullptr) : basic_object(lv_label_create(parent?parent:lv_scr_act()) {} template<typename... Args> label& set_text(const char* fmt, Args&&... args) { lv_label_set_text_fmt(*this, fmt, std::forward(args)...); return *this; } label& set_text(const char* txt) { lv_label_set_text(*this,txt); return *this; } };
it allows to chain object members and correct derived class is returned.
static auto lbl = label().set_x(100).set_text("hello");
If in object i would return
basic_object&
i could not useset_text
aftrerset_x
.
Sounds good and more clean for returning Obj reference !!!!
@maciekr1234 At moment i'm working on implementing your solution in the automatic script generator ! I think that your ideas are really good ! nice work !
I can't understand one thing.
auto btn = new LvButton();
Why do we have to do this allocation through new operator? I guess, it should be enough to use the simple allocated object.
Object should be created after lv_init() call !
I can't understand one thing.
auto btn = new LvButton();
Why do we have to do this allocation through new operator? I guess, it should be enough to use the simple allocated object.
I just used example following context of previous post. I actually use flag in object instance to indicate if it's an owner of object or not. in the destructor I check the flag and if it owns an object lv_obj_delete is called. because the actual object is allocated by c api, the cpp is used as convenience api and memory is managed by lvgl.
I jave opened a repo with the generator https://github.com/fstuff-dev/lv_binding_cpp_generator let me know… This is more clean and usable as my first script !
I really appreciate your work but given that you're currently pretty much writing everything yourself and how the interest (compared to the original discussion) has flattened out I'm afraid that everything but fully automated code generation is a dead end. Personally I'd already be happy to have the simplest possible form of bindings where every C function has a class equivalent which simply forwards it's arguments. No additional memory management, no smart objects, nothing. (so basically syntactic sugar)
Have you thought about using Clangs LibTooling for handling the original LVGL code?
Yep ... atm i'm a little bit busy and i can't spend much time in automatic script ! We can discuss in simple solution !
I'll try to find the way of auto-generating the wrapper with using libclangd+ annotations of the LVGL code. I guess, it can be more flexible.
For the current moment, we can provide a small set-of-tips article for using LVGL in the C++ environment. @kisvegabor what do you think about this?
For the current moment, we can provide a small set-of-tips article for using LVGL in the C++ environment
For example a C++ section here? What could we mention in such an article?
@kisvegabor yes, exactly. We can mention:
Any other propositions?
As for me, it's cool to have either a set of utilities or a auto-generated wrapper. For the first step, we can just add a sections of C++-related tooltips.
Sounds good! Do you have time to send a PR with it?
@kisvegabor I'll try to find it, really. I guess, it will be better, as a first step of the further C++ integration
Great, thanks!
I guess, it will be better, as a first step of the further C++ integration
I agree, it should be only a few hours of work but still very useful.
Is this still a goal? Is it OK to start using for new projects and hopefully it will be stable in the future? Or is there another effort tracking this down? I have just started in the lvgl world and would like to use c++ if possible.
This project seems stale a little bit, but it'd be great to boost it.
You might have heard of our new Sponsor opportunity. The C++ binding can be a good target for it. @embeddedt what do you think?
I agree.
Great! I'd be happy to give 400 USD for a C++ binding which is
If it makes sense for you too I'll open an issue for it.
Hello there! Just in case I did release one a while ago there: https://github.com/vpaeder/lvglpp I have a python code for autogeneration but it needs more work and cleanup.
@vpaeder Very nice!
I have a python code for autogeneration but it needs more work and cleanup.
Do you plan to add that Python autogeneration code to the repo?
Do you plan to auto-generate the C++ wrapper as a build step, so the users could switch LVGL version and build C++ wrapper for it?
Do you plan to add that Python autogeneration code to the repo?
Likely after I clean it up and document it.
Do you plan to auto-generate the C++ wrapper as a build step, so the users could switch LVGL version and build C++ wrapper for it?
So far I used it as an aid to avoid having to wrap all the styles and widgets by hand, not really in that sense. In theory it's of course possible, in practice it'll depend on how the workarounds for things like events, timers and animations behave.
Amazing! I think this is exactly what we need. OOP like API and many examples.
I'd be happy to pay the 400 USD if you finalized it and we could bring it into the LVGL organization. cc @embeddedt
@Sxs7513 To avoid duplication do you think this C++ binding could be used in the hearth of your JS binding?
Alright, I'll try to squeeze in some tidying up in the coming days.
Great! :slightly_smiling_face:
IMO we have now to define a list of "Done" or "Quite Done" and "Todo".
Done:
Quite done:
Todo:
LVGL Rocks !