-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
Encoder support #422
Comments
Hi Martin, Thank you for the suggestions and remarks! They very reasonable! I never used LittelvGL with an encoder controller GUI so I didn't have first-hand experience with its usability. I try to release v5.2 soon and I'd like to add your great suggestions to it. So I implemented some of them. See below. Please test the updates and give some feedback!
When v5.2 will be released I'm planning to add a working example to the documentation which you can just copy-paste to your code.
Why not!? Added.
Good perception! I fixed it. Now the border is drawn above the indicator.
If you mean Drop down list then I agree, it's a little bit confusing. I also updated it. Random ideas
As probably you noticed in LittelvGL the order of creation of objects determines the "tab" order. I agree, in some cases, a "tabindex" property might be useful. However, it has some memory overheads... Do you think it's really useful for usually small embedded GUI's or just mentioned it?
Good idea. The navigate->edit can be short and/or long enter too. Only edit->naviagate needs to be long enter. I added it as well.
Now only one style can be modified to have a focused appearance. It seems a littlebit complicated to add more (arbitrary styles) to update. Adding a glow seems difficult too because it would increase the size of the object and it's not supported now.
It also sounds reasonable. Added. |
Wow, that's fast! 👍
That would indeed be helpful. I meant something different, though (I now see I wasn't very clear ;)). I meant that it would be nice if just registering a keypad driver would be sufficient, i.e. that it wouldn't be necessary to assign it to a group, and to add individual objects to that group.
Heh, again my mistake in unclarity... I meant to have short-click enter (and exit) edit mode of the slider, such that I can move it after a short-click (and get back to navigate mode after another short-click).
Cool, thanks! Much clearer now!
Ah yes, I meant ddlist. Open on edit-mode is OK now, but I would propose to also leave edit mode again after selecting an item (with short-click), e.g.:
Random ideas
The reason why I mentioned it was the thought whether the concept of groups is really necessary:
Advantages:
One would loose the possibility of having multiple encoders controlling different parts of the GUI. Not sure if this is a use-case, though. (I could imagine someone wanting to assign an encoder to a single dedicated control, but that wouldn't need navigate/edit mode anyway?) I'll try to experiment a bit to see if this idea indeed might work.
I think that in most cases, edit->navigate is possible with short-click too (e.g. slider, ddlist, roller, ...).
Fair enough :) Hmmm, just a thought: if the focus/edit concept indeed becomes based on the object structure, there can only be one focused element in the app (instead of multiple, as possible with multiple groups). This would also enable any object to simply check "Am I the focused (or editing) object?" (i.e. without adding extra state in each object) and if so select the appropriate style from its theme. This would also remove the need for E.g.: struct {
lv_style_t *bg;
lv_style_t *bg_focus_and_edit;
lv_style_t *indic;
lv_style_t *knob;
lv_style_t *knob_focus;
lv_style_t *knob_edit;
} slider; (In this example, e.g. the background would have the same style for both focus and edit, and only the knob would change to indicate edit mode.)
Nice, thanks! Encoder input driver FWIW, here's the code I'm using for my encoder input: bool input_read_encoder(lv_indev_data_t *data) {
static int16_t diff = 0;
diff += encoder.getValue();
static uint32_t key = 0;
static lv_indev_state_t state = LV_INDEV_STATE_REL;
ClickEncoder::Button button = encoder.getButton();
if (button != ClickEncoder::Open) {
// If button is pressed, process that first, and ignore
// turns of the encoder while pressed
key = LV_GROUP_KEY_ENTER;
state = LV_INDEV_STATE_PR;
diff = 0;
} else if (state == LV_INDEV_STATE_PR) {
// Otherwise, first emit a release event before
// processing remaining ticks of the knob
state = LV_INDEV_STATE_REL;
} else if (diff > 0) {
// Emit one right 'click'
diff--;
key = LV_GROUP_KEY_RIGHT;
state = LV_INDEV_STATE_PR;
} else if (diff < 0) {
// Emit one left 'click'
diff++;
key = LV_GROUP_KEY_LEFT;
state = LV_INDEV_STATE_PR;
}
data->state = state;
data->key = key;
// Keep coming back until all encoder pulses are processed
return diff != 0;
} The library I use (ClickEncoder) supports 'acceleration' (nice for e.g. sliders), which means that in a single read of the input driver, the value may have gone up multiple ticks. I am emulating multiple key down/up events to pass these on to lvgl. It might be handy to provide some sort of wrapper (or even a dedicated encoder input driver type?) such that people only have to pass number of ticks and current state of the button to make it work? |
I updated the objects above to work this way.
static bool enc_read(lv_indev_data_t * data)
{
static int32_t last_diff = 0;
data->enc_diff = diff - last_diff;;
data->state = btn_state;
last_diff = diff;
return false;
} Can you test it? Thank you for sharing your code and mentioning ClickEncoder. I didn't know this library but it seems great! |
I've just pushed a minor fix. |
Cool! I also made the same fix on my end already :) I also already implemented the ENCODER type driver yesterday, in much the same way as you did, so it seems we're aligned ;) I do have a few questions on the implementation of focused->signal_func(focused, LV_SIGNAL_FOCUS, NULL); /*Focus again. Some object do something on navigate->edit change*/ Would it make sense to have dedicated signals like In the "Release happened" part, I noticed that it would still send LV_GROUP_KEY_ENTER to the control, even after a long press. Therefore, I added a big /*Release happened*/
else if(data->state == LV_INDEV_STATE_REL && i->proc.last_state == LV_INDEV_STATE_PR)
{
if (i->proc.long_pr_sent == 0) { /* Short-click release */
lv_obj_t * focused = lv_group_get_focused(i->group);
bool editable = false;
focused->signal_func(focused, LV_SIGNAL_GET_EDITABLE, &editable);
/*The button was released on a non-editable object. Just send enter*/
if (!editable) {
lv_group_send_data(i->group, LV_GROUP_KEY_ENTER);
}
/*An object is being edited and the button is released. Just send enter */
else if (i->group->editing) {
lv_group_send_data(i->group, LV_GROUP_KEY_ENTER);
}
/*If the focused object is editable and now in navigate mode then enter edit mode*/
else if(editable && !i->group->editing && !i->proc.long_pr_sent) {
i->group->editing = i->group->editing ? 0 : 1;
focused->signal_func(focused, LV_SIGNAL_FOCUS, NULL); /*Focus again. Some object do something on navigate->edit change*/
LV_LOG_INFO("Edit mode changed (edit)");
lv_obj_invalidate(focused);
}
}
if(i->proc.reset_query) return; /*The object might be deleted in `focus_cb` or due to any other user event*/
i->proc.pr_timestamp = 0;
i->proc.long_pr_sent = 0;
} However, it seems that then (e.g.) button no longer 'clears' its Default group
That would work too, I suppose. I was experimenting a bit with achieving the same without groups, e.g. having an My idea was that a short-click would then:
And that long-press would navigate back to the parent, i.e. behave somewhat like an Escape key or Android's back button. That would probably also work very nice for e.g. a (modal dialog) popup, where long-press would 'cancel' the dialog. Not sure how to handle things like long-press on a button in that case, though. I suppose it could still work, but only when there is no 'parent' to navigate to, such as when the button is on the 'home screen' (i.e. not inside a dialog or something). I'll experiment a bit with this if you don't mind, and if it appears feasible, I'll put up a PR for further discussion (not necessarily for direct merging).
I was under the impression that the necessary styles would only be stored on the theme, so all instances of an object type (e.g. sliders) would reference the same style data. In that case, the overhead is very minimal? (e.g. just 2 extra lv_style_t on the theme for a slider, which would be shared by all sliders)?
Perfect! It was exactly what I was thinking of :) I want to do a bit more testing, but results so far are very nice, thanks! |
I did some more testing (using Some smaller things I found:
I wonder how navigation would work with 'nested' controls, e.g. putting sliders on the tab controls or a page. Didn't try that yet. |
Only one thing is not clear: If you have a page with some objects and you are now focusing on the page you can do 3 things:
|
Sure! Still experimenting anyway, and I'd like to create some actual application objects first to see what I run into in practice :)
Ah, so it's more of a 'release' event than an actual 'click' event then. That makes sense. Maybe naming can be updated to better reflect this in a future release?
Ah, yes, that seems like a good idea! It certainly shouldn't delay the 5.2 release. Hierarchical navigation Yes, exactly! So 'entering' an object (short-clicking the encoder) will either call Long-click will then always try to I'm not sure on how to best implement the 'scroll into view' when focusing on an object on a page, I did not dive into the code of e.g. page yet. I would not be a big fan of people having to assign manual callbacks though. Maybe the current parent (i.e. of which we're navigating the children) should also be sent a signal whenever its children change focus. So e.g. the page could then do a I suppose the same holds for things like |
Tests:
Hierarchical navigation: For clarity, I suggest using this topic for fixing the issue of the currently implemented things and create new issues for improvements planned for next releases. |
Did you push these changes already? BTW, I just found out that the current encoder driver crashes when there are no controls to be focused. I agree with creating separate issues for the remaining 'new' stuff (hierarchical navigation and ddlist animation). I can do that later. |
Upps, I forget to push :) Pushed now. Leave page edit on short click:
So if the default mode is "short click exit" then it will work in general cases. If the object type needs something else it can change the behaviour. What do you think? The crash: |
Hierarchical nav: adding the children themselves to the group seems awkward (even if it happens automatically), because e.g. the childen of the main page would be in the same list as the children of the popup dialog. So things like focus_next() have to somehow skip a whole bunch of controls if they want to stay in 'sibling mode'. Whereas the list of available siblings is already available as the children of the control (e.g. popup window). I'm actually writing some application code right now (using the already excellent lvgl :)), but if I have some more time in the coming weeks I'll see if I can come up with a good 'demo app' to have something to shoot on. Crash: yes, group was 'empty', and encoder called |
Okay, I'm waiting for the demo! Thank you! :) It would be easier to speak about a concrete thing. As the main issue (better Encoder support) seems to be implemented successfully I think this issue can be closed. Thank you very much for the contribution! It was really nice to work with you! :) |
Hi! Thanks for your excellent graphics library!
I'm using it to implement a GUI for a room thermostat on an SSD1331 display (96x64 px) in combination with an encoder (turn + push). I noticed a few other issues (#365, #261) about this subject, and switched to
dev-5.2
to try it out.It took me a while to figure out that I had to assign the input driver to a group, but it works now 😄
I then played a bit with
lv_test_group_1()
.A few things that 'surprised' we were:
(I hope the terse wording doesn't come across as offending, I certainly appreciate the effort that went into this already!)
Some random ideas (just to throw them out there)
I already experimented a bit in the code myself, and am more than happy to help out with both implementation and testing. Curious what your thoughts on this are, though!
The text was updated successfully, but these errors were encountered: