Add support for selection drags. Not yet implemented move drags.

This commit is contained in:
Michael Drake 2013-06-18 14:58:43 +01:00
parent 55dd035686
commit d064e9ac78
1 changed files with 208 additions and 9 deletions

View File

@ -45,6 +45,12 @@ enum treeview_node_type {
TREE_NODE_ENTRY
};
enum treeview_node_section {
TV_NODE_SECTION_TOGGLE,
TV_NODE_SECTION_ON_NODE,
TV_NODE_SECTION_NONE
};
struct treeview_text {
const char *data;
uint32_t len;
@ -87,6 +93,27 @@ struct treeview_node_entry {
struct treeview_field fields[];
};
struct treeview_pos {
int x;
int y;
int node_y;
int node_h;
};
struct treeview_drag {
enum {
TV_DRAG_NONE,
TV_DRAG_SELECTION,
TV_DRAG_MOVE,
TV_DRAG_TEXTAREA
} type;
struct treeview_node *start_node;
bool selected; /* Whether start node is selected */
enum treeview_node_section section;
struct treeview_pos start;
struct treeview_pos prev;
};
struct treeview {
uint32_t view_height;
uint32_t view_width;
@ -97,6 +124,8 @@ struct treeview {
int n_fields; /* fields[n_fields] is folder, lower are entry fields */
int field_width;
struct treeview_drag drag;
const struct treeview_callback_table *callbacks;
const struct core_window_callback_table *cw_t; /**< Core window callback table */
struct core_window *cw_h; /**< Core window handle */
@ -525,6 +554,17 @@ nserror treeview_create(struct treeview **tree,
(*tree)->callbacks = callbacks;
(*tree)->n_fields = n_fields - 1;
(*tree)->drag.type = TV_DRAG_NONE;
(*tree)->drag.start_node = NULL;
(*tree)->drag.start.x = 0;
(*tree)->drag.start.y = 0;
(*tree)->drag.start.node_y = 0;
(*tree)->drag.start.node_h = 0;
(*tree)->drag.prev.x = 0;
(*tree)->drag.prev.y = 0;
(*tree)->drag.prev.node_y = 0;
(*tree)->drag.prev.node_h = 0;
(*tree)->cw_t = cw_t;
(*tree)->cw_h = cw;
@ -759,11 +799,21 @@ void treeview_redraw(struct treeview *tree, int x, int y, struct rect *clip,
plot_font_style_t *text_style;
plot_font_style_t *infotext_style;
int height;
int sel_min, sel_max;
bool invert_selection;
assert(tree != NULL);
assert(tree->root != NULL);
assert(tree->root->flags & TREE_NODE_EXPANDED);
if (tree->drag.start.y > tree->drag.prev.y) {
sel_min = tree->drag.prev.y;
sel_max = tree->drag.start.y;
} else {
sel_min = tree->drag.start.y;
sel_max = tree->drag.prev.y;
}
/* Start knockout rendering if it's available for this plotter */
if (ctx->plot->option_knockout)
knockout_plot_start(ctx, &new_ctx);
@ -826,7 +876,16 @@ void treeview_redraw(struct treeview *tree, int x, int y, struct rect *clip,
}
style = (count & 0x1) ? &plot_style_odd : &plot_style_even;
if (node->flags & TREE_NODE_SELECTED) {
if (tree->drag.type == TV_DRAG_SELECTION &&
(render_y + height > sel_min &&
render_y < sel_max)) {
invert_selection = true;
} else {
invert_selection = false;
}
if ((node->flags & TREE_NODE_SELECTED && !invert_selection) ||
(!(node->flags & TREE_NODE_SELECTED) &&
invert_selection)) {
bg_style = &style->sbg;
text_style = &style->stext;
infotext_style = &style->sitext;
@ -950,7 +1009,8 @@ struct treeview_selection_walk_data {
enum {
TREEVIEW_WALK_HAS_SELECTION,
TREEVIEW_WALK_CLEAR_SELECTION,
TREEVIEW_WALK_SELECT_ALL
TREEVIEW_WALK_SELECT_ALL,
TREEVIEW_WALK_COMMIT_SELECT_DRAG
} purpose;
union {
bool has_selection;
@ -958,6 +1018,10 @@ struct treeview_selection_walk_data {
bool required;
struct rect *rect;
} redraw;
struct {
int sel_min;
int sel_max;
} drag;
} data;
int current_y;
};
@ -993,6 +1057,14 @@ static bool treeview_node_selection_walk_cb(struct treeview_node *node,
changed = true;
}
break;
case TREEVIEW_WALK_COMMIT_SELECT_DRAG:
if (sw->current_y > sw->data.drag.sel_min &&
sw->current_y - height <
sw->data.drag.sel_max) {
node->flags ^= TREE_NODE_SELECTED;
}
return false; /* Don't stop walk */
}
if (changed) {
@ -1062,6 +1134,25 @@ bool treeview_select_all(struct treeview *tree, struct rect *rect)
return sw.data.redraw.required;
}
static void treeview_commit_selection_drag(struct treeview *tree)
{
struct treeview_selection_walk_data sw;
sw.purpose = TREEVIEW_WALK_COMMIT_SELECT_DRAG;
sw.current_y = 0;
if (tree->drag.start.y > tree->drag.prev.y) {
sw.data.drag.sel_min = tree->drag.prev.y;
sw.data.drag.sel_max = tree->drag.start.y;
} else {
sw.data.drag.sel_min = tree->drag.start.y;
sw.data.drag.sel_max = tree->drag.prev.y;
}
treeview_walk_internal(tree->root, false,
treeview_node_selection_walk_cb, &sw);
}
struct treeview_mouse_action {
struct treeview *tree;
browser_mouse_state mouse;
@ -1080,10 +1171,7 @@ static bool treeview_node_mouse_action_cb(struct treeview_node *node, void *ctx)
TV_NODE_ACTION_NONE = 0,
TV_NODE_ACTION_SELECTION = (1 << 0)
} action = TV_NODE_ACTION_NONE;
enum {
TV_NODE_SECTION_TOGGLE,
TV_NODE_SECTION_NODE
} section = TV_NODE_SECTION_NODE;
enum treeview_node_section section = TV_NODE_SECTION_NONE;
nserror err;
r.x0 = 0;
@ -1099,9 +1187,112 @@ static bool treeview_node_mouse_action_cb(struct treeview_node *node, void *ctx)
}
/* Find where the mouse is */
if (ma->x >= node->inset - 1 &&
ma->x < node->inset + tree_g.step_width) {
section = TV_NODE_SECTION_TOGGLE;
if (ma->y <= ma->current_y + tree_g.line_height) {
if (ma->x >= node->inset - 1 &&
ma->x < node->inset + tree_g.step_width) {
/* Over expansion toggle */
section = TV_NODE_SECTION_TOGGLE;
} else if (ma->x >= node->inset + tree_g.step_width &&
ma->x < node->inset + tree_g.step_width +
tree_g.icon_step + node->text.value.width) {
/* On node */
section = TV_NODE_SECTION_ON_NODE;
}
} else if (node->type == TREE_NODE_ENTRY &&
height > tree_g.line_height) {
/* Expanded entries */
int x = node->inset + tree_g.step_width + tree_g.icon_step;
int y = ma->current_y + tree_g.line_height;
int i;
struct treeview_node_entry *entry =
(struct treeview_node_entry *)node;
for (i = 0; i < ma->tree->n_fields - 1; i++) {
struct treeview_field *ef = &(ma->tree->fields[i + 1]);
if (ma->y > y + tree_g.line_height) {
y += tree_g.line_height;
continue;
}
if (ef->flags & TREE_FLAG_SHOW_NAME) {
int max_width = ma->tree->field_width;
if (ma->x >= x + max_width - ef->value.width -
tree_g.step_width &&
ma->x < x + max_width -
tree_g.step_width) {
/* On a field name */
section = TV_NODE_SECTION_ON_NODE;
} else if (ma->x >= x + max_width &&
ma->x < x + max_width +
entry->fields[i].value.width) {
/* On a field value */
section = TV_NODE_SECTION_ON_NODE;
}
} else {
if (ma->x >= x && ma->x < x +
entry->fields[i].value.width) {
/* On a field value */
section = TV_NODE_SECTION_ON_NODE;
}
}
break;
}
}
/* Record what position / section a drag started on */
if (ma->mouse & (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2) &&
ma->tree->drag.type == TV_DRAG_NONE) {
ma->tree->drag.selected = node->flags & TREE_NODE_SELECTED;
ma->tree->drag.start_node = node;
ma->tree->drag.section = section;
ma->tree->drag.start.x = ma->x;
ma->tree->drag.start.y = ma->y;
ma->tree->drag.start.node_y = ma->current_y;
ma->tree->drag.start.node_h = height;
ma->tree->drag.prev.x = ma->x;
ma->tree->drag.prev.y = ma->y;
ma->tree->drag.prev.node_y = ma->current_y;
ma->tree->drag.prev.node_h = height;
}
/* Handle drag start */
if (ma->tree->drag.type == TV_DRAG_NONE) {
if (ma->mouse & BROWSER_MOUSE_DRAG_1 &&
ma->tree->drag.selected == false &&
ma->tree->drag.section ==
TV_NODE_SECTION_NONE) {
ma->tree->drag.type = TV_DRAG_SELECTION;
} else if (ma->mouse & BROWSER_MOUSE_DRAG_2) {
ma->tree->drag.type = TV_DRAG_SELECTION;
}
if (ma->tree->drag.start_node != NULL &&
ma->tree->drag.type == TV_DRAG_SELECTION) {
ma->tree->drag.start_node->flags ^= TREE_NODE_SELECTED;
}
}
/* Handle selection drags */
if (ma->tree->drag.type == TV_DRAG_SELECTION) {
int curr_y1 = ma->current_y + height;
int prev_y1 = ma->tree->drag.prev.node_y +
ma->tree->drag.prev.node_h;
r.y0 = (ma->current_y < ma->tree->drag.prev.node_y) ?
ma->current_y : ma->tree->drag.prev.node_y;
r.y1 = (curr_y1 > prev_y1) ? curr_y1 : prev_y1;
redraw = true;
ma->tree->drag.prev.x = ma->x;
ma->tree->drag.prev.y = ma->y;
ma->tree->drag.prev.node_y = ma->current_y;
ma->tree->drag.prev.node_h = height;
}
click = ma->mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2);
@ -1181,6 +1372,14 @@ void treeview_mouse_action(struct treeview *tree,
{
struct treeview_mouse_action ma;
if (mouse == BROWSER_MOUSE_HOVER &&
tree->drag.type == TV_DRAG_SELECTION) {
treeview_commit_selection_drag(tree);
tree->drag.type = TV_DRAG_NONE;
tree->drag.start_node = NULL;
return;
}
ma.tree = tree;
ma.mouse = mouse;
ma.x = x;