Slider object: make it react only to knob (V7)

What MCU/Processor/Board and compiler are you using?

C in general

What do you want to achieve?

Now a touch event or similar on any part of a slider makes it change value. I’d like to know whether there is a way to change it such that only the slider’s knob reacts to user fiddling.

I have a menu skeleton under development, and it will list N menu items on a scrollable page, also sliders. Now it is rather annoying if one tries to scroll the page by dragging the slider bar area --> slider knob gets to the point touched right away.

What have you tried so far?

The usual, ie. wading through the documentation and this forum :wink: :wink:

I think you’re looking for this feature: https://github.com/lvgl/lvgl/pull/1318

You’ll need to override the slider’s signal callback and implement a hit-testing function (location where it’s called) that checks whether the point is within ext->left_knob_area. You then need to call lv_obj_set_adv_hittest(slider, true) on the slider to make the input system use your hit-testing function.

2 Likes

Thank you Sir!

Sounds complicated :smiley:, but after a quick look I feel it might be achievable even with my abilities. I’ll tell you later!

@kisvegabor, @embeddedt Guys … I LOVE YOU :revolving_hearts:

And every day I love more and more this absolutely fascinating piece of software! I’m getting emotional thinking how on earth a software package of this quality and performance (and SUPPORT!!!) has been released to public. Should we ever meet, it will be a dinner!

@embeddedt, it finally got to quite minimized version. I have saved the “factory version” of slider’s signal callback at slider object creation and will call it in my own cb. Here:

static lv_res_t cb_slider_signal(struct _lv_obj_t * obj, lv_signal_t sign, void * param)
{
    lv_res_t res = LV_RES_OK;

    // "advanced hit testing" has been enabled in slider creation
    if (LV_SIGNAL_HIT_TEST == sign) {
        lv_slider_ext_t *ext_slider = lv_obj_get_ext_attr(obj);
        lv_hit_test_info_t *info = param;

        // Ordinary slider: did it hit the knob area?
        info->result = _lv_area_is_point_on(&ext_slider->right_knob_area, info->point, 0);
    }
    else {
        res = cb_slider_signal_default(obj, sign, param);
    }
    return res;
}

@peejii73

Thank you, so happy to hear that! :slight_smile:

Your implementation looks good!
@embeddedt, what do you think, would it be a good idea to integrate it into lvgl?

Thanks, nice to hear :slight_smile:

There’s a bit field in slider’s ext data where only one bit is used (dragging). How about adding a “knob_only” bit, which is checked for in signal cb? We could add a function “lv_slider_set_knob_adj_only()” or similar? Or maybe make this “knob only” behavior as default, with optional “fast setting” (i.e click-anywhere-on-slider’s-bar).

I’m bad giving names to these :smiley:, but I’ll make a pull request if you wish.

Cheers!

I’d suggest using the “click anywhere” behavior by default; that’s how many other sliders I’ve seen work. I have no objections to adding the knob-only behavior as an option, though.

That would be fine, although I’d actually suggest continuing to use the advanced hit-testing feature for this (like I did for making the color picker’s center transparent).

Hi,

OK, good. What do you think about enabling / disabling it: use one bit on the existing bitfield in ext, and providing set / get functions for it?

So … Did I get this right:extend the signal callback with LV_SIGNAL_HIT_TEST as done in cpicker and img widgets? Effectively taking my existing piece of code and putting it to the “native” callback, together with checking the enable bit? Does this sound reasonable?

Thanks!

Yes; that sounds fine.

1 Like

Hi, for a long time!

I’d like to make an extension to this feature, that is, making it respect the LV_USE_EXT_CLICK_AREA.

Here’s my proposal for modification of signal handler in lv_slider.c:

    /* Advanced hit testing: react only on dragging the knob(s) */
    if(sign == LV_SIGNAL_HIT_TEST) {
        lv_hit_test_info_t * info = param;
        lv_area_t ext_area;

        /* Ordinary slider: was the knob area hit? */
        lv_area_copy(&ext_area,  &ext->right_knob_area);
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
        ext_area.x1 -= slider->ext_click_pad_hor;
        ext_area.x2 += slider->ext_click_pad_hor;
        ext_area.y1 -= slider->ext_click_pad_ver;
        ext_area.y2 += slider->ext_click_pad_ver;
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
        ext_area.x1 -= slider->ext_click_pad.x1;
        ext_area.x2 += slider->ext_click_pad.x2;
        ext_area.y1 -= slider->ext_click_pad.y1;
        ext_area.y2 += slider->ext_click_pad.y2;
#endif
        info->result = _lv_area_is_point_on(&ext_area, info->point, 0);

        /* There's still a change we have a hit, if we have another knob */
        if((info->result == false) && (type == LV_SLIDER_TYPE_RANGE)) {
            lv_area_copy(&ext_area,  &ext->left_knob_area);
#if LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_TINY
            ext_area.x1 -= slider->ext_click_pad_hor;
            ext_area.x2 += slider->ext_click_pad_hor;
            ext_area.y1 -= slider->ext_click_pad_ver;
            ext_area.y2 += slider->ext_click_pad_ver;
#elif LV_USE_EXT_CLICK_AREA == LV_EXT_CLICK_AREA_FULL
            ext_area.x1 -= slider->ext_click_pad.x1;
            ext_area.x2 += slider->ext_click_pad.x2;
            ext_area.y1 -= slider->ext_click_pad.y1;
            ext_area.y2 += slider->ext_click_pad.y2;
#endif
            info->result = _lv_area_is_point_on(&ext_area, info->point, 0);
        }
    }

Would that be a proper place to include the area enlargement? If yes, I’m happy to make a pull request out of this.

Thanks!