lvgl / lv_binding_cpp

[WIP] C++ binding for LVGL
MIT License
44 stars 20 forks source link

Still on ? #8

Open AlixANNERAUD opened 1 year ago

AlixANNERAUD commented 1 year ago

Hello, Is this project still on? I would be interested in doing the binding.

RobinPurtee commented 1 year ago

I am also interested in doing a C++ binding. After a quick review of this project, I believe it could be done in a more "Standard" compliant manner than this.

kisvegabor commented 1 year ago

Hi guys,

Thank you for writing. Sorry @AlixANNERAUD somehow I haven't noticed your comment earlier. :slightly_frowning_face:

This project is really stale now, but it would be really great to have a C++ binding!

My 400 USD offering in the README is still also active.

However there is one more thing: LVGL v9 will be released by end of this year so we should target the v9 API with the C++ binding too. There will be some refactoring later on on the API but no conceptual changes.

AlixANNERAUD commented 1 year ago

Hello, OK, well I'm working on it. However, could you tell me what a typical widget class should look like? Because at the moment I'm using an approach where the class just contains a pointer to the widget which is then used/modified by the various methods. I'm not sure this is exactly what you're thinking...

kisvegabor commented 1 year ago

How can it be used? E.g. how the set the size and position of a button and how to add an event to it?

RobinPurtee commented 1 year ago

Alix, I have been reviewing this code. To answer your question: A class containing a pointer to a widget and using that to invoke the C functions is exactly what you want. Basically. What you really want is a class that encapsulates an 'lv_obj_t*' and the widget classes derive from that, as it is in this code base. The basic design of this code base is correct. The issues I have are with the implementation. Chief among them being it is designed to be fluent (i.e. methods return a reference to the object). While fluent design is cool, but it makes the code difficult to in-line without adding bytes. The ideal C++ binding should not add any amount of code storage above what a C implementation would. While that may not be completely possible, placing function return value on what is effectively a void method is difficult to optimize away. Also the coding style used, while not uncommon, is not the same has the C code base, nor current C++ standard library, style. Using the same naming convention has the C library would make it easier for developers to move from C to C++. It should that they use the library basically the same way did before, but do not have to worry about object life time management has much.

AlixANNERAUD commented 1 year ago

Hello, I've started generating the binding from scratch again using PyGCCXML. Here is the wrapper class for lv_obj_t as an example (don't pay attention to the casing ^^ ) :

namespace LVGL
{
    typedef class Object_Class
    {
    public:
        virtual ~Object_Class();
        void Clean();
        void Del_Delayed(uint32_t delay_ms);
        void Del_Anim_Ready_Cb(lv_anim_t * a);
        void Del_Async();
        void Set_Parent(Object_Class parent);
        /* ... */
        void Move_Background();
        uint32_t Get_Child_Id();
        inline Object_Class(lv_obj_t* LVGL_Pointer) : LVGL_Pointer(LVGL_Pointer) { };
        inline Object_Class() : LVGL_Pointer(NULL) { };
        inline lv_obj_t* Get_LVGL_Pointer() const { return LVGL_Pointer; };_lv_obj_t* Object_Class::Get_Child(int32_t id)
{
    return lv_obj_get_child(LVGL_Pointer, id);
}
        inline void Clear_Pointer() { LVGL_Pointer = NULL; };

        static const lv_obj_class_t& Class;
    protected:
        lv_obj_t* LVGL_Pointer;
    } Object_Type;
}

Some function here are not part of LVGL, it's only for shorter implementation.

And here the definition of one function as an example :

Object_Type Object_Class::Get_Child(int32_t id)
{
    return Object_Type(lv_obj_get_child(LVGL_Pointer, id));
}

Does this look right to you ? I'd like to use a more RAII-oriented model (object creation / deletion) within the constructor, move semantic ...), but I don't think it's the most suitable for this context.

AlixANNERAUD commented 1 year ago

Hello, I have a first working version that generates a binding for almost all the wigets (the rest will come later). It is RAII oriented (all the objects are deleted out of the scope):

 LVGL::Object_Type Screen(lv_scr_act());

  LVGL::Button_Type B(Screen);
  B.Set_Align(LV_ALIGN_CENTER);

  LVGL::Label_Type Label(B);

  LVGL::Switch_Type S(Screen);
  S.Set_Align(LV_ALIGN_BOTTOM_LEFT);

  LVGL::Colorwheel_Type C(Screen, true);
  C.Set_Align(LV_ALIGN_BOTTOM_RIGHT);
  C.Set_Size(200, 200);

  Label.Set_Text("Hello World!");

Would it be possible to have your opinion on it? If you want to test it, I suggest you use my development environment available here: https://github.com/AlixANNERAUD/LVGL-Cpp-Binding-Environment (a fork of the native LVGL platform io). Just run Main.my inside the Tool folder, it's will generate the binding inside the LVGL_Cpp folder. Juste include from it the LVGL_Cpp.hpp file and you are done. Bye.

kisvegabor commented 1 year ago

Thank you for the update. Does this code just creates a an obj on the current screen, right? So I wonder why the variable is called "Screen"?

Shouldn't it be LVGL::Object_Type Screen(NULL);?

AlixANNERAUD commented 1 year ago

Hello, Sorry for my late reply, I've just come back from vacation. The first object instantiated Screen is instantiated from a pointer : there is a normal constructor that actually create an object and a constructor from a LVGL pointer which juste copy the LVGL pointer (no object creation). I know this can be confusing, but it simplifies the generation of binding code greatly.

Here's what the two constructors code looks like, roughly speaking :

Object_Class::Object_Class(lv_obj_t* LVGL_Pointer) : LVGL_Pointer(LVGL_Pointer)
{
}

Object_Class::Object_Class(Object_Class& Parent_Object) : LVGL_Pointer(NULL) // ! : This is not a copy constructor !!
{
this->LVGL_Pointer = lv_obj_create(Parent_Object.LVGL_Pointer)
}

I don't think LVGL::Object_Type Screen(NULL) is right, because then only the child object of Screen would be a screen actually, which isn't the point here. Rest assured, this isn't the final design at all, just some quick code for testing purposes.

kisvegabor commented 1 year ago

In LVGL v9 I'm thinking about having an lv_obj_t * lv_screen_create(void) function and remove the "create screen with NULL parent". Would it help for the binding?

AlixANNERAUD commented 1 year ago

Well, it's could help, and would make more sense, but it's not required.