Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lv_obj_get_type function #91

Closed
upbeat27 opened this issue Jan 30, 2018 · 8 comments
Closed

Add lv_obj_get_type function #91

upbeat27 opened this issue Jan 30, 2018 · 8 comments

Comments

@upbeat27
Copy link
Contributor

Currently it is not really possible to navigate a menu and also control objects with just a rotary encoder (if I am missing something, please let me know!)

When I say rotary encoder, I am referring to one that has a button (pressing the encoder in) in addition to the rotation.

It is possible to tell what object is focused with the group focus style modifier, but it isn't possible to edit that object's value using the same encoder that is used to navigate between objects in the group.
The "selected" state can be controlled outside of lvgl in order to change the encoder between sending LV_GROUP_KEY_RIGHT/LV_GROUP_KEY_LEFT and LV_GROUP_KEY_NEXT/LV_GROUP_KEY_PREV. The problem here is that there isn't an indicator of the object being selected as opposed to just being in focus.

The proposed solution would be a combination of a "selected" state within the group, and also another style modifier callback. Setting this style modifier would have the additional option to be applied to either the selected object or every other object in the group. This would be useful for differentiating between "focused" and "selected" further than what can be achieved by changing just the selected object's style (by dimming all other objects in the group, for example).

Also important would be the input device handling for the object group checking if an objected is currently selected, to either send LV_GROUP_KEY_RIGHT/LV_GROUP_KEY_LEFT or LV_GROUP_KEY_NEXT/LV_GROUP_KEY_PREV. LV_GROUP_KEY_ENTER would toggle between selected/unselected on the focused object, unless that object doesn't require selection to edit (a checkbox would not need to be selected - it would just toggle its state directly).

I'd be interested in implementing this and submitting a pull request, if the functionality sounds good.

@kisvegabor
Copy link
Member

I'm so happy to see that you would like to contribute in a new feature!

Actually the lv_group system was originally created to ensure encoder control (the keyboard came later). My idea ("mapping") was this:

  • turn right: LV_GROUP_KEY_RIGHT
  • turn left: LV_GROUP_KEY_LEFT
  • press: LV_GROUP_KEY_NEXT
  • long press: LV_GROUP_KEY_ENTER

This way I was able to focus on a list by pressing the encoder, chose a list element by turning the encoder and "press" the selected list element by long pressing the encoder.

However in this case I lost the LV_GROUP_KEY_PREV opportunity and the long press in might be inconvenient. So I see you point and it's really not clear right now.

Let's summarize a scenario according to your suggestion:
So by default the first object is focused and the nothing is selected. By turning the encoder you can focus on an other objects (let's call it "focus" mode). By pressing the encoder you mark the object as selected. So now turning the encoder will modify the object's value instead of change focus. Press again step back to "focus" mode. Did I understood well?

I didn't tried it but it seems to me that can be implement with the current functionality by writing a "smart" input device which sends NEXT/PREV by default and LEFT/RIGHT after it is pressed. You can also change the style modifier function when the encoder is pressed. What do you think?

@upbeat27
Copy link
Contributor Author

Your summary is correct.

I would say the "selected" state would have a completely separate style modifier function, one that can be applied to all other objects beside the selected one.

It could be an input device LV_INDEV_TYPE_ENCODER.
It would use the existing LV_GROUP_KEY_LEFT and LV_GROUP_KEY_RIGHT, with an additional LV_GROUP_KEY_SELECT.
Internally, lvgl would convert LEFT/RIGHT to NEXT/PREV when no object is selected, so the user only has to send one type of control and the handling is automatic.

However, in looking into this more I don't think it will be so easy to implement. The basic idea would be for the encoder button to switch between selected/deselected which switches the keys between NEXT/PREV and LEFT/RIGHT as stated. It gets more complicated when considering objects like buttons/checkboxes/switches which dont need a "selected" state since they can be interacted with directly due to their binary nature. That is, LV_GROUP_KEY_SELECT would automatically become LV_GROUP_KEY_ENTER for those objects... but I don't think this will be easy (or clean) to implement.

Then again, it might not be best to force a user to accept that method of input for binary objects. Maybe they want to first "select" a checkbox then change its state with left/right.

Maybe a compromise is that it must be handled in the input driver read function, whether to send LV_GROUP_KEY_SELECT or LV_GROUP_KEY_ENTER when the encoder button is pressed. For my preference, I could just send LV_GROUP_KEY_ENTER when one of those binary objects is in focus.

Speaking of... is there an easy way to determine the type of an object, after calling lv_group_get_focused(group), for example?

@kisvegabor
Copy link
Member

Hi,
unfortunately there is no easy way to determine the object's type now. However it would be great improvement.

My idea is to add a function like: lv_obj_get_type(obj, buf) which put a string into the buffer. For example: "lv_sw", "lv_cb, "lv_btn" etc.

It can be implemented as a new signal, for example: LV_SIGNAL_GET_TYPE. I'm not sure, but maybe you already know that every object type has a signal handler function (e.g. lv_btn_signal) which gets a void * param If we pass buf (from lv_obj_get_type) as param then the signal function can copy the object's name.

However the object types are inherited from other object types (e.g. lv_btn -> lv_cont -> lv_obj) and the ancestor's signal function will be called first, but lastly the actual signal function will overwrite the ancestors type.

What do you think?

@upbeat27
Copy link
Contributor Author

I see what you're saying.
It could be also be:
const char * lv_obj_get_type(lv_obj_t *)
that returns a pointer to the string like "lv_sw", which could be:
static const char obj_name[] = "lv_sw";
at the top of lv_sw.c
Just to avoid copying the string.

@kisvegabor
Copy link
Member

Yes, good idea!

In this case lv_obj_get_type should pass a pointer to pointer to the signal function:

const char * lv_obj_get_type(lv_obj_t * obj)
{
  const char * name;
  obj->signal_f(obj, LV_SIGNAL_GET_TYPE, &name);
}

And in the signal function:

case LV_SIGNAL_GET_TYPE:
  const char ** name = param;
  *name = obj_name;
break;

Do you agree?

@upbeat27
Copy link
Contributor Author

upbeat27 commented Feb 1, 2018

Seems alright to me.
It allows for adding new objects (or custom objects outside of the main lib) without having some giant global enum list of objects.

It might also be good to have a simple function like bool lv_obj_is_type(const char * name) even though it would pretty much just be a wrapper for strcmp.

@kisvegabor
Copy link
Member

Great! 👍
I also like the bool lv_obj_is_type(const char * name) idea.

@kisvegabor kisvegabor changed the title [Enhancement] Add "selected" state to object groups Add lv_obj_get_type function Feb 5, 2018
@kisvegabor
Copy link
Member

kisvegabor commented Mar 7, 2018

I added this feature v5.1.

It works like this:

    lv_obj_t * btn1 = lv_btn_create(lv_scr_act(), NULL);
    lv_obj_type_t buf;
    lv_obj_get_type(btn1, &buf);

    uint8_t i = 0;
    while(buf.type[i] != NULL) {
        printf("%s\n", buf.type[i]);
        i++;
    }

And produces this result

lv_btn
lv_cont
lv_obj

So the function tells the type of the ancients too.

Thank you for the contribution!

cmumford pushed a commit to cmumford/lvgl that referenced this issue Dec 8, 2020
Initial implementation to support MPI3501 by @ndunello
kisvegabor pushed a commit that referenced this issue Aug 19, 2021
Fix compilation issues with latest master branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants