Manually release button after PRESS_LOST

What do you want to achieve?

I’m using LVGL v9 for a little windows utility. I implemented WindowProc successfully and I got everything running except PRESS_LOST in button logic for when a button is pushed but then cancelled by moving the cursor away from the button.
For the titlebar close button I can successfully get the PRESS_LOST event to fire but the button remains in limbo. I need to click any button twice after to reset the button states and make them work again. How do I properly reset the button states?
One additional tidbit… if i move the cursor over ‘cont’ and release it will reset the button states as expected.
For the select button in my content widget (cont) I cannot get PRESS_LOST to fire and the click will be executed no matter where on screen I release the press.

What have you tried so far?

Too many to list and I have too little inner knowledge of LVGL to figure this out in a reasonable time.

Code to reproduce

The following is the LVGL code only. Adding the WindowProc code is might be overcomplicate things since a lot would need additional explanation.

lv_obj_t* btn_close = nullptr;
lv_obj_t* win = nullptr;
lv_obj_t* cont = nullptr;
lv_obj_t* debug_txt = nullptr;

static void select_button_handler(lv_event_t * e) {
    lv_event_code_t code = lv_event_get_code(e);
	
    if (code == LV_EVENT_PRESSED) {
        if (g_hwnd) {
            SetCapture(g_hwnd);
        }
    }
    else if (code == LV_EVENT_PRESS_LOST) {
        ReleaseCapture();
        if (debug_txt) {
            lv_textarea_add_text(debug_txt, "SELECT PRESS_LOSS!\n");
        }
    }
    else if (code == LV_EVENT_RELEASED) {
        ReleaseCapture();
    }
    else if (code == LV_EVENT_CLICKED) {
        std::string selectedFolder = SelectFolderDialog(g_hwnd);
        
		if (!selectedFolder.empty() && txt_loc != NULL) {
			lv_textarea_set_text(txt_loc, selectedFolder.c_str());
        }
    }
}

static void close_button_handler(lv_event_t * e) {
    lv_event_code_t code = lv_event_get_code(e);
    // lv_obj_t* btn = (lv_obj_t*)lv_event_get_target(e);
    
    if (code == LV_EVENT_PRESSED) {
        if (g_hwnd) {
            SetCapture(g_hwnd);
        }
    }
    else if (code == LV_EVENT_PRESS_LOST) {
        ReleaseCapture();
		
        if (debug_txt) {
            lv_textarea_add_text(debug_txt, "CLOSE PRESS_LOSS!\n");
        }
    }
    else if (code == LV_EVENT_RELEASED) {
        ReleaseCapture();
    }
    else if (code == LV_EVENT_CLICKED) {
        if (g_hwnd) {
			ReleaseCapture();
            PostMessage(g_hwnd, WM_CLOSE, 0, 0);
        }
    }
}


void lv_my_test_gui(void)
{
    win = lv_win_create(lv_screen_active());
    lv_obj_set_style_border_width(win, 1, LV_PART_MAIN);
    lv_obj_set_scroll_dir(win, LV_DIR_NONE);
    lv_win_add_title(win, "LVGL Test Window");
    
    lv_obj_t * header = lv_win_get_header(win);
    lv_obj_set_height(header, 36);
    lv_obj_set_style_pad_all(header, 0, 4);
    lv_obj_set_scroll_dir(header, LV_DIR_NONE);

    btn_close = lv_win_add_button(win, LV_SYMBOL_CLOSE, 28);
    lv_obj_set_size(btn_close, 28, 28);
	lv_obj_set_style_shadow_width(btn_close, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
	lv_obj_set_style_shadow_opa(btn_close, LV_OPA_TRANSP, LV_PART_MAIN | LV_STATE_DEFAULT);
    lv_obj_clear_flag(btn_close, LV_OBJ_FLAG_PRESS_LOCK);
	lv_obj_add_event_cb(btn_close, close_button_handler, LV_EVENT_PRESSED, NULL);
	lv_obj_add_event_cb(btn_close, close_button_handler, LV_EVENT_RELEASED, NULL);
	lv_obj_add_event_cb(btn_close, close_button_handler, LV_EVENT_PRESS_LOST, NULL);
	lv_obj_add_event_cb(btn_close, close_button_handler, LV_EVENT_CLICKED, NULL);
	
    cont = lv_win_get_content(win);
    lv_obj_set_size(cont, LV_PCT(100), LV_PCT(100));
    lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLLABLE);
    lv_obj_set_style_border_width(cont, 0, LV_PART_MAIN);
    lv_obj_set_style_radius(cont, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_color(cont, lv_color_hex(0xADD8E6), LV_PART_MAIN);
    
    // Setup flex layout
    lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
    lv_obj_set_style_pad_all(cont, 10, 0);
    lv_obj_set_style_pad_row(cont, 5, 0);

    // --- Location Row ---
    lv_obj_t *row2 = lv_obj_create(cont);
    lv_obj_set_size(row2, LV_PCT(100), 40);
    lv_obj_set_flex_flow(row2, LV_FLEX_FLOW_ROW);
    lv_obj_set_flex_align(row2, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START);
    lv_obj_set_style_pad_column(row2, 10, 0);
    lv_obj_set_style_bg_opa(row2, LV_OPA_TRANSP, 0);
    lv_obj_set_style_border_width(row2, 0, 0);
    lv_obj_set_style_pad_all(row2, 0, 0);

    lv_obj_t *btn_select = lv_button_create(row2);
    lv_obj_set_size(btn_select, 90, 28);
	lv_obj_set_style_shadow_width(btn_select, 0, LV_PART_MAIN | LV_STATE_DEFAULT);
	lv_obj_set_style_shadow_opa(btn_select, LV_OPA_TRANSP, LV_PART_MAIN | LV_STATE_DEFAULT);
    lv_obj_t *lbl_sel = lv_label_create(btn_select);
    lv_label_set_text(lbl_sel, "Select");
    lv_obj_center(lbl_sel);
	lv_obj_add_event_cb(btn_select, select_button_handler, LV_EVENT_PRESSED, NULL);
	lv_obj_add_event_cb(btn_select, select_button_handler, LV_EVENT_RELEASED, NULL);
	lv_obj_add_event_cb(btn_select, select_button_handler, LV_EVENT_PRESS_LOST, NULL);
    lv_obj_add_event_cb(btn_select, select_button_handler, LV_EVENT_CLICKED, NULL);

    // --- Debug Text (separate, not in flex row) ---
    debug_txt = lv_textarea_create(cont);
    lv_obj_set_size(debug_txt, LV_PCT(100), 300);
    lv_textarea_set_text(debug_txt, "Debug: No input yet\n");
    
}

Screenshot

lvgl_problem

Environment

  • Win10:
  • LVGL version: v9.3