GuiLite 学习笔记(一) Mainloop与ViewTree

以GuiLiteSamples中的HelloSlide 为例,剖析一下GuiLite的设计思路和刷新机制;

首先是main.cpp; 可以分成3部分:

1、根据fb mode拿到对应的phy_fb, 后续的绘制都在这个fb上执行;

2、init _std_io(), 初始化输入设备,这里创建一个线程专门用于输入事件的poll, 检测到对应事件后, 直接调用c_slide_group的on_touch方法;

void sendTouch2HelloSlide(int x, int y, bool is_down)
{
    is_down ? s_root.on_touch(x, y, TOUCH_DOWN) : s_root.on_touch(x, y, TOUCH_UP);
}

这个s_root就是在layout UI的时候我们创建的一个static的c_slide_group;

//////////////////////// layout UI ////////////////////////
c_page s_page1, s_page2, s_page3, s_page4, s_page5;
static c_slide_group s_root;
static WND_TREE s_root_children[] =
{
    { NULL,0,0,0,0,0,0 }
};

3、startHelloSlide(phy_fb, screen_width, screen_height, color_bytes);//never return;

摘取关键的代码片段进行剖析:

在UIcode.cpp中,有UI初始化的code

void create_ui(void* phy_fb, int screen_width, int screen_height, int color_bytes)
{
    load_resource();
    s_display = new c_display(phy_fb, screen_width, screen_height, UI_WIDTH, UI_HEIGHT, color_bytes, (1 + 5)/*1 root + 5 pages*/);
    c_surface* surface = s_display->alloc_surface(Z_ORDER_LEVEL_1);
    surface->set_active(true);

    s_root.set_surface(surface);
    s_root.connect(NULL, ID_ROOT, 0, 0, 0, UI_WIDTH, UI_HEIGHT, s_root_children);

    s_root.add_slide(&s_page1, ID_PAGE1, 0, 0, UI_WIDTH, UI_HEIGHT, NULL);
    s_root.add_slide(&s_page2, ID_PAGE2, 0, 0, UI_WIDTH, UI_HEIGHT, NULL);
    s_root.add_slide(&s_page3, ID_PAGE3, 0, 0, UI_WIDTH, UI_HEIGHT, NULL);
    s_root.add_slide(&s_page4, ID_PAGE4, 0, 0, UI_WIDTH, UI_HEIGHT, NULL);
    s_root.add_slide(&s_page5, ID_PAGE5, 0, 0, UI_WIDTH, UI_HEIGHT, NULL);
    s_root.set_active_slide(0);
    s_root.show_window();

    while(1)
    {
        thread_sleep(1000000);
    }
}

//////////////////////// interface for all platform ////////////////////////
void startHelloSlide(void* phy_fb, int width, int height, int color_bytes)
{
    create_ui(phy_fb, width, height, color_bytes);
}

上面只是在比较浅显的层面看了一下,现在要深入进入,看看slide的操作是如何传递的,以及重绘是如何发生的:

首先是s_root.on_touch() 方法;我们在GuiLite.h 中找到对应的类和方法的实现如下:

 

class c_slide_group : public c_wnd {
public:
...
inline virtual void on_touch(int x, int y, TOUCH_ACTION action);
protected:
    c_wnd* m_slides[MAX_PAGES];
    int m_active_slide_index;
    c_gesture* m_gesture;
};

对应的实现是一个虚函数,虚函数的语义是子类可以重载这个函数,我们先看下在这一层的实现:

 

inline c_slide_group::c_slide_group()
{
    m_gesture = new c_gesture(this);
    for (int i = 0; i < MAX_PAGES; i++)
    {
        m_slides[i] = 0;
    }
    m_active_slide_index = 0;
}
inline void c_slide_group::on_touch(int x, int y, TOUCH_ACTION action)
{
    x -= m_wnd_rect.m_left;
    y -= m_wnd_rect.m_top;
    if (m_gesture->handle_swipe(x, y, action))
    {
        if (m_slides[m_active_slide_index])
        {
            m_slides[m_active_slide_index]->on_touch(x, y, action);
        }
    }
}

 

这里看到的是,最后调了c_wnd类的on_touch方法;实现如下:

virtual void on_touch(int x, int y, TOUCH_ACTION action)
    {
        c_wnd* model_wnd = 0;
        c_wnd* tmp_child = m_top_child;
        while (tmp_child)
        {
            if ((tmp_child->m_attr & ATTR_PRIORITY) && (tmp_child->m_attr & ATTR_VISIBLE))
            {
                model_wnd = tmp_child;
                break;
            }
            tmp_child = tmp_child->m_next_sibling;
        }
        if (model_wnd)
        {
            return model_wnd->on_touch(x, y, action);
        }
        x -= m_wnd_rect.m_left;
        y -= m_wnd_rect.m_top;
        c_wnd* child = m_top_child;
        while (child)
        {
            if (child->is_focus_wnd())
            {
                c_rect rect;
                child->get_wnd_rect(rect);
                if (true == rect.PtInRect(x, y))
                {
                    return child->on_touch(x, y, action);
                }
            }
            child = child->m_next_sibling;
        }
    }

看到这里,基本上接触到GUI的核心概念了,也就是VieeTree。所谓ViewTree是指,GUI的所有View都是以Tree的数据结构来组织的。我们重点关注c_wnd这个类,看看里面到底有何玄机:首先我们看下一个c_wnd是如何添加到Tree的,首先是,connect方法:

virtual int connect(c_wnd *parent, unsigned short resource_id, const char* str,
        short x, short y, short width, short height, WND_TREE* p_child_tree = 0)
    {
        if (0 == resource_id)
        {
            ASSERT(false);
            return -1;
        }
        m_id = resource_id;
        set_str(str);
        m_parent = parent;
        m_status = STATUS_NORMAL;
        if (parent)
        {
            m_z_order = parent->m_z_order;
            m_surface = parent->m_surface;
        }
        if (0 == m_surface)
        {
            ASSERT(false);
            return -2;
        }
        /* (cs.x = x * 1024 / 768) for 1027*768=>800*600 quickly*/
        m_wnd_rect.m_left = x;
        m_wnd_rect.m_top = y;
        m_wnd_rect.m_right = (x + width - 1);
        m_wnd_rect.m_bottom = (y + height - 1);
        c_rect rect;
        get_screen_rect(rect);
        ASSERT(m_surface->is_valid(rect));
        pre_create_wnd();
        if (0 != parent)
        {
            parent->add_child_2_tail(this);
        }
        if (load_child_wnd(p_child_tree) >= 0)
        {
            load_cmd_msg();
            on_init_children();
        }
        return 0;
    }

先看接口,再看实现,首先,一个node添加到Tree上,一定是要首先指定其parent节点的,这个毫无疑问;

如果parent不是NULL,说明很可能,这个节点还有兄弟节点,那么这个时候就要调用parent的add_child_2_tail方法把this这个节点add进去,具体如下:

void add_child_2_tail(c_wnd *child)
    {
        if (0 == child)return;
        if (child == get_wnd_ptr(child->m_id))return;
        if (0 == m_top_child)
        {
            m_top_child = child;
            child->m_prev_sibling = 0;
            child->m_next_sibling = 0;
        }
        else
        {
            c_wnd* last_child = get_last_child();
            if (0 == last_child)
            {
                ASSERT(false);
            }
            last_child->m_next_sibling = child;
            child->m_prev_sibling = last_child;
            child->m_next_sibling = 0;
        }
    }

添加过程叙述如下:

先判空,再判等,这两种情况都无需进行添加操作;然后再判断m_top_child是否为空,m_top_child是当前节点的第一个插入的子节点,所以叫top child, 后面简称长子;若长子为空,则本次插入的child就是长子,否则这次插入的就是兄弟节点,而且要找到当前年龄最小的节点,在其之后进行插入;

这里分析完后,回到刚才的connect函数,继续看后面load_child_wnd的操作:

typedef struct struct_wnd_tree
{
    c_wnd*                    p_wnd;
    unsigned int            resource_id;
    const char*                str;
    short                   x;
    short                   y;
    short                   width;
    short                    height;
    struct struct_wnd_tree*    p_child_tree;
}WND_TREE;
int load_child_wnd(WND_TREE *p_child_tree)
{
    if (0 == p_child_tree)
    {
        return 0;
    }
    int sum = 0;
    WND_TREE* p_cur = p_child_tree;
    while (p_cur->p_wnd)
    {
        if (0 != p_cur->p_wnd->m_id)
        {//This wnd has been used! Do not share!
            ASSERT(false);
            return -1;
        }
        else
        {
            p_cur->p_wnd->connect(this, p_cur->resource_id, p_cur->str,
                p_cur->x, p_cur->y, p_cur->width, p_cur->height, p_cur->p_child_tree);
        }
        p_cur++;
        sum++;
    }
    return sum;
}

注意,如果这个添加的子树不为空的话,这里会产生递归的操作,也就是说在connect中,又产生了connect。

再次回到之前对c_slide_group类的on_touch方法的回顾,可以看到这个类自带c_gestrure; 通过这个

m_gesture->handle_swipe(x, y, action)

 处理swipe的操作,这里会影响m_active_slide_index, 以及gestrue处理后还会让c_slide_group继续在更新后的m_active_slide_index处理swipe的事件;

可以猜到,画面的刷新就是在这里完成的,具体类和时序,我们下一章继续分析;

 

posted on 2022-12-18 18:16  疾速瓜牛  阅读(433)  评论(0编辑  收藏  举报

导航