CARPLAY版本整理

This commit is contained in:
2025-01-21 16:49:37 +08:00
commit f0fb64e4e6
26542 changed files with 13719676 additions and 0 deletions

View File

@ -0,0 +1,564 @@
/**
* File: children_layouter_list_view_list_view.c
* Author: AWTK Develop Team
* Brief: children layouter list view
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-05-22 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include "tkc/rect.h"
#include "tkc/utils.h"
#include "base/layout.h"
#include "base/widget.h"
#include "tkc/tokenizer.h"
#include "scroll_view/scroll_bar.h"
#include "scroll_view/scroll_view.h"
#include "scroll_view/list_view.h"
#include "scroll_view/list_view_h.h"
#include "scroll_view/children_layouter_list_view.h"
static const char* children_layouter_list_view_to_string(children_layouter_t* layouter) {
char temp[32];
str_t* str = &(layouter->params);
children_layouter_list_view_t* layout = (children_layouter_list_view_t*)layouter;
return_value_if_fail(layout != NULL, NULL);
memset(temp, 0, sizeof(temp));
str_set(str, "list_view(");
str_append(str, temp);
if (layout->item_height) {
tk_snprintf(temp, sizeof(temp) - 1, "item_height=%d,", (int)(layout->item_height));
}
str_append(str, temp);
if (layout->default_item_height) {
tk_snprintf(temp, sizeof(temp) - 1, "default_item_height=%d,",
(int)(layout->default_item_height));
}
str_append(str, temp);
if (layout->x_margin) {
tk_snprintf(temp, sizeof(temp) - 1, "x_margin=%d,", (int)(layout->x_margin));
str_append(str, temp);
}
if (layout->y_margin) {
tk_snprintf(temp, sizeof(temp) - 1, "y_margin=%d,", (int)(layout->y_margin));
str_append(str, temp);
}
if (layout->spacing) {
tk_snprintf(temp, sizeof(temp) - 1, "spacing=%d,", (int)(layout->spacing));
str_append(str, temp);
}
if (layout->cols > 1) {
tk_snprintf(temp, sizeof(temp) - 1, "cols=%d,", (int)(layout->cols));
str_append(str, temp);
}
if (!(layout->keep_disable)) {
str_append(str, "keep_disable=false,");
}
if (layout->keep_invisible) {
str_append(str, "keep_invisible=true,");
}
if (layout->hlayouter) {
str_append(str, "hlayouter=true,");
}
str_trim_right(str, ",");
str_append(str, ")");
return str->str;
}
static ret_t children_layouter_list_view_set_param(children_layouter_t* layouter, const char* name,
const value_t* v) {
int val = value_int(v);
children_layouter_list_view_t* l = (children_layouter_list_view_t*)layouter;
switch (*name) {
case 'x': {
l->x_margin = val;
break;
}
case 'y': {
l->y_margin = val;
break;
}
case 'm': {
l->x_margin = val;
l->y_margin = val;
break;
}
case 's': {
l->spacing = val;
break;
}
case 'i': {
l->item_height = val;
break;
}
case 'd': {
l->default_item_height = val;
break;
}
case 'c': {
l->cols = val;
break;
}
case 'k': {
if (strstr(name, "invisible") != NULL || name[1] == 'i') {
l->keep_invisible = value_bool(v);
} else if (strstr(name, "disable") != NULL || name[1] == 'd') {
l->keep_disable = value_bool(v);
}
break;
}
case 'h': {
if (strstr(name, "layouter") != NULL || name[1] == 'l') {
l->hlayouter = value_bool(v);
}
break;
}
default:
break;
}
return RET_OK;
}
static ret_t children_layouter_list_view_get_param(children_layouter_t* layouter, const char* name,
value_t* v) {
children_layouter_list_view_t* l = (children_layouter_list_view_t*)layouter;
switch (*name) {
case 'x': {
value_set_int(v, l->x_margin);
return RET_OK;
}
case 'y': {
value_set_int(v, l->y_margin);
return RET_OK;
}
case 'm': {
value_set_int(v, l->x_margin);
return RET_OK;
}
case 's': {
value_set_int(v, l->spacing);
return RET_OK;
}
case 'i': {
value_set_int(v, l->item_height);
return RET_OK;
}
case 'd': {
value_set_int(v, l->default_item_height);
return RET_OK;
}
case 'c': {
value_set_int(v, l->cols);
return RET_OK;
}
case 'k': {
if (strstr(name, "invisible") != NULL || name[1] == 'i') {
value_set_bool(v, l->keep_invisible);
return RET_OK;
} else if (strstr(name, "disable") != NULL || name[1] == 'd') {
value_set_bool(v, l->keep_disable);
return RET_OK;
}
break;
}
case 'h': {
if (strstr(name, "layouter") != NULL || name[1] == 'l') {
value_set_bool(v, l->hlayouter);
}
break;
}
default: {
assert(!"not support param");
break;
}
}
return RET_FAIL;
}
static ret_t children_layouter_list_view_for_list_view_children_layout_h(
darray_t* children_for_layout, int32_t item_height, int32_t default_item_height) {
int32_t i = 0;
int32_t h = 0;
int32_t n = 0;
widget_t** children = NULL;
return_value_if_fail(children_for_layout != NULL, RET_BAD_PARAMS);
n = children_for_layout->size;
children = (widget_t**)children_for_layout->elms;
for (i = 0; i < n; i++) {
widget_t* iter = children[i];
if (iter->w == 0) {
iter->w = iter->parent->w;
}
if (iter->h == 0) {
iter->h = item_height;
}
if (iter->self_layout != NULL || iter->auto_adjust_size) {
widget_layout(iter);
}
h = item_height;
if (h <= 0) {
h = iter->h;
}
if (h <= 0) {
h = default_item_height;
}
widget_resize(iter, iter->w, h);
}
return RET_OK;
}
static ret_t children_layouter_list_view_for_list_view_children_layout_w(
darray_t* children_for_layout, uint32_t cols, int32_t x_margin, int32_t y_margin,
int32_t spacing, int32_t scroll_view_w) {
int32_t i = 0;
int32_t w = 0;
int32_t x = x_margin;
int32_t y = y_margin;
widget_t** children = NULL;
return_value_if_fail(children_for_layout != NULL, RET_BAD_PARAMS);
w = scroll_view_w - 2 * x_margin;
children = (widget_t**)children_for_layout->elms;
if (cols <= 1) {
for (i = 0; i < children_for_layout->size; i++) {
widget_t* iter = children[i];
widget_move_resize(iter, x, y, w, iter->h);
widget_layout_children(iter);
y += (iter->h + spacing);
}
} else {
int32_t j = 0;
int32_t n = 0;
int32_t h = 0;
int32_t size = children_for_layout->size;
uint32_t rows = (size % cols) ? (size / cols) + 1 : (size / cols);
w = (w - (cols - 1) * spacing) / cols;
for (i = 0; i < rows && n < size; i++) {
h = 0;
for (j = 0; j < cols && n < size; j++, n++) {
widget_t* iter = children[n];
int32_t tmp_x = x + j * (w + spacing);
widget_move_resize(iter, tmp_x, y, w, iter->h);
widget_layout_children(iter);
h = tk_max(h, iter->h);
}
y += (h + spacing);
}
}
return RET_OK;
}
static int32_t children_layouter_list_view_for_list_view_get_virtual_h(
darray_t* children_for_layout, uint32_t cols, int32_t y_margin, int32_t spacing) {
int32_t i = 0;
widget_t** children = NULL;
int32_t virtual_h = y_margin;
return_value_if_fail(children_for_layout != NULL, 0);
children = (widget_t**)children_for_layout->elms;
if (cols <= 1) {
for (i = 0; i < children_for_layout->size; i++) {
virtual_h += (children[i]->h + spacing);
}
} else {
int32_t j = 0;
int32_t h = 0;
int32_t num = 0;
int32_t n = children_for_layout->size;
uint32_t rows = (n % cols) ? (n / cols) + 1 : (n / cols);
for (i = 0; i < rows && num < children_for_layout->size; i++) {
for (j = 0, h = 0; j < cols && num < children_for_layout->size; j++, num++) {
h = tk_max(h, children[num]->h);
}
virtual_h += (h + spacing);
}
}
return virtual_h;
}
static int32_t children_layouter_list_view_for_list_view_get_scroll_view_w(list_view_t* list_view,
widget_t* scroll_view,
int32_t virtual_h) {
int32_t scroll_view_w = 0;
widget_t* scroll_bar = NULL;
return_value_if_fail(list_view != NULL && scroll_view != NULL, 0);
scroll_bar = list_view->scroll_bar;
if (list_view->floating_scroll_bar || scroll_bar == NULL || scroll_bar_is_mobile(scroll_bar) ||
(!scroll_bar_is_mobile(scroll_bar) && list_view->auto_hide_scroll_bar &&
scroll_view->h >= virtual_h)) {
scroll_view_w = list_view->widget.w;
} else {
scroll_view_w = list_view->widget.w - scroll_bar->w;
}
return scroll_view_w;
}
static ret_t children_layouter_list_view_for_list_view_set_scroll_view_info(widget_t* widget,
widget_t* scroll_bar,
int32_t virtual_h) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_BAD_PARAMS);
if (scroll_bar_is_mobile(scroll_bar)) {
scroll_view_set_yslidable(widget, TRUE);
}
scroll_view_set_xslidable(widget, FALSE);
scroll_view_set_virtual_h(widget, virtual_h);
if (!scroll_view->dragged && scroll_view->wa == NULL) {
if (widget->h >= virtual_h) {
scroll_view_set_offset(widget, 0, 0);
}
scroll_view->xoffset = 0;
if (scroll_view->yoffset + widget->h > scroll_view->virtual_h) {
scroll_view->yoffset = scroll_view->virtual_h - widget->h;
scroll_view->yoffset = scroll_view->yoffset > 0 ? scroll_view->yoffset : 0;
}
if (scroll_view->on_scroll) {
scroll_view->on_scroll(widget, scroll_view->xoffset, scroll_view->yoffset);
}
}
return RET_OK;
}
static ret_t children_layouter_list_view_for_list_view_set_scroll_bar_info(widget_t* widget,
list_view_t* list_view,
widget_t* scroll_view,
int32_t virtual_h,
int32_t item_height) {
scroll_view_t* ascroll_view = SCROLL_VIEW(scroll_view);
return_value_if_fail(list_view != NULL && ascroll_view != NULL, RET_BAD_PARAMS);
scroll_bar_set_params(widget, virtual_h, item_height);
if (scroll_bar_is_mobile(widget)) {
if (widget->h > virtual_h) {
scroll_bar_set_params(widget, widget->h, item_height);
}
if (SCROLL_BAR(widget)->auto_hide && !ascroll_view->dragged && ascroll_view->wa == NULL) {
widget_set_visible_only(widget, FALSE);
}
} else {
if (scroll_view->h >= virtual_h) {
scroll_bar_set_value(widget, 0);
if (list_view->auto_hide_scroll_bar || list_view->floating_scroll_bar) {
widget_set_visible_only(widget, FALSE);
} else {
widget_set_enable(widget, FALSE);
widget_set_visible_only(widget, TRUE);
}
} else {
if (list_view->auto_hide_scroll_bar && list_view->floating_scroll_bar) {
widget_set_visible_only(widget, list_view->is_over);
} else {
widget_set_enable(widget, TRUE);
widget_set_visible_only(widget, TRUE);
}
}
}
return RET_OK;
}
static ret_t children_layouter_list_view_for_list_view_layout(children_layouter_t* layouter,
widget_t* widget) {
int32_t virtual_h = 0;
int32_t item_height = 0;
list_view_t* list_view = NULL;
int32_t default_item_height = 0;
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
children_layouter_list_view_t* l = (children_layouter_list_view_t*)layouter;
return_value_if_fail(widget != NULL && scroll_view != NULL && l != NULL, RET_BAD_PARAMS);
list_view = LIST_VIEW(widget->parent);
return_value_if_fail(list_view != NULL, RET_BAD_PARAMS);
item_height = list_view->item_height ? list_view->item_height : l->item_height;
default_item_height =
list_view->default_item_height ? list_view->default_item_height : l->default_item_height;
if (widget->children != NULL) {
int32_t scroll_view_w = 0;
darray_t children_for_layout;
uint32_t cols = l->cols <= 1 ? 1 : l->cols;
widget_layout_floating_children(widget);
darray_init(&children_for_layout, widget->children->size, NULL, NULL);
return_value_if_fail(
widget_get_children_for_layout(widget, &children_for_layout, l->keep_disable,
l->keep_invisible) == RET_OK,
RET_BAD_PARAMS);
children_layouter_list_view_for_list_view_children_layout_h(&children_for_layout, item_height,
default_item_height);
virtual_h = children_layouter_list_view_for_list_view_get_virtual_h(&children_for_layout, cols,
l->y_margin, l->spacing);
scroll_view_w =
children_layouter_list_view_for_list_view_get_scroll_view_w(list_view, widget, virtual_h);
widget_move_resize_ex(widget, widget->x, widget->y, scroll_view_w, widget->h, FALSE);
children_layouter_list_view_for_list_view_children_layout_w(
&children_for_layout, cols, l->x_margin, l->y_margin, l->spacing, scroll_view_w);
darray_deinit(&(children_for_layout));
}
children_layouter_list_view_for_list_view_set_scroll_view_info(widget, list_view->scroll_bar,
virtual_h);
if (list_view->scroll_bar != NULL) {
children_layouter_list_view_for_list_view_set_scroll_bar_info(list_view->scroll_bar, list_view,
widget, virtual_h, item_height);
}
return RET_OK;
}
static ret_t children_layouter_list_view_for_list_view_h_layout(children_layouter_t* layouter,
widget_t* widget) {
int32_t virtual_w = 0;
list_view_h_t* list_view_h = NULL;
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
children_layouter_list_view_t* l = (children_layouter_list_view_t*)layouter;
return_value_if_fail(widget != NULL && scroll_view != NULL && l != NULL, RET_BAD_PARAMS);
list_view_h = LIST_VIEW_H(widget->parent);
return_value_if_fail(list_view_h != NULL, RET_BAD_PARAMS);
if (widget->children != NULL && l->cols > 0) {
int32_t i = 0;
int32_t nw = 0;
int32_t x = l->x_margin;
int32_t y = l->y_margin;
uint32_t cols = l->cols;
widget_t** children = NULL;
darray_t children_for_layout;
int32_t spacing = l->spacing;
int32_t h = widget->h - l->y_margin * 2;
int32_t w = widget->w - l->x_margin * 2;
int32_t oy = l->y_margin - l->item_height - spacing;
uint32_t item_w = (w - (cols - 1) * spacing) / cols;
widget_layout_floating_children(widget);
darray_init(&children_for_layout, widget->children->size, NULL, NULL);
return_value_if_fail(
widget_get_children_for_layout(widget, &children_for_layout, l->keep_disable,
l->keep_invisible) == RET_OK,
RET_BAD_PARAMS);
children = (widget_t**)(children_for_layout.elms);
y = oy;
virtual_w = w;
for (i = 0; i < children_for_layout.size; i++) {
widget_t* iter = children[i];
if (i % cols == 0) {
x = l->x_margin + w * nw;
y += l->item_height + spacing;
if (y + l->item_height + spacing > h) {
nw++;
x += w;
virtual_w += w;
y = oy + l->item_height + spacing;
}
} else {
x += item_w + spacing;
}
widget_move_resize(iter, x, y, item_w, l->item_height);
widget_layout(iter);
}
scroll_view_set_virtual_w(list_view_h->scroll_view, virtual_w);
darray_deinit(&children_for_layout);
}
return RET_OK;
}
static ret_t children_layouter_list_view_layout(children_layouter_t* layouter, widget_t* widget) {
children_layouter_list_view_t* l = (children_layouter_list_view_t*)layouter;
return_value_if_fail(l != NULL, RET_BAD_PARAMS);
if (l->hlayouter) {
return children_layouter_list_view_for_list_view_h_layout(layouter, widget);
} else {
return children_layouter_list_view_for_list_view_layout(layouter, widget);
}
}
static bool_t children_layouter_list_view_is_valid(children_layouter_t* layouter) {
return TRUE;
}
static ret_t children_layouter_list_view_destroy(children_layouter_t* layouter) {
children_layouter_list_view_t* l = (children_layouter_list_view_t*)layouter;
return_value_if_fail(l != NULL, RET_BAD_PARAMS);
str_reset(&(layouter->params));
TKMEM_FREE(l);
return RET_OK;
}
static children_layouter_t* children_layouter_list_view_clone(children_layouter_t* layouter) {
children_layouter_list_view_t* l = TKMEM_ZALLOC(children_layouter_list_view_t);
return_value_if_fail(l != NULL, NULL);
memcpy(l, layouter, sizeof(*l));
str_init(&(l->layouter.params), 0);
str_set(&(l->layouter.params), layouter->params.str);
return (children_layouter_t*)l;
}
static const children_layouter_vtable_t s_children_layouter_list_view_vtable = {
.type = CHILDREN_LAYOUTER_LIST_VIEW,
.clone = children_layouter_list_view_clone,
.to_string = children_layouter_list_view_to_string,
.get_param = children_layouter_list_view_get_param,
.set_param = children_layouter_list_view_set_param,
.layout = children_layouter_list_view_layout,
.is_valid = children_layouter_list_view_is_valid,
.destroy = children_layouter_list_view_destroy};
children_layouter_t* children_layouter_list_view_create(void) {
children_layouter_t* l = NULL;
children_layouter_list_view_t* layouter = NULL;
layouter = TKMEM_ZALLOC(children_layouter_list_view_t);
return_value_if_fail(layouter != NULL, NULL);
l = (children_layouter_t*)layouter;
layouter->keep_disable = TRUE;
str_init(&(l->params), 0);
l->vt = &s_children_layouter_list_view_vtable;
return (children_layouter_t*)layouter;
}

View File

@ -0,0 +1,67 @@
/**
* File: children_layouter_list_view.h
* Author: AWTK Develop Team
* Brief: children layouter list view
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2019-05-22 Li XianJing <xianjimli@hotmail.com> created
*
*/
#ifndef TK_CHILDREN_LAYOUTER_LIST_VIEW_H
#define TK_CHILDREN_LAYOUTER_LIST_VIEW_H
#include "base/children_layouter.h"
BEGIN_C_DECLS
/**
* @class children_layouter_list_view_t
* @parent children_layouter_t
*
* listview的子控件布局算法实现。
*
*> 用法请参考:[listview子控件布局算法](
*https://github.com/zlgopen/awtk/blob/master/docs/children_layouter_list_view.md)
*
*/
typedef struct _children_layouter_list_view_t {
children_layouter_t layouter;
uint8_t x_margin;
uint8_t y_margin;
uint16_t spacing;
uint16_t cols;
uint16_t item_height;
uint16_t default_item_height;
uint8_t hlayouter : 1;
uint8_t keep_invisible : 1;
uint8_t keep_disable : 1;
} children_layouter_list_view_t;
/**
* @method children_layouter_list_view_create
* @annotation ["constructor"]
*
* 创建子控件布局对象。
*
* @return {children_layouter_t*} 返回创建子控件布局对象。
*
*/
children_layouter_t* children_layouter_list_view_create(void);
#define CHILDREN_LAYOUTER_LIST_VIEW "list_view"
END_C_DECLS
#endif /*TK_CHILDREN_LAYOUTER_LIST_VIEW_H*/

View File

@ -0,0 +1,187 @@
/**
* File: list_item.h
* Author: AWTK Develop Team
* Brief: list_item
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-07-05 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include "tkc/mem.h"
#include "base/timer.h"
#include "scroll_view/list_item.h"
static ret_t list_item_on_paint_self(widget_t* widget, canvas_t* c) {
return widget_paint_helper(widget, c, NULL, NULL);
}
static ret_t list_item_on_parent_pointer_up(void* ctx, event_t* e) {
list_item_t* list_item = LIST_ITEM(ctx);
return_value_if_fail(list_item != NULL, RET_BAD_PARAMS);
list_item->dragged = FALSE;
list_item->pressed = FALSE;
list_item->downed = FALSE;
return RET_REMOVE;
}
static ret_t list_item_on_detach_parent(widget_t* widget, widget_t* parent) {
return_value_if_fail(widget != NULL && parent != NULL, RET_BAD_PARAMS);
widget_off_by_func(parent, EVT_POINTER_UP, list_item_on_parent_pointer_up, widget);
return RET_OK;
}
static ret_t list_item_on_timer(const timer_info_t* info) {
widget_t* widget = WIDGET(info->ctx);
list_item_t* list_item = LIST_ITEM(widget);
return_value_if_fail(widget != NULL && list_item != NULL, RET_BAD_PARAMS);
if (!list_item->dragged) {
widget_set_state(widget, WIDGET_STATE_PRESSED);
}
list_item->timer_id = TK_INVALID_ID;
return RET_REMOVE;
}
static ret_t list_item_remove_timer(widget_t* widget) {
list_item_t* list_item = LIST_ITEM(widget);
return_value_if_fail(list_item != NULL, RET_BAD_PARAMS);
if (list_item->timer_id != TK_INVALID_ID) {
timer_remove(list_item->timer_id);
list_item->timer_id = TK_INVALID_ID;
}
return RET_OK;
}
static ret_t list_item_on_event(widget_t* widget, event_t* e) {
uint16_t type = e->type;
list_item_t* list_item = LIST_ITEM(widget);
return_value_if_fail(list_item != NULL, RET_BAD_PARAMS);
switch (type) {
case EVT_POINTER_DOWN: {
pointer_event_t* evt = (pointer_event_t*)e;
list_item->downed = TRUE;
list_item->down.x = evt->x;
list_item->down.y = evt->y;
list_item->pressed = TRUE;
widget_grab(widget->parent, widget);
list_item->timer_id = timer_add(list_item_on_timer, widget, 30);
widget_invalidate_force(widget, NULL);
widget_off_by_func(widget->parent, EVT_POINTER_UP, list_item_on_parent_pointer_up, widget);
widget_on(widget->parent, EVT_POINTER_UP, list_item_on_parent_pointer_up, widget);
break;
}
case EVT_POINTER_DOWN_ABORT: {
list_item->dragged = FALSE;
list_item->pressed = FALSE;
list_item->downed = FALSE;
widget_ungrab(widget->parent, widget);
list_item_remove_timer(widget);
widget_invalidate_force(widget, NULL);
widget_set_state(widget, WIDGET_STATE_NORMAL);
break;
}
case EVT_POINTER_UP: {
list_item_remove_timer(widget);
widget_invalidate_force(widget, NULL);
widget_set_state(widget, WIDGET_STATE_NORMAL);
widget_ungrab(widget->parent, widget);
if (!list_item->dragged && list_item->pressed) {
pointer_event_t evt = *(pointer_event_t*)e;
evt.e = event_init(EVT_CLICK, widget);
evt.e.size = sizeof(pointer_event_t);
widget_dispatch(widget, (event_t*)&evt);
}
list_item->dragged = FALSE;
list_item->pressed = FALSE;
list_item->downed = FALSE;
break;
}
case EVT_POINTER_MOVE: {
pointer_event_t* evt = (pointer_event_t*)e;
uint32_t dy = tk_abs(evt->y - list_item->down.y);
if (list_item->downed && evt->pressed && dy > TK_DRAG_THRESHOLD) {
list_item->dragged = TRUE;
list_item->pressed = FALSE;
widget_ungrab(widget->parent, widget);
list_item_remove_timer(widget);
widget_set_state(widget, WIDGET_STATE_NORMAL);
}
break;
}
case EVT_POINTER_LEAVE:
list_item_remove_timer(widget);
widget_set_state(widget, WIDGET_STATE_NORMAL);
break;
case EVT_POINTER_ENTER:
if (list_item->dragged) {
widget_set_state(widget, WIDGET_STATE_NORMAL);
} else {
widget_set_state(widget, WIDGET_STATE_OVER);
}
break;
default:
break;
}
return RET_OK;
}
static ret_t list_item_on_destroy(widget_t* widget) {
list_item_t* list_item = LIST_ITEM(widget);
return_value_if_fail(list_item != NULL, RET_BAD_PARAMS);
if (list_item->timer_id != TK_INVALID_ID) {
timer_remove(list_item->timer_id);
list_item->timer_id = TK_INVALID_ID;
}
return RET_OK;
}
TK_DECL_VTABLE(list_item) = {.size = sizeof(list_item_t),
.type = WIDGET_TYPE_LIST_ITEM,
.space_key_to_activate = TRUE,
.return_key_to_activate = TRUE,
.parent = TK_PARENT_VTABLE(widget),
.create = list_item_create,
.on_event = list_item_on_event,
.on_paint_self = list_item_on_paint_self,
.on_detach_parent = list_item_on_detach_parent,
.on_destroy = list_item_on_destroy};
widget_t* list_item_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) {
widget_t* widget = widget_create(parent, TK_REF_VTABLE(list_item), x, y, w, h);
list_item_t* list_item = LIST_ITEM(widget);
return_value_if_fail(list_item != NULL, NULL);
list_item->timer_id = TK_INVALID_ID;
return widget;
}
widget_t* list_item_cast(widget_t* widget) {
return_value_if_fail(WIDGET_IS_INSTANCE_OF(widget, list_item), NULL);
return widget;
}

View File

@ -0,0 +1,127 @@
/**
* File: list_item.h
* Author: AWTK Develop Team
* Brief: list_item
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-07-05 Li XianJing <xianjimli@hotmail.com> created
*
*/
#ifndef TK_LIST_ITEM_H
#define TK_LIST_ITEM_H
#include "base/widget.h"
BEGIN_C_DECLS
/**
* @class list_item_t
* @parent widget_t
* @annotation ["scriptable","design","widget"]
* 列表项控件。
*
* 列表项控件是一个简单的容器控件,一般作为列表视图中滚动视图的子控件。
*
* list\_item\_t是[widget\_t](widget_t.md)的子类控件widget\_t的函数均适用于list\_item\_t控件。
*
* 在xml中使用"list\_item"标签创建列表项控件。如:
*
* ```xml
* <list_view x="0" y="30" w="100%" h="-80" item_height="60">
* <scroll_view name="view" x="0" y="0" w="100%" h="100%">
* <list_item style="odd" children_layout="default(rows=1,cols=0)">
* <image draw_type="icon" w="30" image="earth"/>
* <label w="-30" text="1.Hello AWTK !">
* <switch x="r:10" y="m" w="60" h="20"/>
* </label>
* </list_item>
* ...
* </scroll_view>
* </list_view>
* ```
*
* > 更多用法请参考:[list\_view\_m.xml](
*https://github.com/zlgopen/awtk/blob/master/design/default/ui/list_view_m.xml)
*
* 在c代码中使用函数list\_item\_create创建列表项控件。如
*
* ```c
* widget_t* list_item = list_item_create(scroll_view, 0, 0, 0, 0);
* ```
*
* > 列表项控件大小一般由列表控制不需指定xywh参数。
*
* 可以用style来实现可点击或不可点击的效果。如
*
* ```xml
* <style name="odd_clickable" border_color="#a0a0a0" border="bottom" text_color="black">
* <normal bg_color="#f5f5f5" />
* <pressed bg_color="#c0c0c0" />
* <over bg_color="#f5f5f5" />
* </style>
* ```
*
* > 更多用法请参考:[theme default](
*https://github.com/zlgopen/awtk/blob/master/design/default/styles/default.xml#L372)
*
*/
typedef struct _list_item_t {
widget_t widget;
/*private*/
int32_t timer_id;
bool_t dragged;
bool_t pressed;
bool_t downed;
point_t down;
} list_item_t;
/**
* @event {pointer_event_t} EVT_CLICK
* 点击事件。
*/
/**
* @method list_item_create
* @annotation ["constructor", "scriptable"]
* 创建list_item对象
* @param {widget_t*} parent 父控件
* @param {xy_t} x x坐标
* @param {xy_t} y y坐标
* @param {wh_t} w 宽度
* @param {wh_t} h 高度
*
* @return {widget_t*} 对象。
*/
widget_t* list_item_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h);
/**
* @method list_item_cast
* 转换为list_item对象(供脚本语言使用)。
* @annotation ["cast", "scriptable"]
* @param {widget_t*} widget list_item对象。
*
* @return {widget_t*} list_item对象。
*/
widget_t* list_item_cast(widget_t* widget);
#define LIST_ITEM(widget) ((list_item_t*)(list_item_cast(WIDGET(widget))))
/*public for subclass and runtime type check*/
TK_EXTERN_VTABLE(list_item);
END_C_DECLS
#endif /*TK_LIST_ITEM_H*/

View File

@ -0,0 +1,493 @@
/**
* File: list_view.h
* Author: AWTK Develop Team
* Brief: list_view
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-07-04 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include "tkc/mem.h"
#include "tkc/utils.h"
#include "tkc/time_now.h"
#include "base/layout.h"
#include "scroll_view/list_view.h"
#include "scroll_view/scroll_bar.h"
#include "scroll_view/scroll_view.h"
#define LIST_VIEW_FLOATING_SCROLL_BAR_HIDE_TIME 500
#define LIST_VIEW_FLOATING_SCROLL_BAR_SHOW_TIME 300
static ret_t list_view_on_add_child(widget_t* widget, widget_t* child);
static ret_t list_view_on_remove_child(widget_t* widget, widget_t* child);
static ret_t list_view_on_paint_self(widget_t* widget, canvas_t* c) {
return widget_paint_helper(widget, c, NULL, NULL);
}
static ret_t list_view_get_prop(widget_t* widget, const char* name, value_t* v) {
list_view_t* list_view = LIST_VIEW(widget);
return_value_if_fail(list_view != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, WIDGET_PROP_ITEM_HEIGHT)) {
value_set_int(v, list_view->item_height);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_DEFAULT_ITEM_HEIGHT)) {
value_set_int(v, list_view->default_item_height);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_AUTO_HIDE_SCROLL_BAR)) {
value_set_bool(v, list_view->auto_hide_scroll_bar);
return RET_OK;
} else if (tk_str_eq(name, LIST_VIEW_PROP_FLOATING_SCROLL_BAR)) {
value_set_bool(v, list_view->floating_scroll_bar);
return RET_OK;
}
return RET_NOT_FOUND;
}
static ret_t list_view_on_pointer_up(list_view_t* list_view, pointer_event_t* e) {
scroll_bar_t* scroll_bar = (scroll_bar_t*)list_view->scroll_bar;
if (scroll_bar != NULL && scroll_bar->wa_opactiy == NULL && list_view->scroll_bar->visible &&
scroll_bar_is_mobile(list_view->scroll_bar)) {
scroll_bar_hide_by_opacity_animation(list_view->scroll_bar,
LIST_VIEW_FLOATING_SCROLL_BAR_HIDE_TIME,
LIST_VIEW_FLOATING_SCROLL_BAR_HIDE_TIME);
}
return RET_OK;
}
static ret_t list_view_set_prop(widget_t* widget, const char* name, const value_t* v) {
list_view_t* list_view = LIST_VIEW(widget);
return_value_if_fail(list_view != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, WIDGET_PROP_ITEM_HEIGHT)) {
list_view->item_height = value_int(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_DEFAULT_ITEM_HEIGHT)) {
list_view->default_item_height = value_int(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_AUTO_HIDE_SCROLL_BAR)) {
list_view->auto_hide_scroll_bar = value_bool(v);
return RET_OK;
} else if (tk_str_eq(name, LIST_VIEW_PROP_FLOATING_SCROLL_BAR)) {
return list_view_set_floating_scroll_bar(widget, value_bool(v));
}
return RET_NOT_FOUND;
}
static ret_t list_view_hanlde_wheel_event(list_view_t* list_view, event_t* e) {
wheel_event_t* evt = (wheel_event_t*)e;
int32_t delta = -evt->dy;
if (list_view->scroll_bar != NULL) {
scroll_bar_add_delta(list_view->scroll_bar, delta);
log_debug("wheel: %d\n", delta);
}
return RET_STOP;
}
static ret_t list_view_on_wheel_before(void* ctx, event_t* e) {
return list_view_hanlde_wheel_event(LIST_VIEW(ctx), e);
}
static bool_t list_view_is_play_floating_scroll_bar_animtion(list_view_t* list_view) {
scroll_view_t* scroll_view = NULL;
return_value_if_fail(list_view != NULL && list_view->scroll_view != NULL, FALSE);
if (list_view->scroll_bar != NULL) {
scroll_view = SCROLL_VIEW(list_view->scroll_view);
return_value_if_fail(scroll_view != NULL, FALSE);
if (list_view->floating_scroll_bar && list_view->scroll_bar->enable &&
scroll_view->virtual_h >= list_view->widget.h) {
return TRUE;
}
}
return FALSE;
}
static ret_t list_view_on_pointer_leave(list_view_t* list_view) {
return_value_if_fail(list_view != NULL, RET_BAD_PARAMS);
if (list_view_is_play_floating_scroll_bar_animtion(list_view)) {
widget_t* win = widget_get_window(WIDGET(list_view));
list_view->is_over = FALSE;
scroll_bar_hide_by_opacity_animation(list_view->scroll_bar,
LIST_VIEW_FLOATING_SCROLL_BAR_HIDE_TIME, 0);
if (list_view->wheel_before_id != TK_INVALID_ID) {
widget_off(win, list_view->wheel_before_id);
list_view->wheel_before_id = TK_INVALID_ID;
}
}
return RET_OK;
}
static ret_t list_view_on_pointer_enter(list_view_t* list_view) {
return_value_if_fail(list_view != NULL, RET_BAD_PARAMS);
if (list_view_is_play_floating_scroll_bar_animtion(list_view)) {
widget_t* win = widget_get_window(WIDGET(list_view));
list_view->is_over = TRUE;
scroll_bar_show_by_opacity_animation(list_view->scroll_bar,
LIST_VIEW_FLOATING_SCROLL_BAR_SHOW_TIME, 0);
if (list_view->wheel_before_id == TK_INVALID_ID) {
list_view->wheel_before_id =
widget_on(win, EVT_WHEEL_BEFORE_CHILDREN, list_view_on_wheel_before, WIDGET(list_view));
}
}
return RET_OK;
}
static ret_t list_view_on_event(widget_t* widget, event_t* e) {
ret_t ret = RET_OK;
list_view_t* list_view = LIST_VIEW(widget);
return_value_if_fail(list_view != NULL, RET_BAD_PARAMS);
switch (e->type) {
case EVT_WHEEL: {
ret = list_view_hanlde_wheel_event(list_view, e);
break;
}
case EVT_KEY_DOWN: {
key_event_t* evt = (key_event_t*)e;
if (evt->key == TK_KEY_PAGEDOWN) {
scroll_view_scroll_delta_to(list_view->scroll_view, 0, widget->h, TK_ANIMATING_TIME);
ret = RET_STOP;
} else if (evt->key == TK_KEY_PAGEUP) {
scroll_view_scroll_delta_to(list_view->scroll_view, 0, -widget->h, TK_ANIMATING_TIME);
ret = RET_STOP;
} else if (evt->key == TK_KEY_UP) {
uint32_t item_height = tk_max(list_view->item_height, list_view->default_item_height);
scroll_view_scroll_delta_to(list_view->scroll_view, 0, -item_height, TK_ANIMATING_TIME);
ret = RET_STOP;
} else if (evt->key == TK_KEY_DOWN) {
uint32_t item_height = tk_max(list_view->item_height, list_view->default_item_height);
scroll_view_scroll_delta_to(list_view->scroll_view, 0, item_height, TK_ANIMATING_TIME);
ret = RET_STOP;
}
break;
}
case EVT_POINTER_UP: {
pointer_event_t* evt = (pointer_event_t*)e;
list_view_on_pointer_up(list_view, evt);
break;
}
case EVT_POINTER_LEAVE: {
list_view_on_pointer_leave(list_view);
break;
}
case EVT_POINTER_ENTER: {
list_view_on_pointer_enter(list_view);
break;
}
default:
break;
}
return ret;
}
TK_DECL_VTABLE(list_view) = {.type = WIDGET_TYPE_LIST_VIEW,
.size = sizeof(list_view_t),
.parent = TK_PARENT_VTABLE(widget),
.create = list_view_create,
.set_prop = list_view_set_prop,
.get_prop = list_view_get_prop,
.on_event = list_view_on_event,
.on_add_child = list_view_on_add_child,
.on_remove_child = list_view_on_remove_child,
.on_paint_self = list_view_on_paint_self};
static int32_t scroll_bar_to_scroll_view(list_view_t* list_view, int32_t v) {
int32_t range = 0;
float_t percent = 0;
scroll_view_t* scroll_view = NULL;
scroll_bar_t* scroll_bar = NULL;
return_value_if_fail(list_view != NULL, 0);
scroll_view = SCROLL_VIEW(list_view->scroll_view);
scroll_bar = SCROLL_BAR(list_view->scroll_bar);
return_value_if_fail(scroll_bar != NULL && scroll_view != NULL, 0);
range = scroll_bar->virtual_size;
percent = range > 0 ? (float_t)v / (float_t)(range) : 0;
return percent * (scroll_view->virtual_h - list_view->scroll_view->h);
}
static ret_t list_view_on_scroll_bar_value_changed(void* ctx, event_t* e) {
int32_t offset = 0;
scroll_bar_t* scroll_bar = NULL;
list_view_t* list_view = LIST_VIEW(ctx);
return_value_if_fail(list_view != NULL, RET_REMOVE);
scroll_bar = SCROLL_BAR(list_view->scroll_bar);
offset = scroll_bar_to_scroll_view(list_view, scroll_bar->value);
scroll_view_set_offset(list_view->scroll_view, 0, offset);
return RET_OK;
}
static int32_t scroll_view_to_scroll_bar(list_view_t* list_view, int32_t v) {
int32_t range = 0;
float_t percent = 0;
scroll_view_t* scroll_view = NULL;
scroll_bar_t* scroll_bar = NULL;
return_value_if_fail(list_view != NULL, 0);
scroll_view = SCROLL_VIEW(list_view->scroll_view);
scroll_bar = SCROLL_BAR(list_view->scroll_bar);
return_value_if_fail(scroll_bar != NULL && scroll_view != NULL, 0);
range = scroll_view->virtual_h - list_view->scroll_view->h;
percent = range > 0 ? (float_t)v / (float_t)range : 0;
return percent * scroll_bar->virtual_size;
}
static ret_t list_view_on_scroll_view_scroll(widget_t* widget, int32_t xoffset, int32_t yoffset) {
list_view_t* list_view = LIST_VIEW(widget->parent);
return_value_if_fail(list_view != NULL, RET_BAD_PARAMS);
if (list_view->scroll_bar != NULL) {
int32_t value = scroll_view_to_scroll_bar(list_view, yoffset);
scroll_bar_set_value_only(list_view->scroll_bar, value);
if (scroll_bar_is_mobile(list_view->scroll_bar)) {
widget_set_opacity(list_view->scroll_bar, 0xff);
widget_set_visible_only(list_view->scroll_bar, TRUE);
}
}
return RET_OK;
}
static ret_t list_view_on_scroll_view_scroll_to(widget_t* widget, int32_t xoffset_end,
int32_t yoffset_end, int32_t duration) {
list_view_t* list_view = LIST_VIEW(widget->parent);
return_value_if_fail(widget != NULL && list_view != NULL, RET_BAD_PARAMS);
if (scroll_bar_is_mobile(list_view->scroll_bar)) {
int32_t value = scroll_view_to_scroll_bar(list_view, yoffset_end);
emitter_disable(list_view->scroll_bar->emitter);
scroll_bar_scroll_to(list_view->scroll_bar, value, duration);
emitter_enable(list_view->scroll_bar->emitter);
(void)xoffset_end;
}
return RET_OK;
}
static ret_t list_view_on_scroll_view_layout_children(widget_t* widget) {
if (widget->children_layout == NULL) {
widget_set_children_layout(widget, "list_view(m=0,s=0)");
}
return_value_if_fail(widget->children_layout != NULL, RET_BAD_PARAMS);
children_layouter_layout(widget->children_layout, widget);
return RET_OK;
}
static ret_t list_view_on_scroll_view_paint_children(widget_t* widget, canvas_t* c) {
int32_t left = 0;
int32_t top = 0;
int32_t bottom = 0;
int32_t right = 0;
int32_t max_w = canvas_get_width(c);
int32_t max_h = canvas_get_height(c);
WIDGET_FOR_EACH_CHILD_BEGIN(widget, iter, i)
if (!iter->visible) {
iter->dirty = FALSE;
continue;
}
left = c->ox + iter->x;
top = c->oy + iter->y;
bottom = top + iter->h;
right = left + iter->w;
if (top > max_h || left > max_w) {
break;
}
if (bottom < 0 || right < 0) {
iter->dirty = FALSE;
continue;
}
if (!canvas_is_rect_in_clip_rect(c, left, top, right, bottom)) {
iter->dirty = FALSE;
continue;
}
widget_paint(iter, c);
WIDGET_FOR_EACH_CHILD_END();
return RET_OK;
}
static ret_t list_view_on_add_child(widget_t* widget, widget_t* child) {
list_view_t* list_view = LIST_VIEW(widget);
const char* type = widget_get_type(child);
return_value_if_fail(list_view != NULL && widget != NULL && child != NULL, RET_BAD_PARAMS);
if (tk_str_eq(type, WIDGET_TYPE_SCROLL_VIEW)) {
scroll_view_t* scroll_view = (scroll_view_t*)list_view->scroll_view;
if (scroll_view != NULL) {
scroll_view->on_scroll = NULL;
scroll_view->on_scroll_to = NULL;
scroll_view->on_layout_children = NULL;
}
list_view->scroll_view = child;
scroll_view = SCROLL_VIEW(child);
scroll_view->on_scroll = list_view_on_scroll_view_scroll;
scroll_view->on_scroll_to = list_view_on_scroll_view_scroll_to;
scroll_view->on_layout_children = list_view_on_scroll_view_layout_children;
scroll_view->on_paint_children = list_view_on_scroll_view_paint_children;
scroll_view_set_recursive_only(child, FALSE);
} else if (tk_str_eq(type, WIDGET_TYPE_SCROLL_BAR) ||
tk_str_eq(type, WIDGET_TYPE_SCROLL_BAR_DESKTOP) ||
tk_str_eq(type, WIDGET_TYPE_SCROLL_BAR_MOBILE)) {
if (list_view->scroll_bar != NULL) {
widget_off_by_func(list_view->scroll_bar, EVT_VALUE_CHANGED,
list_view_on_scroll_bar_value_changed, widget);
}
list_view->scroll_bar = child;
widget_on(child, EVT_VALUE_CHANGED, list_view_on_scroll_bar_value_changed, widget);
}
return RET_CONTINUE;
}
static ret_t list_view_on_remove_child(widget_t* widget, widget_t* child) {
list_view_t* list_view = LIST_VIEW(widget);
return_value_if_fail(list_view != NULL && widget != NULL && child != NULL, RET_BAD_PARAMS);
if (list_view->scroll_view == child) {
scroll_view_t* scroll_view = SCROLL_VIEW(child);
scroll_view->on_scroll = NULL;
scroll_view->on_scroll_to = NULL;
scroll_view->on_layout_children = NULL;
list_view->scroll_view = NULL;
WIDGET_FOR_EACH_CHILD_BEGIN_R(widget, iter, i)
if (iter && iter != child && tk_str_eq(iter->vt->type, WIDGET_TYPE_SCROLL_VIEW)) {
list_view->scroll_view = iter;
scroll_view->on_scroll = list_view_on_scroll_view_scroll;
scroll_view->on_scroll_to = list_view_on_scroll_view_scroll_to;
scroll_view->on_layout_children = list_view_on_scroll_view_layout_children;
break;
}
WIDGET_FOR_EACH_CHILD_END();
} else if (list_view->scroll_bar == child) {
widget_off_by_func(child, EVT_VALUE_CHANGED, list_view_on_scroll_bar_value_changed, widget);
list_view->scroll_bar = NULL;
WIDGET_FOR_EACH_CHILD_BEGIN_R(widget, iter, i)
if (iter && iter != child &&
(tk_str_eq(iter->vt->type, WIDGET_TYPE_SCROLL_BAR) ||
tk_str_eq(iter->vt->type, WIDGET_TYPE_SCROLL_BAR_DESKTOP) ||
tk_str_eq(iter->vt->type, WIDGET_TYPE_SCROLL_BAR_MOBILE))) {
list_view->scroll_bar = iter;
widget_on(iter, EVT_VALUE_CHANGED, list_view_on_scroll_bar_value_changed, widget);
break;
}
WIDGET_FOR_EACH_CHILD_END();
}
return RET_FAIL;
}
ret_t list_view_reinit(widget_t* widget) {
list_view_t* list_view = LIST_VIEW(widget);
return_value_if_fail(list_view != NULL && widget != NULL, RET_BAD_PARAMS);
WIDGET_FOR_EACH_CHILD_BEGIN_R(widget, iter, i)
if (iter && tk_str_eq(iter->vt->type, WIDGET_TYPE_SCROLL_VIEW)) {
if (iter == list_view->scroll_view) break;
scroll_view_t* scroll_view = SCROLL_VIEW(list_view->scroll_view);
list_view->scroll_view = iter;
scroll_view->on_scroll = list_view_on_scroll_view_scroll;
scroll_view->on_scroll_to = list_view_on_scroll_view_scroll_to;
scroll_view->on_layout_children = list_view_on_scroll_view_layout_children;
break;
}
WIDGET_FOR_EACH_CHILD_END();
WIDGET_FOR_EACH_CHILD_BEGIN_R(widget, iter, i)
if (iter && (tk_str_eq(iter->vt->type, WIDGET_TYPE_SCROLL_BAR) ||
tk_str_eq(iter->vt->type, WIDGET_TYPE_SCROLL_BAR_DESKTOP) ||
tk_str_eq(iter->vt->type, WIDGET_TYPE_SCROLL_BAR_MOBILE))) {
if (iter == list_view->scroll_bar) break;
if (list_view->scroll_bar != NULL) {
widget_off_by_func(list_view->scroll_bar, EVT_VALUE_CHANGED,
list_view_on_scroll_bar_value_changed, widget);
}
list_view->scroll_bar = iter;
widget_on(iter, EVT_VALUE_CHANGED, list_view_on_scroll_bar_value_changed, widget);
break;
}
WIDGET_FOR_EACH_CHILD_END();
return RET_OK;
}
widget_t* list_view_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) {
return widget_create(parent, TK_REF_VTABLE(list_view), x, y, w, h);
}
ret_t list_view_set_item_height(widget_t* widget, int32_t item_height) {
list_view_t* list_view = LIST_VIEW(widget);
return_value_if_fail(list_view != NULL, RET_BAD_PARAMS);
list_view->item_height = item_height;
return RET_OK;
}
ret_t list_view_set_default_item_height(widget_t* widget, int32_t default_item_height) {
list_view_t* list_view = LIST_VIEW(widget);
return_value_if_fail(list_view != NULL, RET_BAD_PARAMS);
list_view->default_item_height = default_item_height;
return RET_OK;
}
ret_t list_view_set_auto_hide_scroll_bar(widget_t* widget, bool_t auto_hide_scroll_bar) {
list_view_t* list_view = LIST_VIEW(widget);
return_value_if_fail(list_view != NULL, RET_BAD_PARAMS);
list_view->auto_hide_scroll_bar = auto_hide_scroll_bar;
return RET_OK;
}
ret_t list_view_set_floating_scroll_bar(widget_t* widget, bool_t floating_scroll_bar) {
list_view_t* list_view = LIST_VIEW(widget);
return_value_if_fail(list_view != NULL, RET_BAD_PARAMS);
list_view->floating_scroll_bar = floating_scroll_bar;
return RET_OK;
}
widget_t* list_view_cast(widget_t* widget) {
return_value_if_fail(WIDGET_IS_INSTANCE_OF(widget, list_view), NULL);
return widget;
}

View File

@ -0,0 +1,211 @@
/**
* File: list_view.h
* Author: AWTK Develop Team
* Brief: list_view
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-07-04 Li XianJing <xianjimli@hotmail.com> created
*
*/
#ifndef TK_LIST_VIEW_H
#define TK_LIST_VIEW_H
#include "base/widget.h"
BEGIN_C_DECLS
/**
* @class list_view_t
* @parent widget_t
* @annotation ["scriptable","design","widget"]
* 列表视图控件。
*
* 列表视图控件是一个可以垂直滚动的列表控件。
*
* 如果不需要滚动可以用view控件配置适当的layout参数作为列表控件。
*
* 列表视图中的列表项可以固定高度,也可以使用不同高度。请参考[变高列表项](
*https://github.com/zlgopen/awtk/blob/master/design/default/ui/list_view_vh.xml)
*
* 列表视图控件的中可以有滚动条,也可以没有滚动条。
* 可以使用移动设备风格的滚动条,也可以使用桌面风格的滚动条。
*
* list\_view\_t是[widget\_t](widget_t.md)的子类控件widget\_t的函数均适用于list\_view\_t控件。
*
* 在xml中使用"list\_view"标签创建列表视图控件。如:
*
* ```xml
* <list_view x="0" y="30" w="100%" h="-80" item_height="60">
* <scroll_view name="view" x="0" y="0" w="100%" h="100%">
* <list_item style="odd" children_layout="default(rows=1,cols=0)">
* <image draw_type="icon" w="30" image="earth"/>
* <label w="-30" text="1.Hello AWTK !">
* <switch x="r:10" y="m" w="60" h="20"/>
* </label>
* </list_item>
* ...
* </scroll_view>
* </list_view>
* ```
*
* > 注意:列表项不是作为列表视图控件的直接子控件,而是作为滚动视图的子控件。
*
*
* > 更多用法请参考:[list\_view\_m.xml](
*https://github.com/zlgopen/awtk/blob/master/design/default/ui/list_view_m.xml)
*
* 在c代码中使用函数list\_view\_create创建列表视图控件。如
*
* ```c
* widget_t* list_view = list_view_create(win, 0, 0, 0, 0);
* ```
*
* 用代码构造列表视图是比较繁琐的事情最好用XML来构造。
* 如果需要动态修改可以使用widget\_clone来增加列表项使用widget\_remove\_child来移出列表项。
*
* 可用通过style来设置控件的显示风格如背景颜色和边框颜色等(一般情况不需要)。
*
* 备注list_view 下的 scroll_view 控件不支持遍历所有子控件的效果。
*
* 下面是针对 scroll_bar_d 桌面版有效果scroll_bar_m移动版没有效果。
* 如果 floating_scroll_bar 属性为 TRUE 和 auto_hide_scroll_bar 属性为 TRUEscroll_view 宽默认为 list_view 的 100% 宽,鼠标在 list_view 上滚动条才显示,不在的就自动隐藏,如果 scroll_view 的高比虚拟高要大的话滚动条变成不可见scroll_view 宽不会变。
* 如果 floating_scroll_bar 属性为 TRUE 和 auto_hide_scroll_bar 属性为 FALSE scroll_view 宽默认为 list_view 的 100% 宽,滚动条不隐藏,如果 scroll_view 的高比虚拟高要大的话滚动条变成不可见scroll_view 宽不会变。
* 如果 floating_scroll_bar 属性为 FALSE 和 auto_hide_scroll_bar 属性为 FALSE如果 scroll_view 的高比虚拟高要大的话滚动条变成不可用scroll_view 宽不会变。
* 如果 floating_scroll_bar 属性为 FALSE 和 auto_hide_scroll_bar 属性为 TRUE如果 scroll_view 的高比虚拟高要大的话滚动条变成不可见scroll_view 宽会合并原来滚动条的宽。
*
*/
typedef struct _list_view_t {
widget_t widget;
/**
* @property {int32_t} item_height
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 列表项的高度。如果 item_height > 0所有列表项使用固定高度否则使用列表项自身的高度。
*/
int32_t item_height;
/**
* @property {int32_t} default_item_height
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 列表项的缺省高度。如果item_height <= 0 而且列表项自身的高度 <= 0则使用缺省高度。
*/
int32_t default_item_height;
/**
* @property {bool_t} auto_hide_scroll_bar
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 如果不需要滚动条时,自动隐藏滚动条。
*/
bool_t auto_hide_scroll_bar;
/**
* @property {bool_t} floating_scroll_bar
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 滚动条是否悬浮在 scroll_view 上面
*/
bool_t floating_scroll_bar;
/*private*/
bool_t is_over;
widget_t* scroll_view;
widget_t* scroll_bar;
uint32_t wheel_before_id;
} list_view_t;
/**
* @method list_view_create
* 创建list_view对象
* @annotation ["constructor", "scriptable"]
* @param {widget_t*} parent 父控件
* @param {xy_t} x x坐标
* @param {xy_t} y y坐标
* @param {wh_t} w 宽度
* @param {wh_t} h 高度
*
* @return {widget_t*} 对象。
*/
widget_t* list_view_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h);
/**
* @method list_view_set_item_height
* 设置列表项的高度。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {int32_t} item_height 列表项的高度。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t list_view_set_item_height(widget_t* widget, int32_t item_height);
/**
* @method list_view_set_default_item_height
* 设置列表项的缺省高度。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {int32_t} default_item_height 列表项的高度。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t list_view_set_default_item_height(widget_t* widget, int32_t default_item_height);
/**
* @method list_view_set_auto_hide_scroll_bar
* 设置是否自动隐藏滚动条。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {bool_t} auto_hide_scroll_bar 是否自动隐藏滚动条。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t list_view_set_auto_hide_scroll_bar(widget_t* widget, bool_t auto_hide_scroll_bar);
/**
* @method list_view_set_floating_scroll_bar
* 设置滚动条是否悬浮在 scroll_view 上面。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {bool_t} floating_scroll_bar 滚动条是否悬浮在 scroll_view 上面。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t list_view_set_floating_scroll_bar(widget_t* widget, bool_t floating_scroll_bar);
/**
* @method list_view_cast
* 转换为list_view对象(供脚本语言使用)。
* @annotation ["cast", "scriptable"]
* @param {widget_t*} widget list_view对象。
*
* @return {widget_t*} list_view对象。
*/
widget_t* list_view_cast(widget_t* widget);
/**
* @method list_view_reinit
* list_view重新初始化。
* @annotation ["scriptable"]
* @param {widget_t*} widget list_view对象。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t list_view_reinit(widget_t* widget);
#define LIST_VIEW(widget) ((list_view_t*)(list_view_cast(WIDGET(widget))))
#define LIST_VIEW_PROP_FLOATING_SCROLL_BAR "floating_scroll_bar"
/*public for subclass and runtime type check*/
TK_EXTERN_VTABLE(list_view);
END_C_DECLS
#endif /*TK_LIST_VIEW_H*/

View File

@ -0,0 +1,192 @@
/**
* File: list_view_h.h
* Author: AWTK Develop Team
* Brief: list_view_h
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-07-04 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include "tkc/mem.h"
#include "tkc/utils.h"
#include "base/layout.h"
#include "scroll_view/list_view_h.h"
#include "scroll_view/scroll_view.h"
static ret_t list_view_h_on_add_child(widget_t* widget, widget_t* child);
static ret_t list_view_h_on_paint_self(widget_t* widget, canvas_t* c) {
return widget_paint_helper(widget, c, NULL, NULL);
}
static ret_t list_view_h_get_prop(widget_t* widget, const char* name, value_t* v) {
list_view_h_t* list_view_h = LIST_VIEW_H(widget);
return_value_if_fail(widget != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, WIDGET_PROP_ITEM_WIDTH)) {
value_set_int(v, list_view_h->item_width);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_SPACING)) {
value_set_int(v, list_view_h->spacing);
return RET_OK;
}
return RET_NOT_FOUND;
}
static ret_t list_view_h_set_prop(widget_t* widget, const char* name, const value_t* v) {
list_view_h_t* list_view_h = LIST_VIEW_H(widget);
return_value_if_fail(widget != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, WIDGET_PROP_ITEM_WIDTH)) {
list_view_h->item_width = value_int(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_SPACING)) {
list_view_h->spacing = value_int(v);
return RET_OK;
}
return RET_NOT_FOUND;
}
static ret_t list_view_h_on_event(widget_t* widget, event_t* e) {
ret_t ret = RET_OK;
list_view_h_t* list_view = LIST_VIEW_H(widget);
return_value_if_fail(list_view != NULL, RET_BAD_PARAMS);
switch (e->type) {
case EVT_KEY_DOWN: {
key_event_t* evt = (key_event_t*)e;
if (evt->key == TK_KEY_PAGEDOWN) {
scroll_view_scroll_delta_to(list_view->scroll_view, widget->w, 0, TK_ANIMATING_TIME);
ret = RET_STOP;
} else if (evt->key == TK_KEY_PAGEUP) {
scroll_view_scroll_delta_to(list_view->scroll_view, -widget->w, 0, TK_ANIMATING_TIME);
ret = RET_STOP;
}
break;
}
default:
break;
}
return ret;
}
TK_DECL_VTABLE(list_view_h) = {.type = WIDGET_TYPE_LIST_VIEW_H,
.size = sizeof(list_view_h_t),
.parent = TK_PARENT_VTABLE(widget),
.create = list_view_h_create,
.set_prop = list_view_h_set_prop,
.get_prop = list_view_h_get_prop,
.on_event = list_view_h_on_event,
.on_add_child = list_view_h_on_add_child,
.on_paint_self = list_view_h_on_paint_self};
static ret_t list_view_h_on_scroll_view_layout_children(widget_t* widget) {
list_view_h_t* list_view_h = LIST_VIEW_H(widget->parent);
return_value_if_fail(list_view_h != NULL, RET_BAD_PARAMS);
int32_t i = 0;
int32_t x = 0;
int32_t y = 0;
int32_t w = list_view_h->item_width;
int32_t h = widget->h;
int32_t spacing = 0;
int32_t x_margin = 0;
int32_t max_w = 0;
int32_t virtual_w = widget->w;
if (widget->children_layout != NULL) {
children_layouter_layout(widget->children_layout, widget);
if (tk_str_start_with(children_layouter_to_string(widget->children_layout), "list_view")) {
scroll_view_set_xslidable(list_view_h->scroll_view, TRUE);
scroll_view_set_yslidable(list_view_h->scroll_view, FALSE);
return RET_OK;
}
spacing = children_layouter_get_param_int(widget->children_layout, "spacing", 0);
x_margin = children_layouter_get_param_int(widget->children_layout, "x_margin", 0);
} else {
spacing = list_view_h->spacing;
}
if (widget->children != NULL) {
for (i = 0; i < widget->children->size; i++) {
widget_t* iter = (widget_t*)darray_get(widget->children, i);
if (widget->children_layout == NULL) {
widget_move_resize_ex(iter, x, y, w, h, FALSE);
widget_layout(iter);
}
x = iter->x + iter->w + spacing;
max_w = tk_max(max_w, x);
}
}
max_w += x_margin - spacing;
if (max_w > virtual_w) {
virtual_w = max_w;
}
scroll_view_set_virtual_w(list_view_h->scroll_view, virtual_w);
scroll_view_set_xslidable(list_view_h->scroll_view, TRUE);
scroll_view_set_yslidable(list_view_h->scroll_view, FALSE);
return RET_OK;
}
static ret_t list_view_h_on_add_child(widget_t* widget, widget_t* child) {
list_view_h_t* list_view_h = LIST_VIEW_H(widget);
const char* type = widget_get_type(child);
if (tk_str_eq(type, WIDGET_TYPE_SCROLL_VIEW)) {
scroll_view_t* scroll_view = SCROLL_VIEW(child);
list_view_h->scroll_view = child;
scroll_view->on_layout_children = list_view_h_on_scroll_view_layout_children;
}
return RET_CONTINUE;
}
widget_t* list_view_h_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) {
return widget_create(parent, TK_REF_VTABLE(list_view_h), x, y, w, h);
}
ret_t list_view_h_set_item_width(widget_t* widget, int32_t item_width) {
list_view_h_t* list_view_h = LIST_VIEW_H(widget);
return_value_if_fail(widget != NULL, RET_BAD_PARAMS);
list_view_h->item_width = item_width;
return RET_OK;
}
ret_t list_view_h_set_spacing(widget_t* widget, int32_t spacing) {
list_view_h_t* list_view_h = LIST_VIEW_H(widget);
return_value_if_fail(widget != NULL, RET_BAD_PARAMS);
list_view_h->spacing = spacing;
return RET_OK;
}
widget_t* list_view_h_cast(widget_t* widget) {
return_value_if_fail(WIDGET_IS_INSTANCE_OF(widget, list_view_h), NULL);
return widget;
}

View File

@ -0,0 +1,138 @@
/**
* File: list_view_h.h
* Author: AWTK Develop Team
* Brief: list_view_h
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-07-17 Li XianJing <xianjimli@hotmail.com> created
*
*/
#ifndef TK_LIST_VIEW_H_H
#define TK_LIST_VIEW_H_H
#include "base/widget.h"
BEGIN_C_DECLS
/**
* @class list_view_h_t
* @parent widget_t
* @annotation ["scriptable","design","widget"]
* 水平列表视图控件。
*
* list\_view\_h\_t是[widget\_t](widget_t.md)的子类控件widget\_t的函数均适用于list\_view\_h\_t控件。
*
* 在xml中使用"list\_view\_h"标签创建水平列表视图控件。如:
*
* ```xml
* <list_view_h x="center" y="10" w="90%" h="100" item_width="200" spacing="5">
* <scroll_view name="view" w="100%" h="100%">
* <image style="border" draw_type="auto" image="1" text="1"/>
* ...
* </scroll_view>
* </list_view_h>
* ```
*
* > 注意:列表项不是作为列表视图控件的直接子控件,而是作为滚动视图的子控件。
*
*
* > 更多用法请参考:[list\_view\_h.xml](
*https://github.com/zlgopen/awtk/blob/master/design/default/ui/list_view_h.xml)
*
* 在c代码中使用函数list\_view\_h\_create创建水平列表视图控件。如
*
* ```c
* widget_t* list_view = list_view_h_create(win, 0, 0, 0, 0);
* ```
*
* 用代码构造列表视图是比较繁琐的事情最好用XML来构造。
* 如果需要动态修改可以使用widget\_clone来增加列表项使用widget\_remove\_child来移出列表项。
*
* 可用通过style来设置控件的显示风格如背景颜色和边框颜色等(一般情况不需要)。
*
*/
typedef struct _list_view_h_t {
widget_t widget;
/**
* @property {int32_t} item_width
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 列表项的宽度。
*/
int32_t item_width;
/**
* @property {int32_t} spacing
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 间距。
*/
int32_t spacing;
/*private*/
widget_t* scroll_view;
} list_view_h_t;
/**
* @method list_view_h_create
* 创建list_view_h对象
* @annotation ["constructor", "scriptable"]
* @param {widget_t*} parent 父控件
* @param {xy_t} x x坐标
* @param {xy_t} y y坐标
* @param {wh_t} w 宽度
* @param {wh_t} h 高度
*
* @return {widget_t*} 对象。
*/
widget_t* list_view_h_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h);
/**
* @method list_view_h_set_item_width
* 设置列表项的宽度。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {int32_t} item_width 列表项的宽度。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t list_view_h_set_item_width(widget_t* widget, int32_t item_width);
/**
* @method list_view_h_set_spacing
* 设置列表项的间距。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {int32_t} spacing 列表项的间距。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t list_view_h_set_spacing(widget_t* widget, int32_t spacing);
/**
* @method list_view_h_cast
* 转换为list_view_h对象(供脚本语言使用)。
* @annotation ["cast", "scriptable"]
* @param {widget_t*} widget list_view_h对象。
*
* @return {widget_t*} list_view_h对象。
*/
widget_t* list_view_h_cast(widget_t* widget);
#define LIST_VIEW_H(widget) ((list_view_h_t*)(list_view_h_cast(WIDGET(widget))))
/*public for subclass and runtime type check*/
TK_EXTERN_VTABLE(list_view_h);
END_C_DECLS
#endif /*TK_LIST_VIEW_H_H*/

View File

@ -0,0 +1,818 @@
/**
* File: scroll_bar.h
* Author: AWTK Develop Team
* Brief: scroll_bar
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-01-28 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include "tkc/mem.h"
#include "tkc/utils.h"
#include "widgets/button.h"
#include "base/layout.h"
#include "widgets/dragger.h"
#include "scroll_view/scroll_bar.h"
#include "widget_animators/widget_animator_value.h"
#include "widget_animators/widget_animator_opacity.h"
#define CHILD_UP "up"
#define CHILD_DOWN "down"
#define CHILD_DRAGGER "dragger"
#define SCROLL_BAR_UP_BUTTON_STYLE_NAME "scroll_up"
#define SCROLL_BAR_DOWN_BUTTON_STYLE_NAME "scroll_down"
#define SCROLL_BAR_LEFT_BUTTON_STYLE_NAME "scroll_left"
#define SCROLL_BAR_RIGHT_BUTTON_STYLE_NAME "scroll_right"
#ifndef TK_DRAGGER_MIN_SIZE
#define TK_DRAGGER_MIN_SIZE 10
#endif /*TK_DRAGGER_MIN_SIZE*/
#define SCROLL_BAR_UP_AND_DOWN_BUTTON_STYLE_IS_EXIST(up, down) \
((up) != NULL && (up)->style != NULL && (down) != NULL && (down)->style != NULL)
static ret_t scroll_bar_update_dragger(widget_t* widget);
static ret_t scroll_bar_set_is_mobile(widget_t* widget, bool_t value);
widget_t* scroll_bar_create_desktop_self(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h);
/*mobile*/
static ret_t scroll_bar_mobile_get_dragger_size(widget_t* widget, rect_t* r) {
int64_t x = 0;
int64_t y = 0;
int64_t w = 0;
int64_t h = 0;
int64_t virtual_size = 0;
int64_t value = 0;
int64_t widget_w = widget->w;
int64_t widget_h = widget->h;
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
if (scroll_bar->virtual_size <= 0) {
return RET_OK;
}
virtual_size = scroll_bar->virtual_size;
value = scroll_bar->value;
if (widget_w > widget_h) {
/*horizon*/
return_value_if_fail(virtual_size >= widget_w, RET_BAD_PARAMS);
y = 1;
h = widget_h - 2;
w = (widget_w * widget_w) / virtual_size;
w = tk_max(w, 4);
x = (widget_w - w) * value / virtual_size;
} else {
/*vertical*/
return_value_if_fail(virtual_size >= widget_h, RET_BAD_PARAMS);
x = 1;
w = widget_w - 2;
h = (widget_h * widget_h) / virtual_size;
h = tk_max(h, 4);
y = (widget_h - h) * value / virtual_size;
}
r->x = x;
r->y = y;
r->w = w;
r->h = h;
return RET_OK;
}
static ret_t scroll_bar_mobile_on_paint_self(widget_t* widget, canvas_t* c) {
rect_t r = rect_init(0, 0, 0, 0);
style_t* style = widget->astyle;
color_t trans = color_init(80, 80, 80, 0xff);
color_t fg = style_get_color(style, STYLE_ID_FG_COLOR, trans);
uint32_t round_radius = style_get_int(style, STYLE_ID_ROUND_RADIUS, 0);
return_value_if_fail(scroll_bar_mobile_get_dragger_size(widget, &r) == RET_OK, RET_FAIL);
canvas_set_fill_color(c, fg);
if (round_radius > 0) {
canvas_fill_rounded_rect(c, &r, &r, &fg, round_radius);
} else {
canvas_fill_rect(c, r.x, r.y, r.w, r.h);
}
return RET_OK;
}
/*destkop*/
static ret_t scroll_bar_desktop_on_click(widget_t* widget, pointer_event_t* e) {
int32_t delta = 0;
point_t p = {e->x, e->y};
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
if (widget->target != NULL) {
return RET_OK;
}
widget_to_local(widget, &p);
if (widget->w > widget->h) {
if (p.x < scroll_bar->dragger->x) {
delta = -widget->w;
} else {
delta = widget->w;
}
if (scroll_bar->dragger->x <= p.y && p.x <= (scroll_bar->dragger->x + scroll_bar->dragger->w)) {
return RET_OK;
}
} else {
if (p.y < scroll_bar->dragger->y) {
delta = -widget->h;
} else {
delta = widget->h;
}
if (scroll_bar->dragger->y <= p.y && p.y <= (scroll_bar->dragger->y + scroll_bar->dragger->h)) {
return RET_OK;
}
}
if (delta > 0) {
delta -= scroll_bar->row;
} else {
delta += scroll_bar->row;
}
scroll_bar_scroll_delta(widget, delta);
return RET_OK;
}
static ret_t scroll_bar_desktop_on_event(widget_t* widget, event_t* e) {
uint16_t type = e->type;
switch (type) {
case EVT_POINTER_UP: {
scroll_bar_desktop_on_click(widget, (pointer_event_t*)e);
break;
}
case EVT_POINTER_LEAVE:
widget_set_state(widget, WIDGET_STATE_NORMAL);
break;
case EVT_POINTER_ENTER:
widget_set_state(widget, WIDGET_STATE_OVER);
break;
case EVT_RESIZE:
case EVT_MOVE_RESIZE: {
widget_t* up = widget_lookup(widget, CHILD_UP, FALSE);
widget_t* down = widget_lookup(widget, CHILD_DOWN, FALSE);
bool_t horizon = widget->w > widget->h;
if (up != NULL) {
const char* style_name =
horizon ? SCROLL_BAR_LEFT_BUTTON_STYLE_NAME : SCROLL_BAR_UP_BUTTON_STYLE_NAME;
if (widget_is_style_exist(down, style_name, NULL)) {
widget_use_style(up, style_name);
}
}
if (down != NULL) {
const char* style_name =
horizon ? SCROLL_BAR_RIGHT_BUTTON_STYLE_NAME : SCROLL_BAR_DOWN_BUTTON_STYLE_NAME;
if (widget_is_style_exist(down, style_name, NULL)) {
widget_use_style(down, style_name);
}
}
break;
}
default:
break;
}
return RET_OK;
}
static ret_t scroll_bar_destop_get_dragger_size(widget_t* widget, rect_t* r) {
int64_t x = 0;
int64_t y = 0;
int64_t w = 0;
int64_t h = 0;
int64_t value = 0;
int64_t virtual_size = 0;
uint32_t button_margin = 0;
int64_t widget_w = widget->w;
int64_t widget_h = widget->h;
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
widget_t* up = widget_lookup(widget, CHILD_UP, FALSE);
widget_t* down = widget_lookup(widget, CHILD_DOWN, FALSE);
memset(r, 0x00, sizeof(rect_t));
if (scroll_bar->virtual_size <= 0) {
return RET_OK;
}
if (SCROLL_BAR_UP_AND_DOWN_BUTTON_STYLE_IS_EXIST(up, down)) {
if (widget_w > widget_h) {
button_margin = widget_h;
} else {
button_margin = widget_w;
}
}
value = scroll_bar->value;
if (widget_w > widget_h) {
int64_t max_bar_w = widget_w - 2 * button_margin;
/*horizon*/
virtual_size = tk_max(widget_w, scroll_bar->virtual_size);
y = 1;
h = widget_h - 2;
w = (widget_w * max_bar_w) / virtual_size;
w = tk_max(w, TK_DRAGGER_MIN_SIZE);
x = (widget_w - w - 2 * button_margin) * value / virtual_size + button_margin;
} else {
/*vertical*/
int64_t max_bar_h = widget_h - 2 * button_margin;
virtual_size = tk_max(widget_h, scroll_bar->virtual_size);
x = 1;
w = widget_w - 2;
h = (widget_h * max_bar_h) / virtual_size;
h = tk_max(h, TK_DRAGGER_MIN_SIZE);
y = (widget_h - h - 2 * button_margin) * value / virtual_size + button_margin;
}
r->x = x;
r->y = y;
r->w = w;
r->h = h;
return RET_OK;
}
ret_t scroll_bar_add_delta_ex(widget_t* widget, int32_t d, bool_t animatable) {
int64_t delta = 0;
int64_t new_value = 0;
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
if (widget->w > widget->h) {
if (scroll_bar->virtual_size > widget->w) {
double scale = (double)(scroll_bar->virtual_size) / (scroll_bar->virtual_size - widget->w);
delta = d * scale;
}
} else {
if (scroll_bar->virtual_size > widget->h) {
double scale = (double)(scroll_bar->virtual_size) / (scroll_bar->virtual_size - widget->h);
delta = d * scale;
}
}
new_value = scroll_bar->value + delta;
new_value = tk_max(new_value, 0);
new_value = tk_min(new_value, scroll_bar->virtual_size);
if (scroll_bar->value != new_value) {
if (scroll_bar->animatable && animatable) {
scroll_bar_scroll_to(widget, new_value, scroll_bar->animator_time);
} else {
scroll_bar_set_value(widget, new_value);
}
}
return RET_OK;
}
ret_t scroll_bar_scroll_delta(widget_t* widget, int32_t delta) {
return scroll_bar_add_delta_ex(widget, delta, TRUE);
}
ret_t scroll_bar_add_delta(widget_t* widget, int32_t delta) {
return scroll_bar_add_delta_ex(widget, delta, FALSE);
}
static ret_t scroll_bar_on_up_button_clicked(void* ctx, event_t* e) {
return scroll_bar_scroll_delta(WIDGET(ctx), -SCROLL_BAR(ctx)->row);
}
static ret_t scroll_bar_on_down_button_clicked(void* ctx, event_t* e) {
return scroll_bar_scroll_delta(WIDGET(ctx), SCROLL_BAR(ctx)->row);
}
static ret_t scroll_bar_on_drag(void* ctx, event_t* e) {
int64_t value = 0;
uint32_t button_margin = 0;
widget_t* widget = WIDGET(ctx);
int64_t widget_w = widget->w;
int64_t widget_h = widget->h;
scroll_bar_t* scroll_bar = SCROLL_BAR(ctx);
widget_t* dragger = scroll_bar->dragger;
widget_t* up = widget_lookup(widget, CHILD_UP, FALSE);
widget_t* down = widget_lookup(widget, CHILD_DOWN, FALSE);
if (SCROLL_BAR_UP_AND_DOWN_BUTTON_STYLE_IS_EXIST(up, down)) {
if (widget_w > widget_h) {
button_margin = widget_h;
} else {
button_margin = widget_w;
}
}
if (widget_w > widget_h) {
int64_t x = scroll_bar->dragger->x;
int64_t max_x = (widget_w - 2 * button_margin - dragger->w);
if (max_x <= 0) {
value = 0;
} else {
value = (x - button_margin) * scroll_bar->virtual_size / max_x;
}
} else {
int64_t y = scroll_bar->dragger->y;
int64_t max_y = (widget_h - 2 * button_margin - dragger->h);
if (max_y <= 0) {
value = 0;
} else {
value = (y - button_margin) * scroll_bar->virtual_size / max_y;
}
}
scroll_bar_set_value(widget, value);
return RET_OK;
}
static ret_t scroll_bar_on_layout_children(widget_t* widget) {
uint32_t button_margin = 0;
int32_t widget_w = widget->w;
int32_t widget_h = widget->h;
rect_t r = rect_init(0, 0, 0, 0);
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
widget_t* dragger = scroll_bar->dragger;
widget_t* up = widget_lookup(widget, CHILD_UP, FALSE);
widget_t* down = widget_lookup(widget, CHILD_DOWN, FALSE);
if (SCROLL_BAR_UP_AND_DOWN_BUTTON_STYLE_IS_EXIST(up, down)) {
widget_set_visible(up, TRUE);
widget_set_visible(down, TRUE);
if (widget->w > widget->h) {
button_margin = widget_h;
widget_move_resize(up, 0, 0, widget->h, widget->h);
widget_move_resize(down, widget->w - widget->h, 0, widget->h, widget->h);
} else {
button_margin = widget_w;
widget_move_resize(up, 0, 0, widget->w, widget->w);
widget_move_resize(down, 0, widget->h - widget->w, widget->w, widget->w);
}
} else {
if (up != NULL) {
widget_set_visible(up, FALSE);
}
if (down != NULL) {
widget_set_visible(down, FALSE);
}
}
if (scroll_bar->virtual_size <= 0) {
return RET_OK;
}
return_value_if_fail(scroll_bar_destop_get_dragger_size(widget, &r) == RET_OK, RET_FAIL);
if (dragger != NULL) {
if (widget->w > widget->h) {
int32_t max_x = button_margin + (widget_w - 2 * button_margin - r.w);
dragger_set_range(dragger, button_margin, r.y, max_x, r.y);
} else {
int32_t max_y = button_margin + (widget_h - 2 * button_margin - r.h);
dragger_set_range(dragger, r.x, button_margin, r.x, max_y);
}
widget_move_resize(WIDGET(dragger), r.x, r.y, r.w, r.h);
}
widget_invalidate_force(widget, NULL);
return RET_OK;
}
static ret_t scroll_bar_create_children(widget_t* widget) {
widget_t* up = NULL;
widget_t* down = NULL;
widget_t* dragger = NULL;
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(widget != NULL && scroll_bar != NULL, RET_BAD_PARAMS);
if (scroll_bar->dragger != NULL) {
return RET_OK;
}
up = button_create(widget, 0, 0, 0, 0);
up->auto_created = TRUE;
button_set_repeat(up, 300);
widget_set_name(up, CHILD_UP);
widget_on(up, EVT_CLICK, scroll_bar_on_up_button_clicked, widget);
down = button_create(widget, 0, 0, 0, 0);
down->auto_created = TRUE;
button_set_repeat(down, 300);
widget_set_name(down, CHILD_DOWN);
widget_on(down, EVT_CLICK, scroll_bar_on_down_button_clicked, widget);
dragger = dragger_create(widget, 0, 0, 0, 0);
dragger->auto_created = TRUE;
widget_set_name(dragger, CHILD_DRAGGER);
widget_use_style(dragger, "scroll_bar");
widget_on(dragger, EVT_DRAG, scroll_bar_on_drag, widget);
if (widget->w > widget->h) {
if (widget_is_style_exist(up, SCROLL_BAR_LEFT_BUTTON_STYLE_NAME, NULL)) {
widget_use_style(up, SCROLL_BAR_LEFT_BUTTON_STYLE_NAME);
}
if (widget_is_style_exist(up, SCROLL_BAR_RIGHT_BUTTON_STYLE_NAME, NULL)) {
widget_use_style(down, SCROLL_BAR_RIGHT_BUTTON_STYLE_NAME);
}
} else {
if (widget_is_style_exist(up, SCROLL_BAR_UP_BUTTON_STYLE_NAME, NULL)) {
widget_use_style(up, SCROLL_BAR_UP_BUTTON_STYLE_NAME);
}
if (widget_is_style_exist(up, SCROLL_BAR_DOWN_BUTTON_STYLE_NAME, NULL)) {
widget_use_style(down, SCROLL_BAR_DOWN_BUTTON_STYLE_NAME);
}
}
scroll_bar->row = 30;
scroll_bar->dragger = dragger;
widget_set_need_relayout_children(widget);
return RET_OK;
}
/*share*/
ret_t scroll_bar_set_params(widget_t* widget, int32_t virtual_size, int32_t row) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL, RET_BAD_PARAMS);
widget_set_need_relayout_children(widget);
scroll_bar->virtual_size = virtual_size;
scroll_bar->row = row;
if (scroll_bar->value > virtual_size) {
scroll_bar->value = virtual_size;
}
return RET_OK;
}
static ret_t scroll_bar_get_prop(widget_t* widget, const char* name, value_t* v) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, WIDGET_PROP_MAX)) {
value_set_int(v, scroll_bar->virtual_size);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_ROW)) {
value_set_int(v, scroll_bar->row);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_ANIMATABLE)) {
value_set_bool(v, scroll_bar->animatable);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_VALUE)) {
value_set_int(v, scroll_bar->value);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_AUTO_HIDE)) {
value_set_bool(v, scroll_bar->auto_hide);
return RET_OK;
} else if (tk_str_eq(name, SCROLL_BAR_PROP_IS_MOBILE)) {
value_set_bool(v, scroll_bar_is_mobile(widget));
return RET_OK;
} else if (tk_str_eq(name, SCROLL_BAR_PROP_ANIMATOR_TIME)) {
value_set_uint32(v, scroll_bar->animator_time);
return RET_OK;
}
return RET_NOT_FOUND;
}
static ret_t scroll_bar_set_prop(widget_t* widget, const char* name, const value_t* v) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, WIDGET_PROP_MAX)) {
scroll_bar->virtual_size = value_int(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_ROW)) {
scroll_bar->row = value_int(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_ANIMATABLE)) {
scroll_bar->animatable = value_bool(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_VALUE)) {
scroll_bar_set_value(widget, value_int(v));
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_AUTO_HIDE)) {
scroll_bar_set_auto_hide(widget, value_bool(v));
return RET_OK;
} else if (tk_str_eq(name, SCROLL_BAR_PROP_IS_MOBILE)) {
scroll_bar_set_is_mobile(widget, value_bool(v));
return RET_OK;
} else if (tk_str_eq(name, SCROLL_BAR_PROP_ANIMATOR_TIME)) {
return scroll_bar_set_animator_time(widget, value_uint32(v));
}
return RET_NOT_FOUND;
}
static const char* s_scroll_bar_clone_properties[] = {WIDGET_PROP_MAX,
WIDGET_PROP_ROW,
WIDGET_PROP_ANIMATABLE,
WIDGET_PROP_VALUE,
SCROLL_BAR_PROP_ANIMATOR_TIME,
NULL};
static const char* s_scroll_bar_persitent_properties[] = {WIDGET_PROP_ANIMATABLE, NULL};
TK_DECL_VTABLE(scroll_bar_mobile) = {.size = sizeof(scroll_bar_t),
.inputable = TRUE,
.type = WIDGET_TYPE_SCROLL_BAR_MOBILE,
.clone_properties = s_scroll_bar_clone_properties,
.parent = TK_PARENT_VTABLE(widget),
.create = scroll_bar_create_mobile,
.set_prop = scroll_bar_set_prop,
.get_prop = scroll_bar_get_prop,
.on_layout_children = scroll_bar_on_layout_children,
.on_paint_self = scroll_bar_mobile_on_paint_self};
TK_DECL_VTABLE(scroll_bar_desktop) = {.size = sizeof(scroll_bar_t),
.inputable = TRUE,
.type = WIDGET_TYPE_SCROLL_BAR_DESKTOP,
.clone_properties = s_scroll_bar_clone_properties,
.persistent_properties = s_scroll_bar_persitent_properties,
.parent = TK_PARENT_VTABLE(widget),
.create = scroll_bar_create_desktop_self,
.on_event = scroll_bar_desktop_on_event,
.on_layout_children = scroll_bar_on_layout_children,
.set_prop = scroll_bar_set_prop,
.get_prop = scroll_bar_get_prop};
bool_t scroll_bar_is_mobile(widget_t* widget) {
return widget != NULL && tk_str_eq(widget->vt->type, WIDGET_TYPE_SCROLL_BAR_MOBILE);
}
static ret_t scroll_bar_on_value_animate_end(void* ctx, event_t* e) {
widget_t* widget = WIDGET(ctx);
scroll_bar_t* scroll_bar = SCROLL_BAR(ctx);
return_value_if_fail(widget != NULL && scroll_bar != NULL, RET_REMOVE);
scroll_bar->wa_value = NULL;
return RET_REMOVE;
}
static ret_t scroll_bar_on_opactiy_animate_end(void* ctx, event_t* e) {
widget_t* widget = WIDGET(ctx);
scroll_bar_t* scroll_bar = SCROLL_BAR(ctx);
return_value_if_fail(widget != NULL && scroll_bar != NULL, RET_REMOVE);
scroll_bar->wa_opactiy = NULL;
if (widget->opacity == 0xff) {
widget_set_visible_only(widget, TRUE);
} else {
widget_set_visible_only(widget, FALSE);
}
return RET_REMOVE;
}
ret_t scroll_bar_scroll_to(widget_t* widget, int32_t value, int32_t duration) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL, RET_BAD_PARAMS);
if (scroll_bar->wa_value != NULL) {
widget_animator_destroy(scroll_bar->wa_value);
scroll_bar->wa_value = NULL;
}
widget_set_opacity(widget, 0xff);
widget_set_visible_only(widget, TRUE);
widget_invalidate_force(widget, NULL);
if (scroll_bar->value == value) {
if (scroll_bar_is_mobile(widget)) {
scroll_bar_hide_by_opacity_animation(widget, duration, TK_ANIMATING_TIME);
}
return RET_OK;
}
#ifndef WITHOUT_WIDGET_ANIMATORS
if (duration > 0) {
scroll_bar->wa_value = widget_animator_value_create(widget, duration, 0, EASING_SIN_INOUT);
return_value_if_fail(scroll_bar->wa_value != NULL, RET_OOM);
widget_animator_value_set_params(scroll_bar->wa_value, scroll_bar->value, value);
widget_animator_start(scroll_bar->wa_value);
widget_animator_on(scroll_bar->wa_value, EVT_ANIM_END, scroll_bar_on_value_animate_end,
scroll_bar);
if (scroll_bar_is_mobile(widget)) {
scroll_bar_hide_by_opacity_animation(widget, TK_ANIMATING_TIME, TK_ANIMATING_TIME);
} else {
scroll_bar->wa_opactiy = NULL;
}
} else
#endif
{
scroll_bar_set_value(widget, value);
scroll_bar_on_value_animate_end(widget, NULL);
}
return RET_OK;
}
static ret_t scroll_bar_update_dragger(widget_t* widget) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL, RET_BAD_PARAMS);
if (!scroll_bar_is_mobile(widget)) {
rect_t r = rect_init(0, 0, 0, 0);
return_value_if_fail(scroll_bar_destop_get_dragger_size(widget, &r) == RET_OK, RET_FAIL);
widget_move_resize(scroll_bar->dragger, r.x, r.y, r.w, r.h);
}
return RET_OK;
}
ret_t scroll_bar_set_value(widget_t* widget, int32_t value) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL, RET_BAD_PARAMS);
if (scroll_bar->value != value) {
value_change_event_t evt;
value_change_event_init(&evt, EVT_VALUE_WILL_CHANGE, widget);
value_set_int(&(evt.old_value), scroll_bar->value);
value_set_int(&(evt.new_value), value);
if (widget_dispatch(widget, (event_t*)&evt) != RET_STOP) {
scroll_bar_set_value_only(widget, value);
evt.e.type = EVT_VALUE_CHANGED;
widget_dispatch(widget, (event_t*)&evt);
widget_invalidate(widget, NULL);
}
scroll_bar_update_dragger(widget);
}
return RET_OK;
}
ret_t scroll_bar_set_value_only(widget_t* widget, int32_t value) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL, RET_BAD_PARAMS);
if (value < 0) {
value = 0;
}
if (value > scroll_bar->virtual_size) {
value = scroll_bar->virtual_size;
}
scroll_bar->value = value;
return RET_OK;
}
/*create*/
static ret_t scroll_bar_set_is_mobile(widget_t* widget, bool_t value) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL, RET_BAD_PARAMS);
if (value == scroll_bar_is_mobile(widget)) {
return RET_OK;
}
if (value) {
widget->vt = TK_REF_VTABLE(scroll_bar_mobile);
widget_destroy_children(widget);
scroll_bar->dragger = NULL;
} else {
widget->vt = TK_REF_VTABLE(scroll_bar_desktop);
scroll_bar_create_children(widget);
}
scroll_bar->auto_hide = scroll_bar_is_mobile(widget);
return RET_OK;
}
static widget_t* scroll_bar_create_internal(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h,
const widget_vtable_t* vt) {
widget_t* widget = widget_create(parent, vt, x, y, w, h);
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL, NULL);
scroll_bar->animatable = TRUE;
scroll_bar->auto_hide = scroll_bar_is_mobile(widget);
scroll_bar->animator_time = TK_ANIMATING_TIME;
widget_set_state(widget, WIDGET_STATE_NORMAL);
return widget;
}
widget_t* scroll_bar_create_mobile(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) {
return scroll_bar_create_internal(parent, x, y, w, h, TK_REF_VTABLE(scroll_bar_mobile));
}
widget_t* scroll_bar_create_desktop_self(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) {
return scroll_bar_create_internal(parent, x, y, w, h, TK_REF_VTABLE(scroll_bar_desktop));
}
widget_t* scroll_bar_create_desktop(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) {
widget_t* widget = scroll_bar_create_desktop_self(parent, x, y, w, h);
return_value_if_fail(widget != NULL, NULL);
scroll_bar_create_children(widget);
return widget;
}
widget_t* scroll_bar_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) {
#ifdef WITH_DESKTOP_STYLE
return scroll_bar_create_desktop(parent, x, y, w, h);
#else
return scroll_bar_create_mobile(parent, x, y, w, h);
#endif /*WITH_DESKTOP_STYLE*/
}
widget_t* scroll_bar_cast(widget_t* widget) {
return_value_if_fail(WIDGET_IS_INSTANCE_OF(widget, scroll_bar_mobile) ||
WIDGET_IS_INSTANCE_OF(widget, scroll_bar_desktop),
NULL);
return widget;
}
ret_t scroll_bar_hide_by_opacity_animation(widget_t* widget, int32_t duration, int32_t delay) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL, RET_BAD_PARAMS);
if (scroll_bar_is_mobile(widget) && !scroll_bar->auto_hide) {
return RET_OK;
}
#ifndef WITHOUT_WIDGET_ANIMATORS
if (scroll_bar->wa_opactiy != NULL) {
widget_animator_destroy(scroll_bar->wa_opactiy);
scroll_bar->wa_opactiy = NULL;
}
scroll_bar->wa_opactiy =
widget_animator_opacity_create(widget, duration, delay, EASING_SIN_INOUT);
widget_animator_on(scroll_bar->wa_opactiy, EVT_ANIM_END, scroll_bar_on_opactiy_animate_end,
scroll_bar);
widget_animator_opacity_set_params(scroll_bar->wa_opactiy, widget->opacity, 0);
widget_animator_start(scroll_bar->wa_opactiy);
#else
widget->opacity = 0;
scroll_bar_on_opactiy_animate_end(widget, NULL);
#endif /*WITHOUT_WIDGET_ANIMATORS*/
return RET_OK;
}
ret_t scroll_bar_show_by_opacity_animation(widget_t* widget, int32_t duration, int32_t delay) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL, RET_BAD_PARAMS);
if (scroll_bar->wa_opactiy != NULL) {
widget_animator_destroy(scroll_bar->wa_opactiy);
scroll_bar->wa_opactiy = NULL;
}
scroll_bar->wa_opactiy =
widget_animator_opacity_create(widget, duration, delay, EASING_SIN_INOUT);
widget_animator_on(scroll_bar->wa_opactiy, EVT_ANIM_END, scroll_bar_on_opactiy_animate_end,
scroll_bar);
widget_animator_opacity_set_params(scroll_bar->wa_opactiy, widget->opacity, 0xff);
widget_animator_start(scroll_bar->wa_opactiy);
return RET_OK;
}
ret_t scroll_bar_set_auto_hide(widget_t* widget, bool_t auto_hide) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL, RET_BAD_PARAMS);
assert(scroll_bar_is_mobile(widget));
scroll_bar->auto_hide = auto_hide;
if (!auto_hide) {
widget_set_visible(widget, TRUE);
}
return RET_OK;
}
ret_t scroll_bar_set_animator_time(widget_t* widget, uint32_t animator_time) {
scroll_bar_t* scroll_bar = SCROLL_BAR(widget);
return_value_if_fail(scroll_bar != NULL, RET_BAD_PARAMS);
scroll_bar->animator_time = animator_time;
return RET_OK;
}

View File

@ -0,0 +1,300 @@
/**
* File: scroll_bar.h
* Author: AWTK Develop Team
* Brief: scroll_bar
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-07-01 Li XianJing <xianjimli@hotmail.com> created
*
*/
#ifndef TK_SCROLL_BAR_H
#define TK_SCROLL_BAR_H
#include "base/widget.h"
#include "base/widget_animator.h"
BEGIN_C_DECLS
/**
* @class scroll_bar_t
* @parent widget_t
* @annotation ["scriptable","design","widget"]
* 滚动条控件。
*
*> 目前只支持垂直滚动。
*
* scroll\_bar\_t是[widget\_t](widget_t.md)的子类控件widget\_t的函数均适用于scroll\_bar\_t控件。
*
* 在xml中使用"scroll\_bar"或"scroll\_bar\_d"或"scroll\_bar\_m"标签创建滚动条控件。如:
*
* ```xml
* <list_view x="0" y="30" w="100%" h="-80" item_height="60">
* <scroll_view name="view" x="0" y="0" w="100%" h="100%">
* ...
* </scroll_view>
* <scroll_bar_m name="bar" x="right" y="0" w="6" h="100%" value="0"/>
* </list_view>
* ```
*
* > 更多用法请参考:[list\_view\_m.xml](
*https://github.com/zlgopen/awtk/blob/master/design/default/ui/list_view_m.xml)
*
* 在c代码中使用函数scroll\_bar\_create创建列表项控件。如
*
* ```c
* widget_t* scroll_bar = scroll_bar_create(list_view, 0, 0, 0, 0);
* ```
*
* ```xml
* <style name="default">
* <normal bg_color="#c0c0c0" fg_color="#808080"/>
* <over bg_color="#c0c0c0" fg_color="#808080"/>
* <pressed bg_color="#c0c0c0" fg_color="#808080"/>
* </style>
* ```
*
* > 更多用法请参考:[theme default](
*https://github.com/zlgopen/awtk/blob/master/design/default/styles/default.xml#L350)
*
*/
typedef struct _scroll_bar_t {
widget_t widget;
/**
* @property {int32_t} virtual_size
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 虚拟宽度或高度。
*/
int32_t virtual_size;
/**
* @property {int32_t} value
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 当前的值。
*/
int32_t value;
/**
* @property {int32_t} row
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 行的高度。
*/
int32_t row;
/**
* @property {uint32_t} animator_time
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 翻页滚动动画时间。
*/
uint32_t animator_time;
/**
* @property {bool_t} animatable
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 滚动时是否启用动画。
*/
bool_t animatable;
/**
* @property {bool_t} auto_hide
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 是否自动隐藏(仅对mobile风格的滚动条有效)。
*/
bool_t auto_hide;
/*private*/
widget_t* dragger;
widget_animator_t* wa_value;
widget_animator_t* wa_opactiy;
} scroll_bar_t;
/**
* @event {value_change_event_t} EVT_VALUE_WILL_CHANGE
* 值(滚动值)即将改变事件。
*/
/**
* @event {value_change_event_t} EVT_VALUE_CHANGED
* 值(滚动值)改变事件。
*/
/**
* @method scroll_bar_create
* 创建scroll_bar对象
*
* > 根据宏WITH_DESKTOP_STYLE决定创建desktop风格还是mobile风格的滚动条
*
* @annotation ["constructor", "scriptable"]
* @param {widget_t*} parent 父控件
* @param {xy_t} x x坐标
* @param {xy_t} y y坐标
* @param {wh_t} w 宽度
* @param {wh_t} h 高度
*
* @return {widget_t*} 对象。
*/
widget_t* scroll_bar_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h);
/**
* @method scroll_bar_cast
* 转换为scroll_bar对象(供脚本语言使用)。
* @annotation ["cast", "scriptable"]
* @param {widget_t*} widget scroll_bar对象。
*
* @return {widget_t*} scroll_bar对象。
*/
widget_t* scroll_bar_cast(widget_t* widget);
/**
* @method scroll_bar_create_mobile
* 创建mobile风格的scroll_bar对象
* @annotation ["constructor", "scriptable"]
* @param {widget_t*} parent 父控件
* @param {xy_t} x x坐标
* @param {xy_t} y y坐标
* @param {wh_t} w 宽度
* @param {wh_t} h 高度
*
* @return {widget_t*} 对象。
*/
widget_t* scroll_bar_create_mobile(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h);
/**
* @method scroll_bar_create_desktop
* 创建desktop风格的scroll_bar对象
* @annotation ["constructor", "scriptable"]
* @param {widget_t*} parent 父控件
* @param {xy_t} x x坐标
* @param {xy_t} y y坐标
* @param {wh_t} w 宽度
* @param {wh_t} h 高度
*
* @return {widget_t*} 对象。
*/
widget_t* scroll_bar_create_desktop(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h);
/**
* @method scroll_bar_set_params
* 设置参数。
* @annotation ["scriptable"]
* @param {widget_t*} widget scroll_bar控件。
* @param {int32_t} virtual_size 虚拟高度。
* @param {int32_t} row 每一行的高度。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_bar_set_params(widget_t* widget, int32_t virtual_size, int32_t row);
/**
* @method scroll_bar_scroll_to
* 滚动到指定的值。
* @annotation ["scriptable"]
* @param {widget_t*} widget scroll_bar控件。
* @param {int32_t} value 值。
* @param {int32_t} duration 动画持续时间。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_bar_scroll_to(widget_t* widget, int32_t value, int32_t duration);
/**
* @method scroll_bar_set_value
* 设置值并触发EVT_VALUE_CHANGED事件。
* @annotation ["scriptable"]
* @param {widget_t*} widget scroll_bar控件。
* @param {int32_t} value 值。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_bar_set_value(widget_t* widget, int32_t value);
/**
* @method scroll_bar_add_delta
* 在当前的值上增加一个值并触发EVT_VALUE_CHANGED事件。
* @annotation ["scriptable"]
* @param {widget_t*} widget scroll_bar控件。
* @param {int32_t} delta 值。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_bar_add_delta(widget_t* widget, int32_t delta);
/**
* @method scroll_bar_scroll_delta
* 在当前的值上增加一个值并滚动到新的值并触发EVT_VALUE_CHANGED事件。
* @annotation ["scriptable"]
* @param {widget_t*} widget scroll_bar控件。
* @param {int32_t} delta 值。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_bar_scroll_delta(widget_t* widget, int32_t delta);
/**
* @method scroll_bar_set_value_only
* 设置值但不触发EVT_VALUE_CHANGED事件。
* @annotation ["scriptable"]
* @param {widget_t*} widget scroll_bar控件。
* @param {int32_t} value 值。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_bar_set_value_only(widget_t* widget, int32_t value);
/**
* @method scroll_bar_set_auto_hide
* 设置auto_hide属性。
*
*>仅对mobile风格的滚动条有效
*
* @annotation ["scriptable"]
* @param {widget_t*} widget scroll_bar控件。
* @param {bool_t} auto_hide 值。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_bar_set_auto_hide(widget_t* widget, bool_t auto_hide);
/**
* @method scroll_bar_is_mobile
* 判断是否是mobile风格的滚动条。
* @annotation ["scriptable"]
* @param {widget_t*} widget scroll_bar控件。
*
* @return {bool_t} 返回TRUE表示是mobile风格的否则表示不是mobile风格的。
*/
bool_t scroll_bar_is_mobile(widget_t* widget);
/**
* @method scroll_bar_set_animator_time
* 设置翻页滚动动画时间。
*
* @annotation ["scriptable"]
* @param {widget_t*} widget scroll_bar控件。
* @param {uint32_t} animator_time 时间。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_bar_set_animator_time(widget_t* widget, uint32_t animator_time);
/* private */
ret_t scroll_bar_hide_by_opacity_animation(widget_t* widget, int32_t duration, int32_t delay);
ret_t scroll_bar_show_by_opacity_animation(widget_t* widget, int32_t duration, int32_t delay);
#define SCROLL_BAR_PROP_IS_MOBILE "is_mobile"
#define SCROLL_BAR_PROP_ANIMATOR_TIME "animator_time"
#define SCROLL_BAR(widget) ((scroll_bar_t*)(scroll_bar_cast(WIDGET(widget))))
/*public for subclass and runtime type check*/
TK_EXTERN_VTABLE(scroll_bar_mobile);
TK_EXTERN_VTABLE(scroll_bar_desktop);
END_C_DECLS
#endif /*TK_SCROLL_BAR_H*/

View File

@ -0,0 +1,829 @@
/**
* File: scroll_view.c
* Author: AWTK Develop Team
* Brief: scroll_view
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-07-03 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include "tkc/mem.h"
#include "tkc/utils.h"
#include "base/layout.h"
#include "base/velocity.h"
#include "tkc/time_now.h"
#include "scroll_view/scroll_view.h"
#include "base/widget_vtable.h"
#include "base/image_manager.h"
#include "widget_animators/widget_animator_scroll.h"
#define SCROLL_VIEW_DEFAULT_XSPEED_SCALE 2.0f
#define SCROLL_VIEW_DEFAULT_YSPEED_SCALE 2.0f
static uint32_t scroll_view_get_curr_page(widget_t* widget);
static uint32_t scroll_view_get_page_max_number(widget_t* widget);
static ret_t scroll_view_set_xoffset(scroll_view_t* scroll_view, int32_t xoffset) {
offset_change_event_t evt;
if (scroll_view->xoffset != xoffset) {
offset_change_event_init(&evt, EVT_PAGE_CHANGING, WIDGET(scroll_view),
(float_t)scroll_view->xoffset_save, (float_t)xoffset);
scroll_view->xoffset = xoffset;
widget_dispatch(WIDGET(scroll_view), (event_t*)&evt);
}
return RET_OK;
}
static ret_t scroll_view_set_yoffset(scroll_view_t* scroll_view, int32_t yoffset) {
offset_change_event_t evt;
if (scroll_view->yoffset != yoffset) {
offset_change_event_init(&evt, EVT_PAGE_CHANGING, WIDGET(scroll_view),
(float_t)scroll_view->yoffset_save, (float_t)yoffset);
scroll_view->yoffset = yoffset;
widget_dispatch(WIDGET(scroll_view), (event_t*)&evt);
}
return RET_OK;
}
static ret_t scroll_view_get_item_rect(widget_t* parent, widget_t* widget, rect_t* item_rect) {
rect_t r;
point_t p;
WIDGET_FOR_EACH_CHILD_BEGIN(widget, iter, i)
memset(&p, 0x0, sizeof(point_t));
widget_to_screen_ex(iter, parent, &p);
r = rect_init(p.x, p.y, iter->w, iter->h);
rect_merge(item_rect, &r);
scroll_view_get_item_rect(parent, iter, item_rect);
WIDGET_FOR_EACH_CHILD_END();
return RET_OK;
}
static ret_t scroll_view_update_virtual_size(widget_t* widget) {
int32_t virtual_w = 0;
int32_t virtual_h = 0;
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL && widget != NULL, RET_BAD_PARAMS);
virtual_w = tk_max(scroll_view->virtual_w, widget->w);
virtual_h = tk_max(scroll_view->virtual_h, widget->h);
WIDGET_FOR_EACH_CHILD_BEGIN(widget, iter, i)
int32_t r = 0;
int32_t b = 0;
if (scroll_view->recursive) {
rect_t rect = rect_init(0, 0, iter->w, iter->h);
scroll_view_get_item_rect(iter, iter, &rect);
r = iter->x + rect.x + rect.w;
b = iter->y + rect.y + rect.h;
} else {
r = iter->x + iter->w;
b = iter->y + iter->h;
}
if (r > virtual_w) {
virtual_w = r;
}
if (b > virtual_h) {
virtual_h = b;
}
WIDGET_FOR_EACH_CHILD_END();
scroll_view->virtual_w = virtual_w;
scroll_view->virtual_h = virtual_h;
return RET_OK;
}
static ret_t scroll_view_on_layout_children(widget_t* widget) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(widget != NULL && scroll_view != NULL, RET_BAD_PARAMS);
if (scroll_view->on_layout_children) {
scroll_view->on_layout_children(widget);
} else {
widget_layout_children_default(widget);
scroll_view_update_virtual_size(widget);
scroll_view_set_offset(widget,
tk_min(scroll_view->xoffset, (scroll_view->virtual_w - widget->w)),
tk_min(scroll_view->yoffset, (scroll_view->virtual_h - widget->h)));
}
if (scroll_view->snap_to_page) {
int32_t curr_page = scroll_view_get_curr_page(widget);
uint32_t max_page = scroll_view_get_page_max_number(widget);
scroll_view->xoffset_end = scroll_view->xoffset;
scroll_view->yoffset_end = scroll_view->yoffset;
scroll_view->fix_end_offset(widget);
scroll_view->xoffset = scroll_view->xoffset_end;
scroll_view->yoffset = scroll_view->yoffset_end;
if (scroll_view->curr_page != curr_page || scroll_view->max_page != max_page) {
scroll_view->max_page = max_page;
scroll_view->curr_page = curr_page;
widget_dispatch_simple_event(widget, EVT_PAGE_CHANGED);
}
}
return RET_OK;
}
static ret_t scroll_view_on_pointer_down(scroll_view_t* scroll_view, pointer_event_t* e) {
velocity_t* v = &(scroll_view->velocity);
velocity_reset(v);
scroll_view->down.x = e->x;
scroll_view->down.y = e->y;
scroll_view->xoffset_save = scroll_view->xoffset;
scroll_view->yoffset_save = scroll_view->yoffset;
scroll_view->xoffset_end = scroll_view->xoffset;
scroll_view->yoffset_end = scroll_view->yoffset;
velocity_update(v, e->e.time, e->x, e->y);
return RET_OK;
}
static ret_t scroll_view_on_scroll_done(void* ctx, event_t* e) {
widget_t* widget = WIDGET(ctx);
scroll_view_t* scroll_view = SCROLL_VIEW(ctx);
return_value_if_fail(widget != NULL && scroll_view != NULL, RET_BAD_PARAMS);
scroll_view->wa = NULL;
scroll_view->curr_page = scroll_view_get_curr_page(widget);
widget_invalidate_force(widget, NULL);
widget_dispatch_simple_event(widget, EVT_SCROLL_END);
widget_dispatch_simple_event(widget, EVT_PAGE_CHANGED);
return RET_REMOVE;
}
static ret_t scroll_view_fix_end_offset_default(widget_t* widget) {
int32_t xoffset_end = 0;
int32_t yoffset_end = 0;
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(widget != NULL && scroll_view != NULL, RET_BAD_PARAMS);
xoffset_end = scroll_view->xoffset_end;
yoffset_end = scroll_view->yoffset_end;
xoffset_end = tk_max(xoffset_end, 0);
yoffset_end = tk_max(yoffset_end, 0);
xoffset_end = tk_min(xoffset_end, (scroll_view->virtual_w - widget->w));
yoffset_end = tk_min(yoffset_end, (scroll_view->virtual_h - widget->h));
if (scroll_view->virtual_w <= widget->w) {
xoffset_end = 0;
}
if (scroll_view->virtual_h <= widget->h) {
yoffset_end = 0;
}
scroll_view->xoffset_end = xoffset_end;
scroll_view->yoffset_end = yoffset_end;
return RET_OK;
}
static int32_t scroll_view_get_snap_to_page_offset_value(widget_t* widget, int32_t offset_end) {
uint32_t tmp = 0;
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, offset_end);
if (scroll_view->xslidable && !scroll_view->yslidable) {
tmp = widget->w;
} else if (!scroll_view->xslidable && scroll_view->yslidable) {
tmp = widget->h;
}
if (tmp != 0) {
int32_t n = (int32_t)((offset_end + tmp * 0.5f) / tmp);
if (scroll_view->move_to_page) {
if (scroll_view->curr_page - n > 1) {
n = scroll_view->curr_page - 1;
} else if (n - scroll_view->curr_page > 1) {
n = scroll_view->curr_page + 1;
}
}
offset_end = n * tmp;
}
return offset_end;
}
ret_t scroll_view_scroll_to(widget_t* widget, int32_t xoffset_end, int32_t yoffset_end,
int32_t duration) {
int32_t xoffset = 0;
int32_t yoffset = 0;
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_FAIL);
if (scroll_view->snap_to_page) {
xoffset_end = scroll_view_get_snap_to_page_offset_value(widget, xoffset_end);
yoffset_end = scroll_view_get_snap_to_page_offset_value(widget, yoffset_end);
}
if (scroll_view->fix_end_offset) {
scroll_view->xoffset_end = xoffset_end;
scroll_view->yoffset_end = yoffset_end;
scroll_view->fix_end_offset(widget);
xoffset_end = scroll_view->xoffset_end;
yoffset_end = scroll_view->yoffset_end;
}
if (xoffset_end == scroll_view->xoffset && yoffset_end == scroll_view->yoffset) {
return RET_OK;
}
xoffset = scroll_view->xoffset;
yoffset = scroll_view->yoffset;
if (scroll_view->on_scroll_to) {
scroll_view->on_scroll_to(widget, xoffset_end, yoffset_end, duration);
}
#ifndef WITHOUT_WIDGET_ANIMATORS
if (scroll_view->wa != NULL) {
widget_animator_scroll_t* wa = (widget_animator_scroll_t*)scroll_view->wa;
if (xoffset_end != scroll_view->xoffset) {
bool_t changed = wa->x_to != xoffset_end;
bool_t in_range = wa->x_to > 0 && wa->x_to < (scroll_view->virtual_w - widget->w);
if (changed && in_range) {
wa->x_to = xoffset_end;
wa->x_from = scroll_view->xoffset;
} else if (!scroll_view->yslidable) {
return RET_OK;
}
}
if (yoffset_end != scroll_view->yoffset) {
bool_t changed = wa->y_to != yoffset_end;
bool_t in_range = wa->y_to > 0 && wa->y_to < (scroll_view->virtual_h - widget->h);
if (changed && in_range) {
wa->y_to = yoffset_end;
wa->y_from = scroll_view->yoffset;
} else if (!scroll_view->xslidable) {
return RET_OK;
}
}
wa->base.now = 0;
wa->base.start_time = 0;
} else {
scroll_view->wa = widget_animator_scroll_create(widget, TK_ANIMATING_TIME, 0, EASING_SIN_INOUT);
return_value_if_fail(scroll_view->wa != NULL, RET_OOM);
widget_animator_scroll_set_params(scroll_view->wa, xoffset, yoffset, xoffset_end, yoffset_end);
widget_animator_on(scroll_view->wa, EVT_ANIM_END, scroll_view_on_scroll_done, scroll_view);
widget_animator_start(scroll_view->wa);
widget_dispatch_simple_event(widget, EVT_SCROLL_START);
}
#else
scroll_view->xoffset = xoffset_end;
scroll_view->yoffset = yoffset_end;
scroll_view_on_scroll_done(widget, NULL);
#endif /*WITHOUT_WIDGET_ANIMATORS*/
return RET_OK;
}
ret_t scroll_view_scroll_delta_to(widget_t* widget, int32_t xoffset_delta, int32_t yoffset_delta,
int32_t duration) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_FAIL);
scroll_view->xoffset_end = scroll_view->xoffset + xoffset_delta;
scroll_view->yoffset_end = scroll_view->yoffset + yoffset_delta;
return scroll_view_scroll_to(widget, scroll_view->xoffset_end, scroll_view->yoffset_end,
duration);
}
static ret_t scroll_view_on_pointer_down_abort(scroll_view_t* scroll_view, pointer_event_t* e) {
widget_t* widget = WIDGET(scroll_view);
(void)e;
if (scroll_view->xslidable || scroll_view->yslidable) {
scroll_view_scroll_to(widget, scroll_view->xoffset_end, scroll_view->yoffset_end,
TK_ANIMATING_TIME);
}
return RET_OK;
}
static ret_t scroll_view_on_pointer_up(scroll_view_t* scroll_view, pointer_event_t* e) {
widget_t* widget = WIDGET(scroll_view);
velocity_t* v = &(scroll_view->velocity);
int32_t move_dx = e->x - scroll_view->down.x;
int32_t move_dy = e->y - scroll_view->down.y;
velocity_update(v, e->e.time, e->x, e->y);
if (scroll_view->xslidable || scroll_view->yslidable) {
#ifndef WITHOUT_WIDGET_ANIMATORS
int yv = v->yv;
int xv = v->xv;
#else
int yv = 0;
int xv = 0;
#endif /*WITHOUT_WIDGET_ANIMATORS*/
if (scroll_view->wa != NULL) {
widget_animator_scroll_t* wa = (widget_animator_scroll_t*)scroll_view->wa;
int32_t dx = wa->x_to - scroll_view->xoffset;
int32_t dy = wa->y_to - scroll_view->yoffset;
xv -= dx;
yv -= dy;
log_debug("dx=%d xv=%d dy=%d yv=%d\n", dx, xv, dy, yv);
}
if (scroll_view->xslidable) {
if (tk_abs(move_dx) > TK_CLICK_TOLERANCE) {
scroll_view->xoffset_end = scroll_view->xoffset - xv * scroll_view->xspeed_scale;
} else {
scroll_view->xoffset_end = scroll_view->xoffset - xv / scroll_view->xspeed_scale;
}
}
if (scroll_view->yslidable) {
if (tk_abs(move_dy) > TK_CLICK_TOLERANCE) {
scroll_view->yoffset_end = scroll_view->yoffset - yv * scroll_view->yspeed_scale;
} else {
scroll_view->yoffset_end = scroll_view->yoffset - yv / scroll_view->yspeed_scale;
}
}
scroll_view_scroll_to(widget, scroll_view->xoffset_end, scroll_view->yoffset_end,
TK_ANIMATING_TIME);
}
return RET_OK;
}
static ret_t scroll_view_notify_scrolled(scroll_view_t* scroll_view) {
widget_t* widget = WIDGET(scroll_view);
if (scroll_view->on_scroll) {
scroll_view->on_scroll(widget, scroll_view->xoffset, scroll_view->yoffset);
}
widget_dispatch_simple_event(widget, EVT_SCROLL);
return RET_OK;
}
static ret_t scroll_view_on_pointer_move(scroll_view_t* scroll_view, pointer_event_t* e) {
int32_t dx = 0;
int32_t dy = 0;
velocity_t* v = NULL;
return_value_if_fail(scroll_view != NULL && e != NULL, RET_BAD_PARAMS);
v = &(scroll_view->velocity);
dx = e->x - scroll_view->down.x;
dy = e->y - scroll_view->down.y;
velocity_update(v, e->e.time, e->x, e->y);
if (scroll_view->wa == NULL) {
if (scroll_view->xslidable && dx) {
scroll_view_set_xoffset(scroll_view, scroll_view->xoffset_save - dx);
}
if (scroll_view->yslidable && dy) {
scroll_view_set_yoffset(scroll_view, scroll_view->yoffset_save - dy);
}
scroll_view_notify_scrolled(scroll_view);
}
scroll_view->first_move_after_down = FALSE;
return RET_OK;
}
static bool_t scroll_view_is_dragged(widget_t* widget, pointer_event_t* evt) {
int32_t delta = 0;
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, FALSE);
if (scroll_view->xslidable && scroll_view->yslidable) {
int32_t xdelta = evt->x - scroll_view->down.x;
int32_t ydelta = evt->y - scroll_view->down.y;
delta = tk_abs(xdelta) > tk_abs(ydelta) ? xdelta : ydelta;
} else if (scroll_view->yslidable) {
delta = evt->y - scroll_view->down.y;
} else {
delta = evt->x - scroll_view->down.x;
}
return scroll_view->snap_to_page || (tk_abs(delta) >= TK_DRAG_THRESHOLD);
}
static ret_t scroll_view_on_event(widget_t* widget, event_t* e) {
ret_t ret = RET_OK;
uint16_t type = e->type;
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_BAD_PARAMS);
switch (type) {
case EVT_POINTER_DOWN:
scroll_view->pressed = TRUE;
scroll_view->dragged = FALSE;
widget_grab(widget->parent, widget);
scroll_view->first_move_after_down = TRUE;
scroll_view_on_pointer_down(scroll_view, (pointer_event_t*)e);
break;
case EVT_POINTER_DOWN_ABORT:
scroll_view_on_pointer_down_abort(scroll_view, (pointer_event_t*)e);
if (scroll_view->pressed) {
widget_ungrab(widget->parent, widget);
}
scroll_view->pressed = FALSE;
scroll_view->dragged = FALSE;
break;
case EVT_POINTER_UP: {
pointer_event_t* evt = (pointer_event_t*)e;
if (scroll_view->pressed && scroll_view_is_dragged(widget, evt)) {
scroll_view_on_pointer_up(scroll_view, (pointer_event_t*)e);
}
scroll_view->pressed = FALSE;
scroll_view->dragged = FALSE;
widget_ungrab(widget->parent, widget);
break;
}
case EVT_POINTER_MOVE: {
pointer_event_t* evt = (pointer_event_t*)e;
if (!scroll_view->pressed || !(scroll_view->xslidable || scroll_view->yslidable)) {
break;
}
if (scroll_view->dragged) {
scroll_view_on_pointer_move(scroll_view, evt);
widget_invalidate_force(widget, NULL);
} else {
if (scroll_view_is_dragged(widget, evt)) {
pointer_event_t abort = *evt;
abort.e.type = EVT_POINTER_DOWN_ABORT;
widget_dispatch_event_to_target_recursive(widget, (event_t*)(&abort));
scroll_view->dragged = TRUE;
}
}
ret = scroll_view->dragged ? RET_STOP : RET_OK;
break;
}
default:
break;
}
return ret;
}
static ret_t scroll_view_on_paint_children(widget_t* widget, canvas_t* c) {
rect_t r;
rect_t r_save;
int32_t xoffset = 0;
int32_t yoffset = 0;
vgcanvas_t* vg = NULL;
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(widget != NULL && c != NULL && scroll_view != NULL, RET_BAD_PARAMS);
vg = canvas_get_vgcanvas(c);
xoffset = -scroll_view->xoffset;
yoffset = -scroll_view->yoffset;
r = rect_init(c->ox, c->oy, widget->w, widget->h);
canvas_translate(c, xoffset, yoffset);
canvas_get_clip_rect(c, &r_save);
r = rect_intersect(&r, &r_save);
if (vg != NULL) {
vgcanvas_save(vg);
vgcanvas_clip_rect(vg, (float_t)r.x, (float_t)r.y, (float_t)r.w, (float_t)r.h);
}
canvas_set_clip_rect(c, &r);
if (scroll_view->on_paint_children) {
scroll_view->on_paint_children(widget, c);
} else {
widget_on_paint_children_default(widget, c);
}
canvas_set_clip_rect(c, &r_save);
canvas_untranslate(c, xoffset, yoffset);
if (vg != NULL) {
vgcanvas_clip_rect(vg, (float_t)r_save.x, (float_t)r_save.y, (float_t)r_save.w,
(float_t)r_save.h);
vgcanvas_restore(vg);
}
return RET_OK;
}
static ret_t scroll_view_on_add_child(widget_t* widget, widget_t* child) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_BAD_PARAMS);
if (scroll_view->on_add_child) {
return scroll_view->on_add_child(widget, child);
} else {
return RET_CONTINUE;
}
}
static widget_t* scroll_view_find_target(widget_t* widget, xy_t x, xy_t y) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, NULL);
if (scroll_view->find_target) {
return scroll_view->find_target(widget, x, y);
} else {
return widget_find_target_default(widget, x, y);
}
}
static uint32_t scroll_view_get_page_max_number(widget_t* widget) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(widget != NULL && scroll_view != NULL, 0);
if (scroll_view->xslidable && !scroll_view->yslidable) {
return scroll_view->virtual_w / widget->w;
} else if (!scroll_view->xslidable && scroll_view->yslidable) {
return scroll_view->virtual_h / widget->h;
}
return 0;
}
static uint32_t scroll_view_get_curr_page(widget_t* widget) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(widget != NULL && scroll_view != NULL, 0);
if (scroll_view->xslidable && !scroll_view->yslidable) {
return scroll_view->xoffset / widget->w;
} else if (!scroll_view->xslidable && scroll_view->yslidable) {
return scroll_view->yoffset / widget->h;
}
return 0;
}
static ret_t scroll_view_set_curr_page(widget_t* widget, int32_t new_page) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(widget != NULL && scroll_view != NULL, RET_BAD_PARAMS);
if (scroll_view->xslidable && !scroll_view->yslidable) {
scroll_view->xoffset_end = new_page * widget->w;
} else if (!scroll_view->xslidable && scroll_view->yslidable) {
scroll_view->yoffset_end = new_page * widget->h;
}
scroll_view->snap_to_page = FALSE;
scroll_view_scroll_to(widget, scroll_view->xoffset_end, scroll_view->yoffset_end,
TK_ANIMATING_TIME);
scroll_view->snap_to_page = TRUE;
return RET_OK;
}
static ret_t scroll_view_get_prop(widget_t* widget, const char* name, value_t* v) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, WIDGET_PROP_VIRTUAL_W)) {
value_set_int(v, tk_max(widget->w, scroll_view->virtual_w));
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_VIRTUAL_H)) {
value_set_int(v, tk_max(widget->h, scroll_view->virtual_h));
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_XOFFSET)) {
value_set_int(v, scroll_view->xoffset);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_YOFFSET)) {
value_set_int(v, scroll_view->yoffset);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_XSLIDABLE)) {
value_set_bool(v, scroll_view->xslidable);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_YSLIDABLE)) {
value_set_bool(v, scroll_view->yslidable);
return RET_OK;
} else if (tk_str_eq(name, SCROLL_VIEW_X_SPEED_SCALE)) {
value_set_float(v, scroll_view->xspeed_scale);
return RET_OK;
} else if (tk_str_eq(name, SCROLL_VIEW_Y_SPEED_SCALE)) {
value_set_float(v, scroll_view->yspeed_scale);
return RET_OK;
} else if (tk_str_eq(name, SCROLL_VIEW_RECURSIVE)) {
value_set_bool(v, scroll_view->recursive);
return RET_OK;
} else if (tk_str_eq(name, SCROLL_VIEW_MOVE_TO_PAGE)) {
value_set_bool(v, scroll_view->move_to_page);
return RET_OK;
} else if (tk_str_eq(name, SCROLL_VIEW_SNAP_TO_PAGE)) {
value_set_bool(v, scroll_view->snap_to_page);
return RET_OK;
}
if (scroll_view->snap_to_page) {
if (tk_str_eq(name, WIDGET_PROP_PAGE_MAX_NUMBER)) {
value_set_uint32(v, scroll_view_get_page_max_number(widget));
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_CURR_PAGE)) {
value_set_uint32(v, scroll_view_get_curr_page(widget));
return RET_OK;
}
}
return RET_NOT_FOUND;
}
static ret_t scroll_view_set_prop(widget_t* widget, const char* name, const value_t* v) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, WIDGET_PROP_VIRTUAL_W)) {
scroll_view->virtual_w = value_int(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_VIRTUAL_H)) {
scroll_view->virtual_h = value_int(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_XSLIDABLE)) {
scroll_view->xslidable = value_bool(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_YSLIDABLE)) {
scroll_view->yslidable = value_bool(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_XOFFSET)) {
scroll_view_set_xoffset(scroll_view, value_int(v));
scroll_view_notify_scrolled(scroll_view);
widget_invalidate_force(widget, NULL);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_YOFFSET)) {
scroll_view_set_yoffset(scroll_view, value_int(v));
scroll_view_notify_scrolled(scroll_view);
widget_invalidate_force(widget, NULL);
return RET_OK;
} else if (tk_str_eq(name, SCROLL_VIEW_X_SPEED_SCALE)) {
scroll_view->xspeed_scale = value_float(v);
return RET_OK;
} else if (tk_str_eq(name, SCROLL_VIEW_Y_SPEED_SCALE)) {
scroll_view->yspeed_scale = value_float(v);
return RET_OK;
} else if (tk_str_eq(name, SCROLL_VIEW_RECURSIVE)) {
return scroll_view_set_recursive(widget, value_bool(v));
} else if (tk_str_eq(name, SCROLL_VIEW_MOVE_TO_PAGE)) {
return scroll_view_set_move_to_page(widget, value_bool(v));
} else if (tk_str_eq(name, SCROLL_VIEW_SNAP_TO_PAGE)) {
return scroll_view_set_snap_to_page(widget, value_bool(v));
} else if (scroll_view->snap_to_page && tk_str_eq(name, WIDGET_PROP_CURR_PAGE)) {
return scroll_view_set_curr_page(widget, value_int(v));
}
return RET_NOT_FOUND;
}
static ret_t scroll_view_get_offset(widget_t* widget, xy_t* out_x, xy_t* out_y) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL && out_x != NULL && out_y != NULL, RET_BAD_PARAMS);
*out_x = scroll_view->xoffset;
*out_y = scroll_view->yoffset;
return RET_OK;
}
static const char* s_scroll_view_clone_properties[] = {
WIDGET_PROP_VIRTUAL_W, WIDGET_PROP_VIRTUAL_H, WIDGET_PROP_XSLIDABLE,
WIDGET_PROP_YSLIDABLE, WIDGET_PROP_XOFFSET, WIDGET_PROP_YOFFSET,
SCROLL_VIEW_X_SPEED_SCALE, SCROLL_VIEW_Y_SPEED_SCALE, SCROLL_VIEW_RECURSIVE,
SCROLL_VIEW_MOVE_TO_PAGE, SCROLL_VIEW_SNAP_TO_PAGE, NULL};
TK_DECL_VTABLE(scroll_view) = {.size = sizeof(scroll_view_t),
.type = WIDGET_TYPE_SCROLL_VIEW,
.scrollable = TRUE,
.clone_properties = s_scroll_view_clone_properties,
.parent = TK_PARENT_VTABLE(widget),
.create = scroll_view_create,
.on_event = scroll_view_on_event,
.on_layout_children = scroll_view_on_layout_children,
.on_paint_children = scroll_view_on_paint_children,
.on_add_child = scroll_view_on_add_child,
.find_target = scroll_view_find_target,
.get_offset = scroll_view_get_offset,
.get_prop = scroll_view_get_prop,
.set_prop = scroll_view_set_prop};
widget_t* scroll_view_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) {
widget_t* widget = widget_create(parent, TK_REF_VTABLE(scroll_view), x, y, w, h);
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, NULL);
scroll_view->snap_to_page = FALSE;
scroll_view->xspeed_scale = SCROLL_VIEW_DEFAULT_XSPEED_SCALE;
scroll_view->yspeed_scale = SCROLL_VIEW_DEFAULT_YSPEED_SCALE;
scroll_view->fix_end_offset = scroll_view_fix_end_offset_default;
return widget;
}
ret_t scroll_view_set_virtual_w(widget_t* widget, wh_t w) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_BAD_PARAMS);
scroll_view->virtual_w = w;
return RET_OK;
}
ret_t scroll_view_set_virtual_h(widget_t* widget, wh_t h) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_BAD_PARAMS);
scroll_view->virtual_h = h;
return RET_OK;
}
ret_t scroll_view_set_speed_scale(widget_t* widget, float_t xspeed_scale, float_t yspeed_scale) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_FAIL);
scroll_view->xspeed_scale = xspeed_scale;
scroll_view->yspeed_scale = yspeed_scale;
return RET_OK;
}
ret_t scroll_view_set_offset(widget_t* widget, int32_t xoffset, int32_t yoffset) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_FAIL);
if (scroll_view->snap_to_page) {
xoffset = scroll_view_get_snap_to_page_offset_value(widget, xoffset);
yoffset = scroll_view_get_snap_to_page_offset_value(widget, yoffset);
}
scroll_view_set_xoffset(scroll_view, xoffset);
scroll_view_set_yoffset(scroll_view, yoffset);
widget_invalidate_force(widget, NULL);
return RET_OK;
}
ret_t scroll_view_set_xslidable(widget_t* widget, bool_t xslidable) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_FAIL);
scroll_view->xslidable = xslidable;
return RET_OK;
}
ret_t scroll_view_set_yslidable(widget_t* widget, bool_t yslidable) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_FAIL);
scroll_view->yslidable = yslidable;
return RET_OK;
}
ret_t scroll_view_set_move_to_page(widget_t* widget, bool_t move_to_page) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_FAIL);
scroll_view->move_to_page = move_to_page;
return RET_OK;
}
ret_t scroll_view_set_snap_to_page(widget_t* widget, bool_t snap_to_page) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_FAIL);
scroll_view->snap_to_page = snap_to_page;
if (snap_to_page) {
if (scroll_view->xspeed_scale == SCROLL_VIEW_DEFAULT_XSPEED_SCALE) {
scroll_view->xspeed_scale /= 2;
}
if (scroll_view->yspeed_scale == SCROLL_VIEW_DEFAULT_YSPEED_SCALE) {
scroll_view->yspeed_scale /= 2;
}
}
return RET_OK;
}
ret_t scroll_view_set_recursive_only(widget_t* widget, bool_t recursive) {
scroll_view_t* scroll_view = SCROLL_VIEW(widget);
return_value_if_fail(scroll_view != NULL, RET_FAIL);
scroll_view->recursive = recursive;
return RET_OK;
}
ret_t scroll_view_set_recursive(widget_t* widget, bool_t recursive) {
ret_t ret = RET_OK;
ret = scroll_view_set_recursive_only(widget, recursive);
return_value_if_fail(ret == RET_OK, ret);
return widget_layout(widget);
}
widget_t* scroll_view_cast(widget_t* widget) {
return_value_if_fail(WIDGET_IS_INSTANCE_OF(widget, scroll_view), NULL);
return widget;
}

View File

@ -0,0 +1,371 @@
/**
* File: scroll_view.h
* Author: AWTK Develop Team
* Brief: scroll_view
*
* Copyright (c) 2018 - 2021 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-07-03 Li XianJing <xianjimli@hotmail.com> created
*
*/
#ifndef TK_SCROLL_VIEW_H
#define TK_SCROLL_VIEW_H
#include "base/widget.h"
#include "base/velocity.h"
#include "base/widget_animator.h"
BEGIN_C_DECLS
typedef ret_t (*scroll_view_fix_end_offset_t)(widget_t* widget);
typedef ret_t (*scroll_view_on_scroll_t)(widget_t* widget, int32_t xoffset, int32_t yoffset);
typedef ret_t (*scroll_view_on_scroll_to_t)(widget_t* widget, int32_t xoffset_end,
int32_t yoffset_end, int32_t duration);
/**
* @class scroll_view_t
* @parent widget_t
* @annotation ["scriptable","design","widget"]
* 滚动视图。
*
* scroll\_view\_t是[widget\_t](widget_t.md)的子类控件widget\_t的函数均适用于scroll\_view\_t控件。
*
* 在xml中使用"scroll\_view"标签创建滚动视图控件。如:
*
* ```xml
* <list_view x="0" y="30" w="100%" h="-80" item_height="60">
* <scroll_view name="view" x="0" y="0" w="100%" h="100%">
* <list_item style="odd" children_layout="default(rows=1,cols=0)">
* <image draw_type="icon" w="30" image="earth"/>
* <label w="-30" text="1.Hello AWTK !">
* <switch x="r:10" y="m" w="60" h="20"/>
* </label>
* </list_item>
* ...
* </scroll_view>
* </list_view>
* ```
*
* > 滚动视图一般作为列表视图的子控件使用。
*
* > 更多用法请参考:[list\_view\_m.xml](
*https://github.com/zlgopen/awtk/blob/master/design/default/ui/list_view_m.xml)
*
* 在c代码中使用函数scroll\_view\_create创建列表视图控件。如
*
* ```c
* widget_t* scroll_view = scroll_view_create(win, 0, 0, 0, 0);
* ```
*
* 可用通过style来设置控件的显示风格如背景颜色和边框颜色等(一般情况不需要)。
*
*/
typedef struct _scroll_view_t {
widget_t widget;
/**
* @property {wh_t} virtual_w
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 虚拟宽度。
*/
wh_t virtual_w;
/**
* @property {wh_t} virtual_h
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 虚拟高度。
*/
wh_t virtual_h;
/**
* @property {int32_t} xoffset
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* x偏移量。
*/
int32_t xoffset;
/**
* @property {int32_t} yoffset
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* y偏移量。
*/
int32_t yoffset;
/**
* @property {float_t} xspeed_scale
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* x偏移速度比例。
*/
float_t xspeed_scale;
/**
* @property {float_t} yspeed_scale
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* y偏移速度比例。
*/
float_t yspeed_scale;
/**
* @property {bool_t} xslidable
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 是否允许x方向滑动。
*/
bool_t xslidable;
/**
* @property {bool_t} yslidable
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 是否允许y方向滑动。
*/
bool_t yslidable;
/**
* @property {bool_t} snap_to_page
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 滚动时offset是否按页面对齐。
*/
bool_t snap_to_page;
/**
* @property {bool_t} move_to_page
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 是否每次翻一页(当 move_to_page 为ture 的时候才有效果,主要用于区分一次翻一页还是一次翻多页)。
*/
bool_t move_to_page;
/**
* @property {bool_t} recursive
* @annotation ["set_prop","get_prop","readable","persitent","design","scriptable"]
* 是否递归查找全部子控件。
*/
bool_t recursive;
/*private*/
point_t down;
bool_t pressed;
bool_t dragged;
int32_t xoffset_end;
int32_t yoffset_end;
int32_t xoffset_save;
int32_t yoffset_save;
int32_t curr_page;
uint32_t max_page;
velocity_t velocity;
widget_animator_t* wa;
scroll_view_fix_end_offset_t fix_end_offset;
widget_on_layout_children_t on_layout_children;
widget_on_paint_children_t on_paint_children;
widget_on_add_child_t on_add_child;
widget_find_target_t find_target;
scroll_view_on_scroll_t on_scroll;
scroll_view_on_scroll_to_t on_scroll_to;
bool_t first_move_after_down;
} scroll_view_t;
/**
* @event {event_t} EVT_SCROLL_START
* 开始滚动事件。
*/
/**
* @event {event_t} EVT_SCROLL_END
* 结束滚动事件。
*/
/**
* @event {event_t} EVT_SCROLL
* 滚动事件。
*/
/**
* @event {event_t} EVT_PAGE_CHANGED
* 页面改变事件。
*/
/**
* @event {event_t} EVT_PAGE_CHANGING
* 页面正在改变。
*/
/**
* @method scroll_view_create
* 创建scroll_view对象
* @annotation ["constructor", "scriptable"]
* @param {widget_t*} parent 父控件
* @param {xy_t} x x坐标
* @param {xy_t} y y坐标
* @param {wh_t} w 宽度
* @param {wh_t} h 高度
*
* @return {widget_t*} 对象。
*/
widget_t* scroll_view_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h);
/**
* @method scroll_view_cast
* 转换为scroll_view对象(供脚本语言使用)。
* @annotation ["cast", "scriptable"]
* @param {widget_t*} widget scroll_view对象。
*
* @return {widget_t*} scroll_view对象。
*/
widget_t* scroll_view_cast(widget_t* widget);
/**
* @method scroll_view_set_virtual_w
* 设置虚拟宽度。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {wh_t} w 虚拟宽度。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_set_virtual_w(widget_t* widget, wh_t w);
/**
* @method scroll_view_set_virtual_h
* 设置虚拟高度。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {wh_t} h 虚拟高度。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_set_virtual_h(widget_t* widget, wh_t h);
/**
* @method scroll_view_set_xslidable
* 设置是否允许x方向滑动。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {bool_t} xslidable 是否允许滑动。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_set_xslidable(widget_t* widget, bool_t xslidable);
/**
* @method scroll_view_set_yslidable
* 设置是否允许y方向滑动。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {bool_t} yslidable 是否允许滑动。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_set_yslidable(widget_t* widget, bool_t yslidable);
/**
* @method scroll_view_set_snap_to_page
* 设置滚动时offset是否按页面对齐。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {bool_t} snap_to_page 是否按页面对齐。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_set_snap_to_page(widget_t* widget, bool_t snap_to_page);
/**
* @method scroll_view_set_move_to_page
* 设置滚动时是否每次翻一页
* 备注:当 snap_to_page 为ture 的时候才有效果,主要用于区分一次翻一页还是一次翻多页。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {bool_t} move_to_page 是否每次翻一页。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_set_move_to_page(widget_t* widget, bool_t move_to_page);
/**
* @method scroll_view_set_recursive
* 设置是否递归查找全部子控件。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {bool_t} recursive 是否递归查找全部子控件。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_set_recursive(widget_t* widget, bool_t recursive);
/**
* @method scroll_view_set_recursive_only
* 设置是否递归查找全部子控件。(不触发repaint和relayout)。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {bool_t} recursive 是否递归查找全部子控件。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_set_recursive_only(widget_t* widget, bool_t recursive);
/**
* @method scroll_view_set_offset
* 设置偏移量。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {int32_t} xoffset x偏移量。
* @param {int32_t} yoffset y偏移量。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_set_offset(widget_t* widget, int32_t xoffset, int32_t yoffset);
/**
* @method scroll_view_set_speed_scale
* 设置偏移速度比例。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {float_t} xspeed_scale x偏移速度比例。
* @param {float_t} yspeed_scale y偏移速度比例。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_set_speed_scale(widget_t* widget, float_t xspeed_scale, float_t yspeed_scale);
/**
* @method scroll_view_scroll_to
* 滚动到指定的偏移量。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {int32_t} xoffset_end x偏移量。
* @param {int32_t} yoffset_end y偏移量。
* @param {int32_t} duration 时间。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_scroll_to(widget_t* widget, int32_t xoffset_end, int32_t yoffset_end,
int32_t duration);
/**
* @method scroll_view_scroll_delta_to
* 滚动到指定的偏移量。
* @annotation ["scriptable"]
* @param {widget_t*} widget 控件对象。
* @param {int32_t} xoffset_delta x偏移量。
* @param {int32_t} yoffset_delta y偏移量。
* @param {int32_t} duration 时间。
*
* @return {ret_t} 返回RET_OK表示成功否则表示失败。
*/
ret_t scroll_view_scroll_delta_to(widget_t* widget, int32_t xoffset_delta, int32_t yoffset_delta,
int32_t duration);
#define SCROLL_VIEW(widget) ((scroll_view_t*)(scroll_view_cast(WIDGET(widget))))
#define SCROLL_VIEW_RECURSIVE "recursive"
#define SCROLL_VIEW_SNAP_TO_PAGE "snap_to_page"
#define SCROLL_VIEW_MOVE_TO_PAGE "move_to_page"
#define SCROLL_VIEW_X_SPEED_SCALE "xspeed_scale"
#define SCROLL_VIEW_Y_SPEED_SCALE "yspeed_scale"
/*public for subclass and runtime type check*/
TK_EXTERN_VTABLE(scroll_view);
END_C_DECLS
#endif /*TK_SCROLL_VIEW_H*/