CARPLAY版本整理
This commit is contained in:
@ -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;
|
||||
}
|
@ -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*/
|
@ -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;
|
||||
}
|
@ -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*/
|
@ -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;
|
||||
}
|
@ -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 属性为 TRUE,scroll_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*/
|
@ -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;
|
||||
}
|
@ -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*/
|
@ -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;
|
||||
}
|
@ -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*/
|
@ -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;
|
||||
}
|
@ -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*/
|
Reference in New Issue
Block a user