How to implement LVGL V8.3 widgets that can be freely dragged and sorted like a mobile phone

I have a requirement now, which is similar to a shortcut card on a mobile phone, where you can freely drag and sort on a certain page. How should I implement this or has anyone developed a similar library

1 Like

https://docs.lvgl.io/8.3/examples.html#make-an-object-draggable

Thank you for your help,I know this, but what I want to know more is how to determine whether two or more controls overlap during the movement process, which can be moved according to predetermined rules. Similar to multiple controls of different sizes occupying positions

You didn’t ask that question. you asked how to move an object freely as if it was an icon on something like a mobile phone.

I will continue to explore this issue, and if a feature similar to a mobile shortcut card is implemented, I will share it with everyone here。

My ideal effect is to have quick cards that can be freely sorted and occupied like a mobile phone

At present, I am also studying this. The layout of my app icon on the desktop is implemented by Flex, but I have a problem with how to insert the app icon into other places after dragging, and also in the grid.

Yes, this is one of the key points to solving this problem. My requirement is to have four card sizes in a ratio of 1x2 2x2 2x4 4x4. Moving the card to the correct position in different sizes is also a challenge.

Hey bro, I’m so happy to have someone working on the same project as me, I’ll let you know as soon as I have any progress!

I am also very happy. I will share any progress as soon as possible

OK this is actually not that difficult to do.

go to this link.

https://sim.lvgl.io/v8.3/micropython/ports/javascript/index.html?script_startup=https://raw.githubusercontent.com/lvgl/lvgl/f2c103260f3ac5a1a8c50af348b994ef8153796d/examples/header.py&script=https://raw.githubusercontent.com/lvgl/lvgl/f2c103260f3ac5a1a8c50af348b994ef8153796d/examples/widgets/obj/lv_example_obj_2.py

and paste the code below into the code editor and press the “refresh” button

import lvgl as lv
import display_driver


def released_event_handler(e):
    obj = e.get_target()

    child_cnt = scrn.get_child_cnt()

    offset_x = 0
    offset_y = 0

    for i in range(child_cnt):
        child = scrn.get_child(i)

        if child == obj:
            continue

        c_x = child.get_x()
        c_y = child.get_y()

        if c_x != offset_x:
            break

        offset_x += icon_width

        if offset_x == DISPLAY_WIDTH:
            offset_x = 0
            offset_y += icon_height

    obj.move_foreground()
    obj.set_pos(offset_x, offset_y)


def drag_event_handler(e):
    obj = e.get_target()

    indev = lv.indev_get_act()

    vect = lv.point_t()
    indev.get_vect(vect)

    if vect.x or vect.y:
        point = lv.point_t()
        indev.get_point(point)

        x = point.x
        y = point.y

        child_cnt = scrn.get_child_cnt()

        for i in range(child_cnt):
            child = scrn.get_child(i)

            if child == obj:
                continue

            c_x1 = child.get_x()
            c_y1 = child.get_y()
            c_x2 = c_x1 + icon_width
            c_y2 = c_y1 + icon_height

            if c_x1 < x < c_x2 and c_y1 < y < c_y2:
                break
        else:
            obj.move_foreground()
            obj.set_pos(obj.get_x() + vect.x, obj.get_y() + vect.y)
            return

        offset_x = 0
        offset_y = 0

        for j in range(child_cnt):
            child = scrn.get_child(j)
            if offset_x == DISPLAY_WIDTH:
                offset_x = 0
                offset_y += icon_height

            if i == j:
                offset_x += icon_width

            if offset_x == DISPLAY_WIDTH:
                offset_x = 0
                offset_y += icon_height

            if child != obj:
                child.set_pos(offset_x, offset_y)

            offset_x += icon_width

        obj.move_foreground()
        obj.set_pos(obj.get_x() + vect.x, obj.get_y() + vect.y)


scrn = lv.scr_act()

DISPLAY_WIDTH = scrn.get_width()
DISPLAY_HEIGHT = scrn.get_height()


def get_icon_size(min_size, max_size, ideal_size, total_size):
    sizes = []

    for i in range(min_size, max_size + 1, 1):
        if total_size % i:
            continue

        sizes.append(i)

    found_size = total_size

    for size in sizes:
        diff1 = abs(size - ideal_size)
        diff2 = abs(found_size - ideal_size)

        if diff1 < diff2:
            found_size = size

    return found_size


icon_width = get_icon_size(60, 80, 70, DISPLAY_WIDTH)
icon_height = get_icon_size(50, 70, 60, DISPLAY_HEIGHT)

icon_count = int(DISPLAY_WIDTH / icon_width) * int(DISPLAY_HEIGHT / icon_height)

offset_x = 0
offset_y = 0

for i in range(icon_count):
    o = lv.obj(scrn)
    o.set_size(icon_width, icon_height)
    if offset_x == DISPLAY_WIDTH:
        offset_x = 0
        offset_y += icon_height

    o.set_pos(offset_x, offset_y)

    label = lv.label(o)
    label.set_text(str(i))
    label.center()

    o.add_event_cb(drag_event_handler, lv.EVENT.PRESSING, None)
    o.add_event_cb(released_event_handler, lv.EVENT.RELEASED, None)
    offset_x += icon_width

You can make it fancier using animations but this gives you a starting point. The code will need to be converted from Python to C code but not that hard to do.

Thank you for your support. I will try to convert it to C later and try the effect on the screen.

Hello, bother you, the above python you run, I have a problem converting out!If you get it up and running, can you share the C code?

This is the result of my conversion yesterday, you can refer to it, but he still has some questions.I am currently completing other features of the project, and I will continue to explore this feature when I have time in the future.

#define DISPLAY_WIDTH 800
#define DISPLAY_HEIGHT 480

lv_obj_t *ui_Screen1;
int icon_width = 0;
int icon_height = 0;
static void released_event_handler(lv_event_t * e)
{
    lv_obj_t * obj = lv_event_get_target(e);
    lv_indev_t * indev = lv_indev_get_act();
    int child_count = lv_obj_get_child_cnt(ui_Screen1);
    int offset_x = 0;
    int offset_y = 0;
    for(int i=0;i<child_count;i++)
    {
        lv_obj_t *child_obj = lv_obj_get_child(ui_Screen1,i);
        if(child_obj == obj)  continue;
        lv_coord_t x = lv_obj_get_x(child_obj);
        lv_coord_t y = lv_obj_get_y(child_obj);
        if(x != offset_x) break;
        offset_x += icon_width; 
        if(offset_x == DISPLAY_WIDTH) //todo >=?
        {
            offset_x = 0;
            offset_y += icon_height; 
        }
    }
    lv_obj_move_foreground(obj);
    lv_obj_set_pos(obj,offset_x, offset_y);
} 

int found_overlap = 0; // 假设没有重叠
void drag_event_handler(lv_event_t *e)
{
    lv_obj_t * obj = lv_event_get_target(e);
    lv_indev_t * indev = lv_indev_get_act();
    lv_point_t vect;
    int i,j = 0;
    lv_indev_get_vect(indev, &vect);
    if((vect.x) || (vect.y))
    {
        lv_point_t p;
        found_overlap = 0;
        lv_indev_get_point(indev,&p);
        int x = p.x;
        int y = p.y;
        int child_count = lv_obj_get_child_cnt(ui_Screen1);
        for(i = 0;i<child_count;i++)
        {
            lv_obj_t *child_obj = lv_obj_get_child(ui_Screen1,i);
            if(child_obj == obj)  continue;
            int x1 = lv_obj_get_x(child_obj);
            int y1 = lv_obj_get_y(child_obj);
            int x2 = x1 + icon_width;
            int y2 = y1 + icon_height;
            if(x1 < x && x < x2 && y1 < y && y < y2) 
            {
                found_overlap = 1;
                break;
            }
        }
        if(!found_overlap)
        {
            lv_obj_move_foreground(obj);
            lv_obj_set_pos(obj, lv_obj_get_x(obj) + vect.x, lv_obj_get_y(obj) + vect.y);
            return;
        }
        int offset_x = 0;
        int offset_y = 0;
        for(j=0;j<child_count;j++)
        {
            lv_obj_t *child = lv_obj_get_child(ui_Screen1,j);
            if (offset_x == DISPLAY_WIDTH)//todo >=?
            {
                offset_x = 0;
                offset_y += icon_height; 
            }
            if(i == j) 
            {
                offset_x += icon_width;
            }
            if (offset_x == DISPLAY_WIDTH)//todo >=?
            {
                offset_x = 0;
                offset_y += icon_height;
            }
            if (child != obj)
            {
                lv_obj_set_pos(child, offset_x, offset_y);
            }
            offset_x += icon_width; 
        }
        lv_obj_move_foreground(obj);
        lv_obj_set_pos(obj, lv_obj_get_x(obj) + vect.x, lv_obj_get_y(obj) + vect.y); 
    }
}

int get_icon_size(int min_size, int max_size, int ideal_size, int total_size) {
    int sizes[100]; 
    int sizes_count = 0; 

    for (int i = min_size; i <= max_size; i++) {
        if (total_size % i != 0) {
            continue;
        }

        
        sizes_count++;
        sizes[sizes_count - 1] = i;
    }

    int found_size = total_size;

    for (int i = 0; i < sizes_count; i++) {
        int diff1 = abs(sizes[i] - ideal_size);
        int diff2 = abs(found_size - ideal_size);

        if (diff1 < diff2) {
            found_size = sizes[i];
        }
    }

    

    return found_size;
}
void ui_Screen1_screen_init(void)
{
    ui_Screen1 = lv_obj_create(NULL);
    lv_obj_clear_flag( ui_Screen1, LV_OBJ_FLAG_SCROLLABLE );    /// Flags
    lv_obj_set_style_bg_color(ui_Screen1, lv_color_hex(0x0B0101), LV_PART_MAIN | LV_STATE_DEFAULT );
    lv_obj_set_style_bg_opa(ui_Screen1, 255, LV_PART_MAIN| LV_STATE_DEFAULT);
    
    int offset_x = 0;
    int offset_y = 0;

    icon_width = get_icon_size(180, 200, 200, DISPLAY_WIDTH);
    icon_height = get_icon_size(80, 100, 100, DISPLAY_HEIGHT);
    int icon_count = (DISPLAY_WIDTH / icon_width) * (DISPLAY_HEIGHT / icon_height);
    for(int i=0;i<icon_count;i++)
    {
        lv_obj_t *obj = lv_obj_create(ui_Screen1);
        lv_obj_set_size(obj,icon_width,icon_height);
        if (offset_x == DISPLAY_WIDTH)
        {
            offset_x = 0;
            offset_y += icon_height;
        }
        lv_obj_set_pos(obj,offset_x,offset_y);
        
        lv_obj_t *label = lv_label_create(obj);
        lv_label_set_text_fmt(label,"%d",i);
        lv_obj_center(label);

        lv_obj_add_event_cb(obj, drag_event_handler, LV_EVENT_PRESSING, NULL);
        lv_obj_add_event_cb(obj, released_event_handler, LV_EVENT_RELEASED, NULL);

        offset_x += icon_width;
    }
}

Ok, I have received it, I will try it now!

I have made some optimizations and the results will be better than before. You can try again.

#include "../ui.h"

#define DISPLAY_WIDTH 800
#define DISPLAY_HEIGHT 480
int icon_width = 0;
int icon_height = 0;
lv_obj_t *ui_Screen1;
static void released_event_handler(lv_event_t * e)
{
    lv_obj_t * obj = lv_event_get_target(e);
    lv_indev_t * indev = lv_indev_get_act();
    int child_count = lv_obj_get_child_cnt(ui_Screen1);
    int offset_x = 0;
    int offset_y = 0;
    int i = 0;
    for(i=0;i<child_count;i++)
    {
        lv_obj_t *child_obj = lv_obj_get_child(ui_Screen1,i);
        if(child_obj == obj)  continue;
        lv_coord_t x = lv_obj_get_x(child_obj);
        lv_coord_t y = lv_obj_get_y(child_obj);
        if(x != offset_x) break;
        offset_x += icon_width; 
        if(offset_x == DISPLAY_WIDTH) //todo >=?
        {
            offset_x = 0;
            offset_y += icon_height; 
        }
    }
    lv_obj_move_foreground(obj);
    lv_obj_set_pos(obj,offset_x, offset_y);
    lv_obj_move_to_index(obj,i);
} 

int found_overlap = 0; // 假设没有重叠
void drag_event_handler(lv_event_t *e)
{
    lv_obj_t * obj = lv_event_get_target(e);
    lv_indev_t * indev = lv_indev_get_act();
    lv_point_t vect;
    int i,j = 0;
    lv_indev_get_vect(indev, &vect);
    if((vect.x) || (vect.y)) 
    {
        lv_point_t p;
        found_overlap = 0;
        lv_indev_get_point(indev,&p);
        int x = lv_obj_get_x(obj)+icon_width;
        int y = lv_obj_get_y(obj)+(icon_height/2);
        int child_count = lv_obj_get_child_cnt(ui_Screen1);
        for(i = 0;i<child_count;i++)
        {
            lv_obj_t *child_obj = lv_obj_get_child(ui_Screen1,i);
            if(child_obj == obj)  continue;
            
                int x1 = lv_obj_get_x(child_obj);
                int y1 = lv_obj_get_y(child_obj);
                int x2 = x1 + icon_width;
                int y2 = y1 + icon_height;
                if((x1 < x && x < x2 && y1 < y && y < y2))
                {
                    found_overlap = 1;
                    break;
                }
        }
        if(!found_overlap)
        {
            lv_obj_move_foreground(obj);
            lv_obj_set_pos(obj, lv_obj_get_x(obj) + vect.x, lv_obj_get_y(obj) + vect.y);
            return;
        }
        int offset_x = 0;
        int offset_y = 0;
        for(j=0;j<child_count;j++)
        {
            lv_obj_t *child = lv_obj_get_child(ui_Screen1,j);
            if (offset_x == DISPLAY_WIDTH)//todo >=?
            {
                offset_x = 0;
                offset_y += icon_height;
            }
            if(i == j) 
            {
                offset_x += icon_width;
            }
            if (offset_x == DISPLAY_WIDTH)//todo >=?
            {
                offset_x = 0;
                offset_y += icon_height;
            }
            if (child != obj)
            {
                lv_obj_set_pos(child, offset_x, offset_y);
                lv_obj_move_to_index(child,j);
            }
            offset_x += icon_width; 
        }
        lv_obj_move_foreground(obj);
        lv_obj_set_pos(obj, lv_obj_get_x(obj) + vect.x, lv_obj_get_y(obj) + vect.y); 
    }
}

int get_icon_size(int min_size, int max_size, int ideal_size, int total_size) {
    int sizes[100]; 
    int sizes_count = 0; 

    for (int i = min_size; i <= max_size; i++) {
        if (total_size % i != 0) {
            continue;
        }
        sizes_count++;
        sizes[sizes_count - 1] = i;
    }

    int found_size = total_size;

    for (int i = 0; i < sizes_count; i++) {
        int diff1 = abs(sizes[i] - ideal_size);
        int diff2 = abs(found_size - ideal_size);

        if (diff1 < diff2) {
            found_size = sizes[i];
        }
    }

    

    return found_size;
}
void ui_Screen1_screen_init(void)
{
    ui_Screen1 = lv_obj_create(NULL);
    lv_obj_clear_flag( ui_Screen1, LV_OBJ_FLAG_SCROLLABLE );    /// Flags
    lv_obj_set_style_bg_color(ui_Screen1, lv_color_hex(0x0B0101), LV_PART_MAIN | LV_STATE_DEFAULT );
    lv_obj_set_style_bg_opa(ui_Screen1, 255, LV_PART_MAIN| LV_STATE_DEFAULT);
    lv_obj_set_style_pad_all(ui_Screen1,0,0);
    
    int offset_x = 0;
    int offset_y = 0;

    icon_width = get_icon_size(180, 200, 200, DISPLAY_WIDTH);
    icon_height = get_icon_size(80, 100, 100, DISPLAY_HEIGHT);
    int icon_count = (DISPLAY_WIDTH / icon_width) * (DISPLAY_HEIGHT / icon_height);
    for(int i=0;i<icon_count;i++)
    {
        lv_obj_t *obj = lv_obj_create(ui_Screen1);
        lv_obj_set_size(obj,icon_width,icon_height);
        if (offset_x == DISPLAY_WIDTH)
        {
            offset_x = 0;
            offset_y += icon_height;
        }
        lv_obj_set_pos(obj,offset_x,offset_y);
        
        lv_obj_t *label = lv_label_create(obj);
        lv_label_set_text_fmt(label,"%d",i);
        lv_obj_center(label);

        lv_obj_add_event_cb(obj, drag_event_handler, LV_EVENT_PRESSING, NULL);
        lv_obj_add_event_cb(obj, released_event_handler, LV_EVENT_RELEASED, NULL);

        offset_x += icon_width;
    }
    
}```
1 Like

Okay, I’ll try it now

@Carol-001

That looks pretty good. Not too terribly hard to port from Python to C.

I didn’t do anything fancy with it like animating the icon movements which can be done as well. I might mess about with doing that to see what it ends up looking like. It could be pretty cool looking.

1 Like

I tried adding some animations, but running animations at high drag speeds would have other negative effects, so I only added animations when I let go at the end of the drag

You have to mitigate the animations and when they are running. So if the icons shuffle you would need to alter the positioning values so the animation would be able to adjust for the new positions. Or you have to cancel an already running animation and add a new one. This you would only want to do when the dragging of an icon passed the 1/2 way point over another icon which would mean it needed to move the icons around.

Instead of using animations I would opt for using timers and when the timer callback function gets called then you would adjust the positioning as needed. This makes it easier to alter any positioning values and it also makes it easier to cancel.