GuiLite开源GUI学习(二):控件


GuiLite项目地址

简单类

theme

声明一个c_theme实例,然后就可以向这个主题实例中添加一些主题素材,比如字体类型,位图等,后面需要用到时再从中取出即可。

  • 添加:add_font,add_bitmap,add_color;
  • 取出:get_font,get_bitmap,get_color

bitmap

没有属性,只有方法,并且比较少。
bitmap类的方法:

static void draw_bitmap(c_surface* surface, int z_order, const BITMAP_INFO *pBitmap, int x, int y, unsigned int mask_rgb = DEFAULT_MASK_COLOR);

// 我明白这个函数的意思了:
// (x,y)表示surface的坐标,即bitmap从那开始显示
// (src_x,src_y,w,h)表示bitmap的内容
// 即将bitmap的(src_x,src_y,w,h)区域的内容显示到(x,y)处,具体应用可见HelloGuiLite中的start_menu的on_paint()函数
static void draw_bitmap(c_surface* surface, int z_order, const BITMAP_INFO* pBitmap, int x, int y, int src_x, int src_y, int width, int height, unsigned int mask_rgb = DEFAULT_MASK_COLOR);

word

没有属性,只有方法,且跟画图相关。

方法(部分):

void draw_string(c_surface* surface, int z_order, const char *s, int x, int y, const FONT_INFO* font, unsigned int font_color, unsigned int bg_color, unsigned int align_type = ALIGN_LEFT);
// align_type对其这个参数在矩形中draw_string时才有用,如
void draw_string_in_rect(c_surface* surface, int z_order, const char *s, c_rect rect, const FONT_INFO* font, unsigned int font_color, unsigned int bg_color, unsigned int align_type = ALIGN_LEFT);

// 如:显示-->:祝GuiLite开发者:
c_word::draw_string(s_surface, Z_ORDER_LEVEL_0, "\xe7\xa5\x9d\x47\x75\x69\x4c\x69\x74\x65\xe5\xbc\x80\xe5\x8f\x91\xe8\x80\x85\xef\xbc\x9a", 10, 10, c_theme::get_font(FONT_DEFAULT), GL_RGB(255, 0, 0), GL_ARGB(0, 0, 0, 0));

c_wnd-难点

虽有的控件,比如button、label、dialog都继承自wnd。因此c_wnd只定义基本属性和方法,更多的属性和方法根据控件来定义。

控件的回调函数:typedef void (c_wnd::*WND_CALLBACK)(int, int);

核心变量WND_TREE

typedef struct struct_wnd_tree
{
	c_wnd*					p_wnd;//window instance,类变量
	unsigned int			resource_id;//ID
	const char*				str;//caption
	short   				x;//position x
	short   				y;//position y
	short   				width;
	short        			height;
	struct struct_wnd_tree*	p_child_tree;//sub tree,指向同类,链表
}WND_TREE;

c_wnd的属性:

unsigned short	m_id;
WND_STATUS		m_status;
WND_ATTRIBUTION	m_attr;
c_rect			m_wnd_rect;		//position relative to parent window.
c_wnd*			m_parent;		//parent window
c_wnd*			m_top_child;	//the first sub window would be navigated,第一个孩子
c_wnd*			m_prev_sibling;	//previous brother,前一个兄弟
c_wnd*			m_next_sibling;	//next brother,下一个兄弟
c_wnd*			m_focus_child;	//current focused window,当前孩子(应该指自己)
const char*		m_str;			//caption(字符内容,比如名字等属性,自定义)

const FONT_INFO*	m_font_type;
unsigned int		m_font_color;
unsigned int		m_bg_color;

int					m_z_order;		//the graphic level for rendering
c_surface*			m_surface;		///指向一个surface,这很重要

c_wnd的方法:

// 在connet中会设置surface,并且会调用pre_create_wnd(),add_child_2_tail(),load_child_wnd()
// 特别是on_init_children();
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);
// 渲染自己和子窗口,show_windows会调用on_paint
void show_windows();

// 设置wnd当前focus的子wnd
set_active_child(c_wnd* child) { m_focus_child = child; }

WND_TREE s_dialog_widgets[] = {
    // 这些坐标都是相对于父窗口左上角的坐标而言,即将父窗口的左上角视为(0,0)
	{ &s_dialog_button,	ID_DIALOG_BUTTON,	"Button",	100, 100, 100, 50},
};
WND_TREE s_main_widgets[] = {
	{ &s_my_dialog,	ID_DIALOG,	"Dialog",	200, 100, 280, 312, s_dialog_widgets},
};
connect(NULL, ID_ROOT, 0, 0, 0, UI_WIDTH, UI_HEIGHT, s_main_widgets);
/// 对于s_dialog_button来说:
// 得到的是(100, 100, 100, 50)
void get_wnd_rect(c_rect &rect);
// 得到的是(300, 200, 100, 50)
void get_screen_rect(c_rect &rect);

控件管理

消息传递

从根节点传入,即调用根节点的ou_touch():子窗口的on_touch()可根据实际情况进行重写,但是调用on_touch()的父窗口,其中必须能根据传入的坐标值找到子窗口,并调用子窗口的on_touch()

// 外部接口
void sendTouch2HelloWidgets(int x, int y, bool is_down)
{
	is_down ? s_my_ui.on_touch(x, y, TOUCH_DOWN) : s_my_ui.on_touch(x, y, TOUCH_UP);
}

virtual void on_touch(int x, int y, TOUCH_ACTION action)
{
    x -= m_wnd_rect.m_left;
    y -= m_wnd_rect.m_top;

    c_wnd* priority_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)) {
            priority_wnd = tmp_child;
            break;
        }
        tmp_child = tmp_child->m_next_sibling;
    }
    if (priority_wnd) {
        return priority_wnd->on_touch(x, y, action);
    }

    c_wnd* child = m_top_child;
    while (child) {
        if (child->is_focus_wnd()) {
            c_rect rect;
            child->get_wnd_rect(rect);
            if (true == rect.pt_in_rect(x, y)) {
                return child->on_touch(x, y, action);
            }
        }
        child = child->m_next_sibling;
    }
}

控件-继承自c_wnd

c_wnd实现了一些基本的方法和属性,自己编写控件时根据需要自行添加。

show_window会调用on_paint,并且会调用自己的子节点的show_window

已实现方法:

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);

// 需重写的方法
virtual void on_init_children();
virtual void on_paint();
// show_window会调用on_paint,并且会调用自己的子节点的show_window
virtual void show_window()
before
属性
m_z_order
*m_surface
m_top_child
m_top_child
sibling
parent
方法
connect
pre_create_child</br>顾名思义,创建</br>孩子前的预操作
load_child_wnd
on_init_children</br>子类应重写
调用孩子的</br>connect
show_window
on_paint</br>子类应重写
调用子节点的</br>show_window
on_touch</br>子类可重写
on_navigation</br>子类可重写

button

action=</br>TOUCH_DOWN
action=else
方法
on_paint
业务代码</br>如画点等
connect
pre_create_wnd</br>可加载字体,颜色等
...
on_init_children
on_focus</br>设置状态
on_kill_focus</br>设置状态
on_touch
m_parent-></br>set_child_focus(this)</br>设置状态
设置状态
on_click
else
on_navigate

dialog

特别属性:

typedef struct
{
	// 说明一个surface对应一个dialog,
    c_dialog* 	dialog;
	c_surface*	surface;
} DIALOG_ARRAY;
static DIALOG_ARRAY ms_the_dialogs[SURFACE_CNT_MAX];

// 关闭surface上的dialog
ms_the_dialogs[get_surface()].dialog = 0;
// set_me_the_dialog()
ms_the_dialogs[get_surface(surface)].dialog = this;
方法
open_dialog(*c_dialog,bool)
clsoe_dialog(*c_surface)</br>关闭surface上的dialog
set_attr()
show_window()</br>渲染自己及子窗口
set_me_the_dialog()
get_screen_rect()
show_layer()
on_paint()
ms_the_dialog[i].dialog=0</br>清除dialog

edit

keyboard相搭配

属性
s_keyboard
重写了connect()
m_str_input[]
m_str[]
方法
on_paint()
on_focus()</br>on_kill_focus()
on_touch()
disconnect()
show_layer()
show_keyboard

创建edit后其子窗口如下:

即一个edit包括一个s_keyboard,一个keyboard又包括许多个s_keyboard_button,调用editon_paint()渲染函数时,会根据edit的状态m_status来做动作;要么是disconnect(),要么就调用show_keyboard(),在show_keyboard()中会调用s_keyboard.connect(this, IDD_KEY_BOARD, m_kb_style),然后

属性
注册
注册
方法
c_edit
s_keyboard
s_keyboard-></br>connect(KEYBOARD_STYLE)</br>keyboard的connect是重写的</br>此处可定义键盘位置
g_key_board_children[]
g_number_board_children[]
c_keyboard_button
show_window()</br>渲染自己
on_paint()</br>m_status
show_keyboard()
s_keyboard-></br>show_window()</br>渲染自己及子窗口
s_keyboard_button-></br>show_window()
disconnect()

keyboard

使用时一般都是使用edit,因为edit中有一个s_keyboard成员。

  1. 分为数字键盘(STYLE_ALL_BOARD)和全键盘(STYLE_NUM_BOARD);
  2. 数字键盘(g_number_board_children)和全键盘(g_key_board_children)的定义在keyboard.cpp中;
  3. 键盘一般是软退出,即使用时才加载(connect()),不用时就卸载(disconnect());
  4. 先前一直没有搞清楚键盘的位置是在那里定义的?(重写了connect());

再次说明一下connect()的传入参数,不过keyboard的connect()进行了重写:

// 即connect时,就确定了该wnd的位置坐标!!!
virtual int connect(c_wnd *parent, resource_id, str, x, y, width, height, WND_TREE* p_child_tree = 0);

数字键盘:

全键盘:

extern WND_TREE g_key_board_children[];
extern WND_TREE g_number_board_children[];
class c_keyboard: public c_wnd{};
class c_keyboard_button : public c_button{};

// 键盘类重写了connect函数!!!!此处定义keyboard的位置
virtual int connect(c_wnd *user, unsigned short resource_id, KEYBOARD_STYLE style)
{
    c_rect user_rect;
    user->get_wnd_rect(user_rect);
    if (style == STYLE_ALL_BOARD)
    {//Place keyboard at the bottom of user's parent window.
        c_rect user_parent_rect;
        user->get_parent()->get_wnd_rect(user_parent_rect);
        /// TODO: 此处验证,画出user和user parent的矩形
		user->get_surface()->draw_rect(user_rect, GL_RGB(0, 255, 0), 3, Z_ORDER_LEVEL_0);
        user->get_surface()->draw_rect(user_parent_rect, GL_RGB(255, 0, 0), 3, Z_ORDER_LEVEL_0);
        /// Notice:此处就是定义keyboard的位置,根据用户user的位置来设置,而这个user就是其父亲,
        return c_wnd::connect(user, resource_id, 0, (0 - user_rect.m_left), (user_parent_rect.height() - user_rect.m_top - KEYBOARD_HEIGHT - 1), KEYBOARD_WIDTH, KEYBOARD_HEIGHT, g_key_board_children);
    }
    else if (style == STYLE_NUM_BOARD)
    {//Place keyboard below the user window.
        return c_wnd::connect(user, resource_id, 0, 0, user_rect.height(), NUM_BOARD_WIDTH, NUM_BOARD_HEIGHT, g_number_board_children);
    }
    else
    {
        ASSERT(false);
    }
    return -1;
}
重写渲染函数
c_keyboard
方法
connect()</br>重写,位置就是在这确定的
else
on_key_clicked()
show_window()
on_paint()
属性
g_key_board_children[]
c_keyboard_button:c_button
on_init_children()
set_on_click(WND_CALLBACK)
on_char_clicked()
on_del_clicked()
on_caps_clicked()
...

label

很简单,就是继承了c_wnd,然后重写了on_paint()pre_create_wnd()

方法
on_paint()
get_screen_rect(&rect)
fill_rect(rect)
draw_string_in_rect()</br>显示label内容
pre_create_wnd()
主要就是加载资源</br>根据实际编写

list_box

c_list_box  *list_box = (c_list_box*)get_wnd_ptr(ID_LIST_BOX);
list_box->set_on_change((WND_CALLBACK)&c_my_ui::on_listbox_confirm);
list_box->clear_item();
list_box->add_item((char*)"Item 0");
list_box->add_item((char*)"Item 1");
list_box->add_item((char*)"Item 2");
list_box->select_item(0);

slide_group

参考HelloSlide示例,理解这个重点在于理解函数add_slide(),其中会从display申请新的surface,然后调用connect()

c_slide_group
c_geusture
属性
c_wnd* m_slides[]
m_active_slide_index
m_gesture
方法
add_slide(c_wnd* slide,...)
属性
m_slide_group
方法
handle_swipe(x,y,action)
on_move()
on_swipe()
move_left()
move_right()
swipe_left()
swipe_right()
get_surface()
get_display()</br>alloc_surface()
connect()

spinbox

继承
成员
指向
成员
指向
c_button
c_spin_button
属性
*m_spin_box
c_spin_box
方法
pre_create_wnd()
初始两个c_spin_button
属性</br>有两个c_spin_button
m_bt_up
m_bt_down
set_value()</br>set_max_min()</br>set_step()

使用很简单,不怎么修改就可以使用。

c_spin_box  *spin_box = (c_spin_box*)get_wnd_ptr(ID_SPIN_BOX);
spin_box->set_on_change((WND_CALLBACK)&c_my_ui::on_spinbox_change);
spin_box->set_max_min(9, 0);
spin_box->set_step(1);
spin_box->set_value(5);

table

一些基本方法:设置对齐方式、设置行列数、设置行高列宽(可单独设置,即每行或每列不必一样宽)

方法
set_item(row,col,*str,rgb)
get_item_rect()
draw_item(同)
属性
m_aligh_type
m_row_num</br>m_col_num
m_row_height[MAX_ROW_NUM]</br>m_row_width[MAX_COL_NUM]

wave_buffer

其实就是一个环形队列,即包括m_head,m_tail,m_wave_data[],以及write_data(data),read_data()。但是为了更好使用,还添加了其它辅助属性,

属性:

short m_wave_buf[WAVE_BUFFER_LEN];	//WAVE_BUFFER_LEN=1024
short m_head;
short m_tail;

方法:

int write_wave_data(short data);
int read_data();
// 会调用read_data(),此函数的意思是读一帧数据,长度为frame_len
int read_wave_data_by_frame(short &max, short &min, short frame_len, unsigned int sequence, short offset);
// 但是sequence和offset是什么意思呢?

wave_buffer</br>循环队列
read_wave_data_by_frame
read_data
write_wave_data
属性
m_wave_buf
m_head
m_tail
其它辅助属性

wave_ctrl

其实就是针对wave_buffer的一些操作函数

方法:

// 设置wave_ctrl对应的wave_buffer
void set_wave(c_wave_buffer* wave){m_wave = wave;}
// 一个是数据率(采样率);一个是刷新率(出去率),单位是times/ms
void set_wave_in_out_rate(unsigned int data_rate, unsigned int refresh_rate);
void refresh_wave(unsigned char frame);

// 得到当前wnd的外框在屏幕上的位置(会根据父节点来计算)
void get_screen_rect(rect);
// 只是得到其属性m_rect
void get_wnd_rect(&rect);

/// 受保护的方法
void draw_smooth_vline(int y_min, int y_max, int mid, unsigned int rgb);
// wave_ctrl有一个m_bg_fb,用于绘制背景(比如刻度)
// 该函数是用于恢复背景,即将m_bg_fb的内容显示到屏幕上(调用surface的draw_pixel)
void restore_background();
// 该函数是把屏幕的值写入m_bg_fb中取,即将当前屏幕的值当作背景
void save_background()
refresh_wave
read_wave_data_by_frame
draw_smooth_vline
restore_background
get_screen_rect
save_background
wnd2screen
属性
*wave_buffer
bg_fb

也可以实现自定义控件

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值