Calendar - disable date selections before today

Description

Using the calendar, is there a way to prevent the user from selecting a date before another date, IE today or another specified date?

IE I want the start to be no earlier than today and the end to be no earlier than the start…

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

Simulator

What LVGL version are you using?

8.1

What do you want to achieve?

As described

What have you tried so far?

Review docs, I cant see anything that would help, maybe this should be a feature request?

Code to reproduce

void cb_setLeavingDate(lv_event_t * event)
{
    lv_obj_t * cont = lv_win_get_content(holidy_mode_window);  /*Content can be added here*/
    lv_obj_t  * calendar = lv_calendar_create(cont);
    lv_obj_set_size(calendar, 385, 385);
    lv_obj_align(calendar, LV_ALIGN_CENTER, 0, 0);
    lv_obj_add_event_cb(calendar, cb_calendar_leavingDate, LV_EVENT_ALL, NULL);

    lv_calendar_set_today_date(calendar, 2022, 03, 8);
    lv_calendar_set_showed_date(calendar, 2022, 03);

    /*Highlight a few days*/
    static lv_calendar_date_t highlighted_days[1];       /*Only its pointer will be saved so should be static*/
    highlighted_days[0].year = 2021;
    highlighted_days[0].month = 03;
    highlighted_days[0].day = 8;

    lv_calendar_set_highlighted_dates(calendar, highlighted_days, 3);

    lv_calendar_header_arrow_create(calendar);

    lv_calendar_set_showed_date(calendar, 2022, 3);
}

Screenshot and/or video

@bader given you are totally rocking today do you have any ideas on this one?

Thanks in advance!

You are more than welcome. Mmm this is interesting, I don’t know if there is such functionality in this library, but I came with my own workaround (Not completed yet).

bool isBeforeToday = true;
static void cb_calendar_event_draw_begin(lv_event_t* e)
{
    lv_obj_t* obj = lv_event_get_target(e);
        lv_obj_draw_part_dsc_t* dsc = lv_event_get_param(e);
        /*Change the draw descriptor*/
        if (dsc->id != 0) {
            dsc->rect_dsc->radius = 0;
            if (lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_CUSTOM_1)) {
                dsc->rect_dsc->border_color = lv_palette_main(LV_PALETTE_RED);
                isBeforeToday = false;
            }
            else if(isBeforeToday) {
                lv_btnmatrix_set_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_DISABLED);

            }

            if (dsc->id == 48) {
                isBeforeToday = true;
            }
        }
    
}



.

  lv_obj_t  * calendar = lv_calendar_create(cont);
  lv_obj_t * btnMatrix= lv_calendar_get_btnmatrix(calendar);
  lv_obj_add_event_cb(btnMatrix, cb_calendar_event_draw_begin, LV_EVENT_DRAW_PART_BEGIN, NULL);

The problem here is when you go to the next month, all squares are disabled. I will try to work on it sometime later. :sweat_smile:

I hope it helps.

Do you think this would be a useful feature request?

Thanks!

hello guys,

Any solution of this problem?

@kisvegabor any chance this can be added as a FR?
Thanks

I can see an elegant solution:

static void prev_event_cb(lv_event_t * e)
{
    lv_obj_t * calendar = lv_event_get_user_data(e);
    const lv_calendar_date_t * d = lv_calendar_get_showed_date(calendar);

    if(d->year <= 2020 && d->month <= 5) {
        lv_event_stop_processing(e);
    }
}

...

lv_obj_t * header = lv_calendar_header_arrow_create(calendar);
lv_obj_add_event_cb(lv_obj_get_child(header, 0), prev_event_cb, LV_EVENT_CLICKED | LV_EVENT_PREPROCESS, calendar);

It prevents selecting a date before 2021.05.

Hi @kisvegabor

Thanks for looking into this for me.

This solution provides part of what I need as in you cant go back before a specific month. I would however like to prevent the user from selecting a specific date as well.

For example I wouldnt want them to be able to select the 1st to the 7th if the date today is the 8th.

Is that possible?
Thanks

Hi Alex,

Expanding on @kisvegabor 's previous answer:

I would do something like this:

static void prev_event_cb(lv_event_t * e)
{
    lv_obj_t * calendar = lv_event_get_user_data(e);
    const lv_calendar_date_t * d = lv_calendar_get_showed_date(calendar);
	/*pseudo code for obtaining the date & time from your platforms Realtime clock*/
	platform_time_struct current_date_time = platform_get_date_time();

    if(d->year <= 2020 && d->month <= 5 && d->day < current_date_time.day ) {
        lv_event_stop_processing(e);
    }
}

...

lv_obj_t * header = lv_calendar_header_arrow_create(calendar);
lv_obj_add_event_cb(lv_obj_get_child(header, 0), prev_event_cb, LV_EVENT_CLICKED | LV_EVENT_PREPROCESS, calendar);

I hope that makes sense, obviously you will need to sort out the pseudo code to obtain the current day from your system clock.

Kind Regards,

Pete

cc: @elgerg , @ankit, @bader

1 Like

thanks I will look into it.
Again thanks for providing the infomation!

Hi Pete,

Thanks for the suggestion but I already tried that.

The problem is that the callback processes the header:
image

Where I want to disable the days as well:
image

Any suggestions would be welcome!

Thanks
Alex

Hi Alex,

At first glance I think you can combine @bader 's answer with @kisvegabor 's to get your solution. I can’t look at it today but if you can’t work it out repost and I will take a look when I have a little more time.

Cheers,

Pete

@pete-pjb / @kisvegabor

Ok, I’m close with the following:

static void cb_calendar_event_draw_begin(lv_event_t* e)
{
  lv_obj_t* obj = lv_event_get_target(e);
  lv_obj_t * calendar = lv_event_get_user_data(e);
  lv_obj_draw_part_dsc_t* dsc = lv_event_get_param(e);
  const lv_calendar_date_t * d = lv_calendar_get_showed_date(calendar);

  time_t now = time(&now);
  struct tm tm_today = *localtime(&now);

  int year = 1900+tm_today.tm_year;
  int mon = 1+tm_today.tm_mon;

  /*Change the draw descriptor*/
  if (dsc->id != 0) {
      dsc->rect_dsc->radius = 0;
      if (lv_btnmatrix_has_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_CUSTOM_1)) {
          dsc->rect_dsc->border_color = lv_color_hex(0x01a2b1);
      }
      else if(d->year <= year && d->month <= mon && dsc->id < tm_today.tm_mday) {
          lv_btnmatrix_set_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_DISABLED);

      }
  }
}

However…

const lv_calendar_date_t * d = lv_calendar_get_showed_date(calendar);

always returned d.day as 1

When I try to use dsc->id I get 1 to 8 then back to 1

The final problem is that this:

lv_btnmatrix_set_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_DISABLED);

Doesnt seem to have an effect…

Any idea what things I’m missing?

Thanks
Alex

Hi Alex,

Please try this:

static void cb_calendar_event_draw_begin(lv_event_t* e)
{
  lv_obj_t* obj = lv_event_get_target(e);
  lv_obj_t * calendar = lv_obj_get_parent( obj );
  lv_obj_draw_part_dsc_t* dsc = lv_event_get_param(e);
  const lv_calendar_date_t * d = lv_calendar_get_showed_date(calendar);
  int btn_day;

  time_t now = time(&now);
  struct tm tm_today = *localtime(&now);

  int year = 1900+tm_today.tm_year;
  int mon = 1+tm_today.tm_mon;

  /*Change the draw descriptor*/
  if (dsc->id > 6) {
	  btn_day = atoi( lv_btnmatrix_get_btn_text( obj, dsc->id ) );
	  if((d->year < year || d->month < mon) || ( (d->year == year && d->month == mon) && btn_day < tm_today.tm_mday ) ) {
          lv_btnmatrix_set_btn_ctrl(obj, dsc->id, LV_BTNMATRIX_CTRL_DISABLED);
          dsc->rect_dsc->bg_opa = LV_OPA_TRANSP;
          dsc->rect_dsc->border_opa = LV_OPA_TRANSP;
          dsc->label_dsc->color = lv_palette_main(LV_PALETTE_GREY);
      }
  }
}

Hopefully this is what you are trying to achieve.

Kind Regards,

Pete

Works perfectly!

Thank you!!

1 Like