How do I scroll the contents of a lv_win up, when the keyboard pops up, so that the active textarea remains in view using LVGL8.1.0

Description

I’m using a lv_win widget, with mutiple lv_textareas on the content arranged below each other, for entering settings for my application.

I have looked at the lv_demo_widgets.c on github repo, and notice that there is now a new lv_obj_scroll_to_view_recursive(ta, LV_ANIM_OFF) that is used to move the textarea into focus, on the tabview widget in the example.

I cannot get this to work on the contents of a lv_win though.
Any advice will be appreciated.

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

VSCode, ESP-IDF, ESP32-Wroom-32E-Devkit4, IL9488 480x320, FT6X06
I’m using [lv_port_esp32 @ 557679a6]

Code to reproduce

static void ta_event_cb(lv_event_t *event)
{
  lv_event_code_t code = lv_event_get_code(event);
  lv_obj_t *ta = lv_event_get_target(event);
 
  if (code == LV_EVENT_FOCUSED)
  {
    lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_TEXT_LOWER);
    lv_keyboard_set_textarea(kb, ta); 
    lv_obj_set_height(cont, LV_VER_RES - lv_obj_get_height(kb));
    lv_obj_update_layout(cont);   /*Be sure the sizes are recalculated*/
    lv_obj_clear_flag(kb, LV_OBJ_FLAG_HIDDEN);
    lv_obj_scroll_to_view_recursive(ta, LV_ANIM_OFF);
  }

  if (code == LV_EVENT_DEFOCUSED)
  {
    lv_keyboard_set_textarea(kb, NULL);
    lv_obj_set_height(cont, LV_VER_RES);
    lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
  }
}

Unfortunately this does not result in the content scrolling up, and the active textarea is overlaid by the keyboard, requiring one to manually scroll up to bring it into view for editing purposes. I had this working perfectly in LVGL 7.1.0 after I found this link on the forum https://forum.lvgl.io/t/text-area-entry-using-a-keypad/3387/5?u=wreyford

Please, can someone help me!

I have set up an example to prove my problem in the simulator:
The following code, in place of lv_demo_wideget.c

/**
 * @file lv_demo_widgets.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "../../lv_demo.h"


#if LV_USE_DEMO_WIDGETS

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/
typedef enum {
    DISP_SMALL,
    DISP_MEDIUM,
    DISP_LARGE,
}disp_size_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/

static void kb_event_cb(lv_event_t* event);
static void ta_event_cb(lv_event_t* event);

/**********************
 *  STATIC VARIABLES
 **********************/


/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void lv_demo_widgets(void)
{
    win = lv_win_create(lv_scr_act(), 60);
    lv_obj_set_scrollbar_mode(win, LV_SCROLLBAR_MODE_OFF);

    /*Add control button to the header*/
    navigate_btn = lv_win_add_btn(win, LV_SYMBOL_CLOSE, 60);
    lv_win_add_title(win, "Window Home"); /*Set the title*/
    mains_btn = lv_win_add_btn(win, LV_SYMBOL_DUMMY "100%", 40);
    batt_btn = lv_win_add_btn(win, LV_SYMBOL_BATTERY_FULL, 40);
    wifi_btn = lv_win_add_btn(win, LV_SYMBOL_WIFI, 40);
    bt_btn = lv_win_add_btn(win, LV_SYMBOL_BLUETOOTH, 40);
    //home_open();

    cont = lv_win_get_content(win);
    lv_obj_set_scrollbar_mode(win, LV_SCROLLBAR_MODE_AUTO);

    /*Create a keyboard to use with text areas*/
    kb = lv_keyboard_create(lv_scr_act());
    lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
    /*Add my custom event cb to the KB */
    lv_obj_add_event_cb(kb, kb_event_cb, LV_EVENT_ALL, NULL);

    /**
     * Build out TextArea 2
     */
    lv_obj_t* textarea_container1 = lv_obj_create(cont);
    lv_obj_align(textarea_container1, LV_ALIGN_TOP_MID, 0, 0);
    lv_obj_set_size(textarea_container1, LV_HOR_RES - 30, LV_SIZE_CONTENT);
    lv_obj_set_flex_flow(textarea_container1, LV_FLEX_FLOW_ROW);

    lv_obj_t* ta_label1 = lv_label_create(textarea_container1);
    lv_label_set_text(ta_label1, "TextArea 1:");

    lv_obj_t* ta_1 = lv_textarea_create(textarea_container1);
    lv_textarea_set_text(ta_1, "Some text to Edit");
    lv_textarea_set_one_line(ta_1, true);
    lv_obj_set_user_data(ta_1, NULL);
    lv_obj_add_event_cb(ta_1, ta_event_cb, LV_EVENT_ALL, NULL);
    lv_obj_set_flex_grow(ta_1, 1);

    /**
     * Build out TextArea 2
     */
    lv_obj_t* textarea_container2 = lv_obj_create(cont);
    lv_obj_align_to(textarea_container2, textarea_container1, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
    lv_obj_set_size(textarea_container2, LV_HOR_RES - 30, LV_SIZE_CONTENT);
    lv_obj_set_flex_flow(textarea_container2, LV_FLEX_FLOW_ROW);

    lv_obj_t* ta_label2 = lv_label_create(textarea_container2);
    lv_label_set_text(ta_label2, "TextArea 2:");

    lv_obj_t* ta_2 = lv_textarea_create(textarea_container2);
    lv_textarea_set_text(ta_2, "");
    lv_textarea_set_one_line(ta_2, true);
    lv_textarea_set_password_mode(ta_2, false);
    lv_obj_set_user_data(ta_2, NULL);
    lv_obj_add_event_cb(ta_2, ta_event_cb, LV_EVENT_ALL, NULL);
    lv_obj_set_flex_grow(ta_2, 1);

    /**
     * Build out TextArea 3
     */
    lv_obj_t* textarea_container3 = lv_obj_create(cont);
    lv_obj_align_to(textarea_container3, textarea_container2, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0);
    lv_obj_set_size(textarea_container3, LV_HOR_RES - 30, LV_SIZE_CONTENT);
    lv_obj_set_flex_flow(textarea_container3, LV_FLEX_FLOW_ROW);

    lv_obj_t* ta_label3 = lv_label_create(textarea_container3);
    lv_label_set_text(ta_label3, "TextArea 3:");

    lv_obj_t* ta_3 = lv_textarea_create(textarea_container3);
    lv_textarea_set_text(ta_3, "The keyboard will draw over this...when edited.");
    lv_textarea_set_one_line(ta_3, true);
    lv_textarea_set_password_mode(ta_3, false);
    lv_obj_set_user_data(ta_3, NULL);
    lv_obj_add_event_cb(ta_3, ta_event_cb, LV_EVENT_ALL, NULL);
    lv_obj_set_flex_grow(ta_3, 1);

}

/**********************
 *   STATIC FUNCTIONS
 **********************/

 /**
  * @brief kb_event_cb Event callback for Keyboard
  */
static void kb_event_cb(lv_event_t* event)
{
    lv_obj_t* ta;
    
    lv_event_code_t code = lv_event_get_code(event);

    //lv_keyboard_def_event_cb(event);
    if (code == LV_EVENT_CANCEL)
    {
        //TODO AJW lv_obj_set_height(cont, cont_scroll_height);
        lv_keyboard_set_textarea(kb, NULL);
        lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
    }
    if (code == LV_EVENT_READY)
    {
        ta = lv_keyboard_get_textarea(kb);
        const char* str = lv_textarea_get_text(ta);
        /*Normally do something with data here*/
     
        /**
         * Restore Cont to original size and delete the KB
         */
        lv_obj_set_height(cont, LV_VER_RES);
        lv_keyboard_set_textarea(kb, NULL);
        lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
    }
}

/**
 * @brief ta_event_cb for TextArea that will create a KB with LV_KEYBOARD_MODE_TEXT_LOWER
 */
static void ta_event_cb(lv_event_t* event)
{
    lv_event_code_t code = lv_event_get_code(event);
    lv_obj_t* ta = lv_event_get_target(event);

    if (code == LV_EVENT_FOCUSED)
    {
        lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_TEXT_LOWER);
        lv_keyboard_set_textarea(kb, ta);
        lv_obj_clear_flag(kb, LV_OBJ_FLAG_HIDDEN);
        lv_obj_update_layout(cont);   /*Be sure the sizes are recalculated*/
        lv_obj_set_height(cont, LV_VER_RES - lv_obj_get_height(kb) - 60);
        lv_obj_clear_flag(kb, LV_OBJ_FLAG_HIDDEN);
        lv_obj_scroll_to_view_recursive(ta, LV_ANIM_OFF);
    }

    if (code == LV_EVENT_DEFOCUSED)
    {
        lv_keyboard_set_textarea(kb, NULL);
        lv_obj_set_height(cont, LV_VER_RES);
        lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
    }
}
#endif

And the following in the place of lv_demo_widgets.h

/**
 * @file lv_demo_widgets.h
 *
 */

#ifndef LV_DEMO_WIDGETS_H
#define LV_DEMO_WIDGETS_H

#ifdef __cplusplus
extern "C" {
#endif

/*********************
 *      INCLUDES
 *********************/

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  GLOBAL VARIABLES
 **********************/

 /**
  * Header Win Buttons
  */
    lv_obj_t* navigate_btn;

    lv_obj_t* batt_btn;
    lv_obj_t* mains_btn;
    lv_obj_t* wifi_btn;
    lv_obj_t* bt_btn;


    /**
     * Global vl_win
     */
    lv_obj_t* win;
    lv_obj_t* cont;
    lv_obj_t* kb;

    lv_coord_t cont_scroll_height;

/**********************
 * GLOBAL PROTOTYPES
 **********************/
void lv_demo_widgets(void);

/**********************
 *      MACROS
 **********************/

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /*LV_DEMO_WIDGETS_H*/

This compiles and illustrates my problem, in spite of doing what the original lv_widget_demo does in a TabView, where the content does scroll up when keyboard pops up.
When you click on the third text area, it will not scroll up …Please help. (I’m sure I’m doing something stupid!)

You need to set the height of win in the events instead of cont.

Thank you so so much. I can’t believe I didn’t think of that. Now I have that part of the migration working too.

Great it’s working there too :slight_smile: