diff options
author | Michael Drake <tlsa@netsurf-browser.org> | 2009-08-14 10:37:33 +0000 |
---|---|---|
committer | Michael Drake <tlsa@netsurf-browser.org> | 2009-08-14 10:37:33 +0000 |
commit | 13afa0ff4c591e7800dc4c11a81d04c335418c05 (patch) | |
tree | 017f315584621f418a8bab5627aee8f8364d377b /render | |
parent | 3549846a9aec635bea82a919c5c1e38c7163731a (diff) | |
download | netsurf-13afa0ff4c591e7800dc4c11a81d04c335418c05.tar.gz netsurf-13afa0ff4c591e7800dc4c11a81d04c335418c05.tar.bz2 |
Merge Paul Blokus' selectscroll branch. Adds core select menu widget for forms and core scrollbar widget.
svn path=/trunk/netsurf/; revision=9289
Diffstat (limited to 'render')
-rw-r--r-- | render/box.c | 164 | ||||
-rw-r--r-- | render/box.h | 13 | ||||
-rw-r--r-- | render/form.c | 492 | ||||
-rw-r--r-- | render/form.h | 34 | ||||
-rw-r--r-- | render/html_redraw.c | 454 | ||||
-rw-r--r-- | render/layout.c | 7 | ||||
-rw-r--r-- | render/layout.h | 2 |
7 files changed, 783 insertions, 383 deletions
diff --git a/render/box.c b/render/box.c index b1ebfcbad..0c0985dab 100644 --- a/render/box.c +++ b/render/box.c @@ -29,11 +29,13 @@ #include "content/content.h" #include "css/css.h" #include "css/dump.h" +#include "desktop/scroll.h" #include "desktop/options.h" #include "render/box.h" #include "render/form.h" #include "utils/log.h" #include "utils/talloc.h" +#include "utils/utils.h" static bool box_contains_point(struct box *box, int x, int y, bool *physically); @@ -80,7 +82,7 @@ struct box * box_create(css_computed_style *style, box->descendant_x1 = box->descendant_y1 = 0; for (i = 0; i != 4; i++) box->margin[i] = box->padding[i] = box->border[i].width = 0; - box->scroll_x = box->scroll_y = 0; + box->scroll_x = box->scroll_y = NULL; box->min_width = 0; box->max_width = UNKNOWN_MAX_WIDTH; box->byte_offset = 0; @@ -226,6 +228,10 @@ void box_free_box(struct box *box) if (!box->clone) { if (box->gadget) form_free_control(box->gadget); + if (box->scroll_x != NULL) + scroll_destroy(box->scroll_x); + if (box->scroll_y != NULL) + scroll_destroy(box->scroll_y); } talloc_free(box); @@ -251,8 +257,8 @@ void box_coords(struct box *box, int *x, int *y) } while (!box->float_children); } else box = box->parent; - *x += box->x - box->scroll_x; - *y += box->y - box->scroll_y; + *x += box->x - scroll_get_offset(box->scroll_x); + *y += box->y - scroll_get_offset(box->scroll_y); } } @@ -328,8 +334,10 @@ struct box *box_at_point(struct box *box, const int x, const int y, /* consider floats second, since they will often overlap other boxes */ for (child = box->float_children; child; child = child->next_float) { if (box_contains_point(child, x - bx, y - by, &physically)) { - *box_x = bx + child->x - child->scroll_x; - *box_y = by + child->y - child->scroll_y; + *box_x = bx + child->x - + scroll_get_offset(child->scroll_x); + *box_y = by + child->y - + scroll_get_offset(child->scroll_y); if (physically) return child; @@ -345,8 +353,10 @@ non_float_children: if (box_is_float(child)) continue; if (box_contains_point(child, x - bx, y - by, &physically)) { - *box_x = bx + child->x - child->scroll_x; - *box_y = by + child->y - child->scroll_y; + *box_x = bx + child->x - + scroll_get_offset(child->scroll_x); + *box_y = by + child->y - + scroll_get_offset(child->scroll_y); if (physically) return child; @@ -370,16 +380,18 @@ siblings: /* siblings and siblings of ancestors */ while (box) { if (box_is_float(box)) { - bx -= box->x - box->scroll_x; - by -= box->y - box->scroll_y; + bx -= box->x - scroll_get_offset(box->scroll_x); + by -= box->y - scroll_get_offset(box->scroll_y); for (sibling = box->next_float; sibling; sibling = sibling->next_float) { if (box_contains_point(sibling, x - bx, y - by, &physically)) { *box_x = bx + sibling->x - - sibling->scroll_x; + scroll_get_offset( + sibling->scroll_x); *box_y = by + sibling->y - - sibling->scroll_y; + scroll_get_offset( + sibling->scroll_y); if (physically) return sibling; @@ -398,8 +410,8 @@ siblings: goto non_float_children; } else { - bx -= box->x - box->scroll_x; - by -= box->y - box->scroll_y; + bx -= box->x - scroll_get_offset(box->scroll_x); + by -= box->y - scroll_get_offset(box->scroll_y); for (sibling = box->next; sibling; sibling = sibling->next) { if (box_is_float(sibling)) @@ -407,9 +419,11 @@ siblings: if (box_contains_point(sibling, x - bx, y - by, &physically)) { *box_x = bx + sibling->x - - sibling->scroll_x; + scroll_get_offset( + sibling->scroll_x); *box_y = by + sibling->y - - sibling->scroll_y; + scroll_get_offset( + sibling->scroll_y); if (physically) return sibling; @@ -978,3 +992,123 @@ void box_duplicate_update(struct box *box, box->next_float = box_dict_element->new; } } + +/** + * Applies the given scroll setup to a box. This includes scroll + * creation/deletion as well as scroll dimension updates. + * + * \param box the box to handle the scrolls for + * \param x X coordinate of the box + * \param y Y coordinate of the box + * \param bottom whether the horizontal scrollbar should be present + * \param right whether the vertical scrollbar should be present + * \return true on success false otherwise + */ +bool box_handle_scrollbars(struct box *box, int x, int y, bool bottom, + bool right) +{ + struct browser_scroll_data *data; + int padding_width, padding_height; + + padding_width = box->width + box->padding[RIGHT] + box->padding[LEFT]; + padding_height = box->height + box->padding[TOP] + box->padding[BOTTOM]; + + if (!bottom && box->scroll_x != NULL) { + data = scroll_get_data(box->scroll_x); + scroll_destroy(box->scroll_x); + free(data); + box->scroll_x = NULL; + } + + if (!right && box->scroll_y != NULL) { + data = scroll_get_data(box->scroll_x); + scroll_destroy(box->scroll_y); + free(data); + box->scroll_y = NULL; + } + + if (!bottom && !right) + return true; + + if (right) { + if (box->scroll_y == NULL) { + data = malloc(sizeof(struct browser_scroll_data)); + if (data == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return false; + } + data->bw = current_redraw_browser; + data->box = box; + if (!scroll_create(false, + padding_height, + box->descendant_y1 - box->descendant_y0, + box->height, + data, + browser_scroll_callback, + &(box->scroll_y))) + return false; + } else + scroll_set_length_and_visible(box->scroll_y, + padding_height, box->height); + } + if (bottom) { + if (box->scroll_x == NULL) { + data = malloc(sizeof(struct browser_scroll_data)); + if (data == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return false; + } + data->bw = current_redraw_browser; + data->box = box; + if (!scroll_create(true, + padding_width - + (right ? SCROLLBAR_WIDTH : 0), + box->descendant_x1 - box->descendant_x0, + box->width, + data, + browser_scroll_callback, + &box->scroll_x)) + return false; + } else + scroll_set_length_and_visible(box->scroll_x, + padding_width - + (right ? SCROLLBAR_WIDTH : 0), + box->width); + } + + if (right && bottom) + scroll_make_pair(box->scroll_x, box->scroll_y); + + return true; +} + +/** + * Determine if a box has a vertical scrollbar. + * + * \param box scrolling box + * \return the box has a vertical scrollbar + */ + +bool box_vscrollbar_present(const struct box * const box) +{ + return box->descendant_y0 < -box->border[TOP].width || + box->padding[TOP] + box->height + box->padding[BOTTOM] + + box->border[BOTTOM].width < box->descendant_y1; +} + + +/** + * Determine if a box has a horizontal scrollbar. + * + * \param box scrolling box + * \return the box has a horizontal scrollbar + */ + +bool box_hscrollbar_present(const struct box * const box) +{ + return box->descendant_x0 < -box->border[LEFT].width || + box->padding[LEFT] + box->width + box->padding[RIGHT] + + box->border[RIGHT].width < box->descendant_x1; +} diff --git a/render/box.h b/render/box.h index 701329108..39a6c2521 100644 --- a/render/box.h +++ b/render/box.h @@ -167,8 +167,8 @@ struct box { int padding[4]; /**< Padding: TOP, RIGHT, BOTTOM, LEFT. */ struct box_border border[4]; /**< Border: TOP, RIGHT, BOTTOM, LEFT. */ - int scroll_x; /**< Horizontal scroll of descendants. */ - int scroll_y; /**< Vertical scroll of descendants. */ + struct scroll *scroll_x; /**< Horizontal scroll. */ + struct scroll *scroll_y; /**< Vertical scroll. */ /** Width of box taking all line breaks (including margins etc). Must * be non-negative. */ @@ -314,15 +314,10 @@ bool box_visible(struct box *box); void box_dump(FILE *stream, struct box *box, unsigned int depth); bool box_extract_link(const char *rel, const char *base, char **result); +bool box_handle_scrollbars(struct box *box, int x, int y, bool bottom, + bool right); bool box_vscrollbar_present(const struct box *box); bool box_hscrollbar_present(const struct box *box); -void box_scrollbar_dimensions(const struct box *box, - int padding_width, int padding_height, int w, - bool *vscroll, bool *hscroll, - int *well_height, - int *bar_top, int *bar_height, - int *well_width, - int *bar_left, int *bar_width); bool xml_to_box(xmlNode *n, struct content *c); diff --git a/render/form.c b/render/form.c index 01e5e1d2f..968397a21 100644 --- a/render/form.c +++ b/render/form.c @@ -2,6 +2,7 @@ * Copyright 2004 James Bursa <bursa@users.sourceforge.net> * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> * Copyright 2005-9 John-Mark Bell <jmb@netsurf-browser.org> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -29,18 +30,60 @@ #include <stdbool.h> #include <stdio.h> #include <string.h> +#include "css/css.h" +#include "css/utils.h" +#include "desktop/gui.h" +#include "desktop/knockout.h" +#include "desktop/plot_style.h" +#include "desktop/plotters.h" +#include "desktop/scroll.h" #include "render/box.h" +#include "render/font.h" #include "render/form.h" +#include "render/layout.h" #include "utils/log.h" +#include "utils/messages.h" #include "utils/url.h" #include "utils/utf8.h" #include "utils/utils.h" +#define MAX_SELECT_HEIGHT 210 +#define SELECT_LINE_SPACING 0.2 +#define SELECT_BORDER_WIDTH 1 +#define SELECT_SELECTED_COLOUR 0xDB9370 + +struct form_select_menu { + int line_height; + int width, height; + struct scroll *scroll; + int f_size; + bool scroll_capture; + select_menu_redraw_callback callback; + void *client_data; + struct browser_window *bw; +}; + +static plot_style_t plot_style_fill_selected = { + .fill_type = PLOT_OP_TYPE_SOLID, + .fill_colour = SELECT_SELECTED_COLOUR, +}; + +static plot_font_style_t plot_fstyle_entry = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .weight = 400, + .flags = FONTF_NONE, + .background = 0xffffff, + .foreground = 0x000000, +}; static char *form_textarea_value(struct form_control *textarea); static char *form_acceptable_charset(struct form *form); static char *form_encode_item(const char *item, const char *charset, const char *fallback); +static void form_select_menu_clicked(struct form_control *control, + int x, int y); +static void form_select_menu_scroll_callback(void *client_data, + struct scroll_msg_data *scroll_data); /** * Create a struct form. @@ -199,6 +242,8 @@ void form_free_control(struct form_control *control) free(option->value); free(option); } + if (control->data.select.menu != NULL) + form_free_select_menu(control); } free(control); @@ -812,3 +857,450 @@ char *form_encode_item(const char *item, const char *charset, return ret; } +/** + * Open a select menu for a select form control, creating it if necessary. + * + * \param client_data data passed to the redraw callback + * \param control the select form control for which the menu is being + * opened + * \param callback redraw callback for the select menu + * \param bw the browser window in which the select menu is being + * opened + * \return false on memory exhaustion, true otherwise + */ +bool form_open_select_menu(void *client_data, + struct form_control *control, + select_menu_redraw_callback callback, + struct browser_window *bw) +{ + int i, line_height_with_spacing, scroll; + struct form_option *option; + struct box *box; + plot_font_style_t fstyle; + int total_height; + struct form_select_menu *menu; + + + /* if the menu is opened for the first time */ + if (control->data.select.menu == NULL) { + + menu = calloc(1, sizeof (struct form_select_menu)); + if (menu == NULL) { + warn_user("NoMemory", 0); + return false; + } + + control->data.select.menu = menu; + + box = control->box; + + menu->width = box->width + + box->border[RIGHT].width + + box->border[LEFT].width + + box->padding[RIGHT] + box->padding[LEFT]; + + font_plot_style_from_css(control->box->style, + &fstyle); + menu->f_size = fstyle.size; + + menu->line_height = + FIXTOINT(FDIVI((FMUL(FLTTOFIX(1.2), + FMULI(nscss_screen_dpi, + (fstyle.size / FONT_SIZE_SCALE)))), 72)); + + line_height_with_spacing = menu->line_height + + menu->line_height * + SELECT_LINE_SPACING; + + total_height = control->data.select.num_items * + line_height_with_spacing; + menu->height = total_height; + + scroll = 0; + if (menu->height > MAX_SELECT_HEIGHT) { + + menu->height = MAX_SELECT_HEIGHT; + + if (control->data.select.num_selected > 0) { + i = 0; + option = control->data.select.items; + while (!option->selected) { + option = option->next; + i++; + } + + if ((i + 1) * line_height_with_spacing > + MAX_SELECT_HEIGHT) + scroll = (i + 1) * + line_height_with_spacing + - MAX_SELECT_HEIGHT; + } + } + menu->client_data = client_data; + menu->callback = callback; + if (!scroll_create(false, + menu->height, + total_height, + menu->height, + control, + form_select_menu_scroll_callback, + &(menu->scroll))) { + free(menu); + return false; + } + menu->bw = bw; + } + else menu = control->data.select.menu; + + menu->callback(client_data, 0, 0, menu->width, menu->height); + + return true; +} + + +/** + * Destroy a select menu and free allocated memory. + * + * \param control the select form control owning the select menu being + * destroyed + */ +void form_free_select_menu(struct form_control *control) +{ + if (control->data.select.menu->scroll != NULL) + scroll_destroy(control->data.select.menu->scroll); + free(control->data.select.menu); + control->data.select.menu = NULL; +} + +/** + * Redraw an opened select menu. + * + * \param control the select menu being redrawn + * \param x the X coordinate to draw the menu at + * \param x the Y coordinate to draw the menu at + * \param scale current redraw scale + * \param clip_x0 minimum x of clipping rectangle + * \param clip_y0 minimum y of clipping rectangle + * \param clip_x1 maximum x of clipping rectangle + * \param clip_y1 maximum y of clipping rectangle + * \return true on success, false otherwise + */ +bool form_redraw_select_menu(struct form_control *control, int x, int y, + float scale, int clip_x0, int clip_y0, int clip_x1, int clip_y1) +{ + struct box *box; + struct form_select_menu *menu = control->data.select.menu; + struct form_option *option; + int line_height, line_height_with_spacing; + int width, height; + int x0, y0, x1, scrollbar_x, y1, y2, y3; + int item_y; + int text_pos_offset, text_x; + int scrollbar_width = SCROLLBAR_WIDTH; + int i; + int scroll; + int x_cp, y_cp; + + box = control->box; + + x_cp = x; + y_cp = y; + width = menu->width; + height = menu->height; + line_height = menu->line_height; + + line_height_with_spacing = line_height + + line_height * SELECT_LINE_SPACING; + scroll = scroll_get_offset(menu->scroll); + + if (scale != 1.0) { + x *= scale; + y *= scale; + width *= scale; + height *= scale; + scrollbar_width *= scale; + + i = scroll / line_height_with_spacing; + scroll -= i * line_height_with_spacing; + line_height *= scale; + line_height_with_spacing *= scale; + scroll *= scale; + scroll += i * line_height_with_spacing; + } + + + x0 = x; + y0 = y; + x1 = x + width - 1; + y1 = y + height - 1; + scrollbar_x = x1 - scrollbar_width; + + if (!plot.clip(x0, y0, x1 + 1, y1 + 1)) + return false; + if (!plot.rectangle(x0, y0, x1, y1 ,plot_style_stroke_darkwbasec)) + return false; + + + x0 = x0 + SELECT_BORDER_WIDTH; + y0 = y0 + SELECT_BORDER_WIDTH; + x1 = x1 - SELECT_BORDER_WIDTH; + y1 = y1 - SELECT_BORDER_WIDTH; + height = height - 2 * SELECT_BORDER_WIDTH; + + if (!plot.clip(x0, y0, x1 + 1, y1 + 1)) + return false; + if (!plot.rectangle(x0, y0, x1 + 1, y1 + 1, + plot_style_fill_lightwbasec)) + return false; + option = control->data.select.items; + item_y = line_height_with_spacing; + + while (item_y < scroll) { + option = option->next; + item_y += line_height_with_spacing; + } + item_y -= line_height_with_spacing; + text_pos_offset = y - scroll + + (int) (line_height * (0.75 + SELECT_LINE_SPACING)); + text_x = x + (box->border[LEFT].width + box->padding[LEFT]) * scale; + + plot_fstyle_entry.size = menu->f_size; + + while (option && item_y - scroll < height) { + + if (option->selected) { + y2 = y + item_y - scroll; + y3 = y + item_y + line_height_with_spacing - scroll; + if (!plot.rectangle(x0, (y0 > y2 ? y0 : y2), + scrollbar_x + 1, + (y3 < y1 + 1 ? y3 : y1 + 1), + &plot_style_fill_selected)) + return false; + } + + y2 = text_pos_offset + item_y; + if (!plot.text(text_x, y2, option->text, + strlen(option->text), &plot_fstyle_entry)) + return false; + + item_y += line_height_with_spacing; + option = option->next; + } + + if (!scroll_redraw(menu->scroll, + x_cp + menu->width - SCROLLBAR_WIDTH, + y_cp, + clip_x0, clip_y0, clip_x1, clip_y1, scale)) + return false; + + return true; +} + +/** + * Check whether a clipping rectangle is completely contained in the + * select menu. + * + * \param control the select menu to check the clipping rectangle for + * \param scale the current browser window scale + * \param clip_x0 minimum x of clipping rectangle + * \param clip_y0 minimum y of clipping rectangle + * \param clip_x1 maximum x of clipping rectangle + * \param clip_y1 maximum y of clipping rectangle + * \return true if inside false otherwise + */ +bool form_clip_inside_select_menu(struct form_control *control, float scale, + int clip_x0, int clip_y0, int clip_x1, int clip_y1) +{ + struct form_select_menu *menu = control->data.select.menu; + int width, height; + + + width = menu->width; + height = menu->height; + + if (scale != 1.0) { + width *= scale; + height *= scale; + } + + if (clip_x0 >= 0 && clip_x1 <= width && + clip_y0 >= 0 && clip_y1 <= height) + return true; + + return false; +} + +/** + * Handle a click on the area of the currently opened select menu. + * + * \param control the select menu which received the click + * \param x X coordinate of click + * \param y Y coordinate of click + */ +void form_select_menu_clicked(struct form_control *control, int x, int y) +{ + struct form_select_menu *menu = control->data.select.menu; + struct form_option *option; + int line_height, line_height_with_spacing; + int item_bottom_y; + int scroll, i; + + scroll = scroll_get_offset(menu->scroll); + + line_height = menu->line_height; + line_height_with_spacing = line_height + + line_height * SELECT_LINE_SPACING; + + option = control->data.select.items; + item_bottom_y = line_height_with_spacing; + i = 0; + while (option && item_bottom_y < scroll + y) { + item_bottom_y += line_height_with_spacing; + option = option->next; + i++; + } + + if (option != NULL) + browser_window_form_select(menu->bw, control, i); + + menu->callback(menu->client_data, 0, 0, menu->width, menu->height); +} + +/** + * Handle mouse action for the currently opened select menu. + * + * \param control the select menu which received the mouse action + * \param mouse current mouse state + * \param x X coordinate of click + * \param y Y coordinate of click + * \return text for the browser status bar or NULL if the menu has + * to be closed + */ +const char *form_select_mouse_action(struct form_control *control, + browser_mouse_state mouse, int x, int y) +{ + struct form_select_menu *menu = control->data.select.menu; + int x0, y0, x1, y1, scrollbar_x; + const char *status = NULL; + bool multiple = control->data.select.multiple; + + x0 = 0; + y0 = 0; + x1 = menu->width; + y1 = menu->height; + scrollbar_x = x1 - SCROLLBAR_WIDTH; + + if (menu->scroll_capture || + (x > scrollbar_x && x < x1 && y > y0 && y < y1)) { + /* The scroll is currently capturing all events or the mouse + * event is taking place on the scrollbar widget area + */ + x -= scrollbar_x; + return scroll_mouse_action(menu->scroll, + mouse, x, y); + } + + + if (x > x0 && x < scrollbar_x && y > y0 && y < y1) { + /* over option area */ + + if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2)) + /* button 1 or 2 click */ + form_select_menu_clicked(control, x, y); + + if (!(mouse & BROWSER_MOUSE_CLICK_1 && !multiple)) + /* anything but a button 1 click over a single select + menu */ + status = messages_get(control->data.select.multiple ? + "SelectMClick" : "SelectClick"); + + } else if (!(mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2))) + /* if not a button 1 or 2 click*/ + status = messages_get("SelectClose"); + + return status; +} + +/** + * Handle mouse drag end for the currently opened select menu. + * + * \param control the select menu which received the mouse drag end + * \param mouse current mouse state + * \param x X coordinate of drag end + * \param y Y coordinate of drag end + */ +void form_select_mouse_drag_end(struct form_control *control, + browser_mouse_state mouse, int x, int y) +{ + int x0, y0, x1, y1; + struct form_select_menu *menu = control->data.select.menu; + + if (menu->scroll_capture) { + x -= menu->width - SCROLLBAR_WIDTH; + scroll_mouse_drag_end(menu->scroll, + mouse, x, y); + return; + } + + x0 = 0; + y0 = 0; + x1 = menu->width; + y1 = menu->height; + + + if (x > x0 && x < x1 - SCROLLBAR_WIDTH && y > y0 && y < y1) + /* handle drag end above the option area like a regular click */ + form_select_menu_clicked(control, x, y); +} + +/** + * Callback for the select menus scroll + */ +void form_select_menu_scroll_callback(void *client_data, + struct scroll_msg_data *scroll_data) +{ + struct form_control *control = client_data; + struct form_select_menu *menu = control->data.select.menu; + + switch (scroll_data->msg) { + case SCROLL_MSG_REDRAW: + menu->callback(menu->client_data, + menu->width - + SCROLLBAR_WIDTH + scroll_data->x0, + scroll_data->y0, + scroll_data->x1 - scroll_data->x0, + scroll_data->y1 - scroll_data->y0); + break; + case SCROLL_MSG_MOVED: + menu->callback(menu->client_data, + 0, 0, + menu->width - SCROLLBAR_WIDTH, + menu->height); + break; + case SCROLL_MSG_SCROLL_START: + menu->scroll_capture = true; + gui_window_box_scroll_start(menu->bw->window, + scroll_data->x0, scroll_data->y0, + scroll_data->x1, scroll_data->y1); + break; + case SCROLL_MSG_SCROLL_FINISHED: + menu->scroll_capture = false; + break; + default: + break; + } +} + +/** + * Get the dimensions of a select menu. + * + * \param control the select menu to get the dimensions of + * \param width gets updated to menu width + * \param height gets updated to menu height + */ +void form_select_get_dimensions(struct form_control *control, + int *width, int *height) +{ + *width = control->data.select.menu->width; + *height = control->data.select.menu->height; +} diff --git a/render/form.h b/render/form.h index eaecf3411..a31c24975 100644 --- a/render/form.h +++ b/render/form.h @@ -1,6 +1,7 @@ /* * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> * Copyright 2003 James Bursa <bursa@users.sourceforge.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -25,11 +26,13 @@ #define _NETSURF_RENDER_FORM_H_ #include <stdbool.h> +#include "desktop/browser.h" #include "utils/config.h" struct box; struct form_control; struct form_option; +struct form_select_menu; /** Form submit method. */ typedef enum { @@ -105,6 +108,7 @@ struct form_control { int num_selected; /** Currently selected item, if num_selected == 1. */ struct form_option *current; + struct form_select_menu *menu; } select; } data; @@ -129,6 +133,19 @@ struct form_successful_control { struct form_successful_control *next; /**< Next in linked list. */ }; +/** + * Called by the select menu when it wants an area to be redrawn. The + * coordinates are menu origin relative. + * + * \param client_data data which was passed to form_open_select_menu + * \param x X coordinate of redraw rectangle + * \param y Y coordinate of redraw rectangle + * \param width width of redraw rectangle + * \param height height of redraw rectangle + */ +typedef void(*select_menu_redraw_callback)(void *client_data, + int x, int y, int width, int height); + struct form *form_new(void *node, const char *action, const char *target, form_method method, const char *charset, const char *doc_charset); @@ -145,4 +162,21 @@ char *form_url_encode(struct form *form, struct form_successful_control *control); void form_free_successful(struct form_successful_control *control); +bool form_open_select_menu(void *client_data, + struct form_control *control, + select_menu_redraw_callback redraw_callback, + struct browser_window *bw); +void form_free_select_menu(struct form_control *control); +bool form_redraw_select_menu(struct form_control *control, int x, int y, + float scale, + int clip_x0, int clip_y0, int clip_x1, int clip_y1); +bool form_clip_inside_select_menu(struct form_control *control, float scale, + int clip_x0, int clip_y0, int clip_x1, int clip_y1); +const char *form_select_mouse_action(struct form_control *control, + browser_mouse_state mouse, int x, int y); +void form_select_mouse_drag_end(struct form_control *control, + browser_mouse_state mouse, int x, int y); +void form_select_get_dimensions(struct form_control *control, + int *width, int *height); + #endif diff --git a/render/html_redraw.c b/render/html_redraw.c index 4a8d72bec..2f4fdf794 100644 --- a/render/html_redraw.c +++ b/render/html_redraw.c @@ -5,6 +5,7 @@ * Copyright 2005-2006 Adrian Lees <adrianl@users.sourceforge.net> * Copyright 2006 Rob Kendrick <rjek@netsurf-browser.org> * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -40,6 +41,7 @@ #include "desktop/textinput.h" #include "desktop/options.h" #include "desktop/print.h" +#include "desktop/scroll.h" #include "image/bitmap.h" #include "render/box.h" #include "render/font.h" @@ -89,23 +91,9 @@ static bool html_redraw_text_decoration_inline(struct box *box, int x, int y, float scale, colour colour, float ratio); static bool html_redraw_text_decoration_block(struct box *box, int x, int y, float scale, colour colour, float ratio); -static bool html_redraw_scrollbars(struct box *box, float scale, - int x, int y, int padding_width, int padding_height, - colour background_colour); bool html_redraw_debug = false; -/** Overflow scrollbar colours - * - * Overflow scrollbar colours can be set by front end code to try to match - * scrollbar colours used on the desktop. - * - * If a front end doesn't set scrollbar colours, these defaults are used. - */ -colour css_scrollbar_fg_colour = 0x00d9d9d9; /* light grey */ -colour css_scrollbar_bg_colour = 0x006b6b6b; /* mid grey */ -colour css_scrollbar_arrow_colour = 0x00444444; /* dark grey */ - /** * Draw a CONTENT_HTML using the current set of plotters (plot). * @@ -131,7 +119,8 @@ bool html_redraw(struct content *c, int x, int y, float scale, colour background_colour) { struct box *box; - bool result, want_knockout; + bool result = true, want_knockout; + bool select, select_only; plot_style_t pstyle_fill_bg = { .fill_type = PLOT_OP_TYPE_SOLID, .fill_colour = background_colour, @@ -144,18 +133,53 @@ bool html_redraw(struct content *c, int x, int y, if (want_knockout) knockout_plot_start(&plot); - /* clear to background colour */ - result = plot.clip(clip_x0, clip_y0, clip_x1, clip_y1); - - if (c->data.html.background_colour != NS_TRANSPARENT) - pstyle_fill_bg.fill_colour = c->data.html.background_colour; - - result &= plot.rectangle(clip_x0, clip_y0, clip_x1, clip_y1, &pstyle_fill_bg); - - result &= html_redraw_box(box, x, y, - clip_x0, clip_y0, clip_x1, clip_y1, - scale, pstyle_fill_bg.fill_colour); + /* The select menu needs special treating because, when opened, it + * reaches beyond its layout box. + */ + select = false; + select_only = false; + if (current_redraw_browser != NULL && + current_redraw_browser->visible_select_menu != NULL) { + struct form_control *control = + current_redraw_browser->visible_select_menu; + select = true; + /* check if the redraw rectangle is completely inside of the + select menu */ + select_only = form_clip_inside_select_menu(control, scale, + clip_x0, clip_y0, clip_x1, clip_y1); + } + + if (!select_only) { + /* clear to background colour */ + result = plot.clip(clip_x0, clip_y0, clip_x1, clip_y1); + + if (c->data.html.background_colour != NS_TRANSPARENT) + pstyle_fill_bg.fill_colour = + c->data.html.background_colour; + + result &= plot.rectangle(clip_x0, clip_y0, clip_x1, clip_y1, + &pstyle_fill_bg); + + result &= html_redraw_box(box, x, y, + clip_x0, clip_y0, clip_x1, clip_y1, + scale, pstyle_fill_bg.fill_colour); + } + if (select) { + int menu_x, menu_y; + box = current_redraw_browser->visible_select_menu->box; + box_coords(box, &menu_x, &menu_y); + + menu_x -= box->border[LEFT].width; + menu_y += box->height + box->border[BOTTOM].width + + box->padding[BOTTOM] + box->padding[TOP]; + result &= form_redraw_select_menu( + current_redraw_browser->visible_select_menu, + x + menu_x, y + menu_y, + current_redraw_browser->scale, + clip_x0, clip_y0, clip_x1, clip_y1); + } + if (want_knockout) knockout_plot_end(); @@ -195,6 +219,7 @@ bool html_redraw_box(struct box *box, int x_scrolled, y_scrolled; struct box *bg_box = NULL; css_color bgcol = 0; + bool has_x_scroll, has_y_scroll; if (html_redraw_printing && box->printed) return true; @@ -622,8 +647,8 @@ bool html_redraw_box(struct box *box, return false; if (box->object) { - x_scrolled = x - box->scroll_x * scale; - y_scrolled = y - box->scroll_y * scale; + x_scrolled = x - scroll_get_offset(box->scroll_x) * scale; + y_scrolled = y - scroll_get_offset(box->scroll_y) * scale; if (!content_redraw(box->object, x_scrolled + padding_left, y_scrolled + padding_top, @@ -664,23 +689,52 @@ bool html_redraw_box(struct box *box, /* list marker */ if (box->list_marker) if (!html_redraw_box(box->list_marker, - x_parent + box->x - box->scroll_x, - y_parent + box->y - box->scroll_y, + x_parent + box->x - + scroll_get_offset(box->scroll_x), + y_parent + box->y - + scroll_get_offset(box->scroll_y), clip_x0, clip_y0, clip_x1, clip_y1, scale, current_background_color)) return false; /* scrollbars */ - if (box->style && box->type != BOX_BR && box->type != BOX_TABLE && - box->type != BOX_INLINE && + if (current_redraw_browser && box->style && box->type != BOX_BR && + box->type != BOX_TABLE && box->type != BOX_INLINE && (css_computed_overflow(box->style) == CSS_OVERFLOW_SCROLL || css_computed_overflow(box->style) == - CSS_OVERFLOW_AUTO)) - if (!html_redraw_scrollbars(box, scale, x, y, - padding_width, padding_height, - current_background_color)) + CSS_OVERFLOW_AUTO)) { + + has_x_scroll = false; + has_y_scroll = false; + + has_x_scroll = box_hscrollbar_present(box); + + has_y_scroll = box_vscrollbar_present(box); + + if (!box_handle_scrollbars(box, + x_parent + box->x, y_parent + box->y, + has_x_scroll, has_y_scroll)) return false; + + if (box->scroll_x != NULL) + scroll_redraw(box->scroll_x, + x_parent + box->x, + y_parent + box->y + box->padding[TOP] + + box->height + box->padding[BOTTOM] - + SCROLLBAR_WIDTH, + clip_x0, clip_y0, clip_x1, clip_y1, + scale); + if (box->scroll_y != NULL) + scroll_redraw(box->scroll_y, + x_parent + box->x + box->padding[LEFT] + + box->width + box->padding[RIGHT] - + SCROLLBAR_WIDTH, + y_parent + box->y, + clip_x0, clip_y0, clip_x1, clip_y1, + scale); + } + if (box->type == BOX_BLOCK || box->type == BOX_INLINE_BLOCK || box->type == BOX_TABLE_CELL || box->object) @@ -717,16 +771,20 @@ bool html_redraw_box_children(struct box *box, if (c->type != BOX_FLOAT_LEFT && c->type != BOX_FLOAT_RIGHT) if (!html_redraw_box(c, - x_parent + box->x - box->scroll_x, - y_parent + box->y - box->scroll_y, + x_parent + box->x - + scroll_get_offset(box->scroll_x), + y_parent + box->y - + scroll_get_offset(box->scroll_y), clip_x0, clip_y0, clip_x1, clip_y1, scale, current_background_color)) return false; } for (c = box->float_children; c; c = c->next_float) if (!html_redraw_box(c, - x_parent + box->x - box->scroll_x, - y_parent + box->y - box->scroll_y, + x_parent + box->x - + scroll_get_offset(box->scroll_x), + y_parent + box->y - + scroll_get_offset(box->scroll_y), clip_x0, clip_y0, clip_x1, clip_y1, scale, current_background_color)) return false; @@ -1957,319 +2015,3 @@ bool html_redraw_text_decoration_block(struct box *box, int x, int y, } return true; } - -static inline bool -html_redraw_scrollbar_rectangle(int x0, int y0, int x1, int y1, colour c, - bool inset) -{ - static plot_style_t c0 = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_width = 1, - }; - - static plot_style_t c1 = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_width = 1, - }; - - static plot_style_t c2 = { - .stroke_type = PLOT_OP_TYPE_SOLID, - .stroke_width = 1, - }; - - if (inset) { - c0.stroke_colour = darken_colour(c); - c1.stroke_colour = lighten_colour(c); - } else { - c0.stroke_colour = lighten_colour(c); - c1.stroke_colour = darken_colour(c); - } - c2.stroke_colour = blend_colour(c0.stroke_colour, c1.stroke_colour); - - if (!plot.line(x0, y0, x1, y0, &c0)) return false; - if (!plot.line(x1, y0, x1, y1 + 1, &c1)) return false; - if (!plot.line(x1, y0, x1, y0 + 1, &c2)) return false; - if (!plot.line(x1, y1, x0, y1, &c1)) return false; - if (!plot.line(x0, y1, x0, y0, &c0)) return false; - if (!plot.line(x0, y1, x0, y1 + 1, &c2)) return false; - return true; -} - -/** - * Plot scrollbars for a scrolling box. - * - * \param box scrolling box - * \param scale scale for redraw - * \param x coordinate of box - * \param y coordinate of box - * \param padding_width width of padding box - * \param padding_height height of padding box - * \return true if successful, false otherwise - */ - -bool html_redraw_scrollbars(struct box *box, float scale, - int x, int y, int padding_width, int padding_height, - colour background_colour) -{ - const int w = SCROLLBAR_WIDTH * scale; - bool vscroll, hscroll; - int well_height, bar_top, bar_height; - int well_width, bar_left, bar_width; - int v[6]; /* array of triangle vertices */ - plot_style_t pstyle_css_scrollbar_bg_colour = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = css_scrollbar_bg_colour, - }; - plot_style_t pstyle_css_scrollbar_fg_colour = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = css_scrollbar_fg_colour, - }; - plot_style_t pstyle_css_scrollbar_arrow_colour = { - .fill_type = PLOT_OP_TYPE_SOLID, - .fill_colour = css_scrollbar_arrow_colour, - }; - - box_scrollbar_dimensions(box, padding_width, padding_height, w, - &vscroll, &hscroll, - &well_height, &bar_top, &bar_height, - &well_width, &bar_left, &bar_width); - - - /* horizontal scrollbar */ - if (hscroll) { - /* scrollbar outline */ - if (!html_redraw_scrollbar_rectangle(x, - y + padding_height - w, - x + padding_width - 1, - y + padding_height - 1, - css_scrollbar_bg_colour, true)) - return false; - /* left arrow icon border */ - if (!html_redraw_scrollbar_rectangle(x + 1, - y + padding_height - w + 1, - x + w - 2, - y + padding_height - 2, - css_scrollbar_fg_colour, false)) - return false; - /* left arrow icon background */ - if (!plot.rectangle(x + 2, - y + padding_height - w + 2, - x + w - 2, - y + padding_height - 2, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* left arrow */ - v[0] = x + w / 4; - v[1] = y + padding_height - w / 2; - v[2] = x + w * 3 / 4; - v[3] = y + padding_height - w * 3 / 4; - v[4] = x + w * 3 / 4; - v[5] = y + padding_height - w / 4; - if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour)) - return false; - /* scroll well background */ - if (!plot.rectangle(x + w - 1, - y + padding_height - w + 1, - x + w + well_width + (vscroll ? 2 : 1), - y + padding_height - 1, - &pstyle_css_scrollbar_bg_colour)) - return false; - /* scroll position indicator bar */ - if (!html_redraw_scrollbar_rectangle(x + w + bar_left, - y + padding_height - w + 1, - x + w + bar_left + bar_width + (vscroll? 1 : 0), - y + padding_height - 2, - css_scrollbar_fg_colour, false)) - return false; - if (!plot.rectangle(x + w + bar_left + 1, - y + padding_height - w + 2, - x + w + bar_left + bar_width + (vscroll? 1 : 0), - y + padding_height - 2, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* right arrow icon border */ - if (!html_redraw_scrollbar_rectangle(x + w + well_width + 2, - y + padding_height - w + 1, - x + w + well_width + w - (vscroll ? 1 : 2), - y + padding_height - 2, - css_scrollbar_fg_colour, false)) - return false; - /* right arrow icon background */ - if (!plot.rectangle(x + w + well_width + 3, - y + padding_height - w + 2, - x + w + well_width + w - (vscroll ? 1 : 2), - y + padding_height - 2, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* right arrow */ - v[0] = x + w + well_width + w * 3 / 4 + (vscroll ? 1 : 0); - v[1] = y + padding_height - w / 2; - v[2] = x + w + well_width + w / 4 + (vscroll ? 1 : 0); - v[3] = y + padding_height - w * 3 / 4; - v[4] = x + w + well_width + w / 4 + (vscroll ? 1 : 0); - v[5] = y + padding_height - w / 4; - if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour)) - return false; - } - - /* vertical scrollbar */ - if (vscroll) { - /* outline */ - if (!html_redraw_scrollbar_rectangle(x + padding_width - w, - y, - x + padding_width - 1, - y + padding_height - 1, - css_scrollbar_bg_colour, - true)) - return false; - /* top arrow background */ - if (!html_redraw_scrollbar_rectangle(x + padding_width - w + 1, - y + 1, - x + padding_width - 2, - y + w - 2, - css_scrollbar_fg_colour, - false)) - return false; - if (!plot.rectangle(x + padding_width - w + 2, - y + 2, - x + padding_width - 2, - y + w - 2, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* up arrow */ - v[0] = x + padding_width - w / 2; - v[1] = y + w / 4; - v[2] = x + padding_width - w * 3 / 4; - v[3] = y + w * 3 / 4; - v[4] = x + padding_width - w / 4; - v[5] = y + w * 3 / 4; - if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour)) - return false; - /* scroll well background */ - if (!plot.rectangle(x + padding_width - w + 1, - y + w - 1, - x + padding_width - 1, - y + padding_height - w + 1, - &pstyle_css_scrollbar_bg_colour)) - return false; - /* scroll position indicator bar */ - if (!html_redraw_scrollbar_rectangle(x + padding_width - w + 1, - y + w + bar_top, - x + padding_width - 2, - y + w + bar_top + bar_height, - css_scrollbar_fg_colour, false)) - return false; - if (!plot.rectangle(x + padding_width - w + 2, - y + w + bar_top + 1, - x + padding_width - 2, - y + w + bar_top + bar_height, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* bottom arrow background */ - if (!html_redraw_scrollbar_rectangle(x + padding_width - w + 1, - y + padding_height - w + 1, - x + padding_width - 2, - y + padding_height - 2, - css_scrollbar_fg_colour, false)) - return false; - if (!plot.rectangle(x + padding_width - w + 2, - y + padding_height - w + 2, - x + padding_width - 2, - y + padding_height - 2, - &pstyle_css_scrollbar_fg_colour)) - return false; - /* down arrow */ - v[0] = x + padding_width - w / 2; - v[1] = y + w + well_height + w * 3 / 4; - v[2] = x + padding_width - w * 3 / 4; - v[3] = y + w + well_height + w / 4; - v[4] = x + padding_width - w / 4; - v[5] = y + w + well_height + w / 4; - if (!plot.polygon(v, 3, &pstyle_css_scrollbar_arrow_colour)) - return false; - } - - return true; -} - - -/** - * Determine if a box has a vertical scrollbar. - * - * \param box scrolling box - * \return the box has a vertical scrollbar - */ - -bool box_vscrollbar_present(const struct box * const box) -{ - return box->descendant_y0 < -box->border[TOP].width || - box->padding[TOP] + box->height + box->padding[BOTTOM] + - box->border[BOTTOM].width < box->descendant_y1; -} - - -/** - * Determine if a box has a horizontal scrollbar. - * - * \param box scrolling box - * \return the box has a horizontal scrollbar - */ - -bool box_hscrollbar_present(const struct box * const box) -{ - return box->descendant_x0 < -box->border[LEFT].width || - box->padding[LEFT] + box->width + box->padding[RIGHT] + - box->border[RIGHT].width < box->descendant_x1; -} - - -/** - * Calculate scrollbar dimensions and positions for a box. - * - * \param box scrolling box - * \param padding_width scaled width of padding box - * \param padding_height scaled height of padding box - * \param w scaled scrollbar width - * \param vscroll updated to vertical scrollbar present - * \param hscroll updated to horizontal scrollbar present - * \param well_height updated to vertical well height - * \param bar_top updated to top position of vertical scrollbar - * \param bar_height updated to height of vertical scrollbar - * \param well_width updated to horizontal well width - * \param bar_left updated to left position of horizontal scrollbar - * \param bar_width updated to width of horizontal scrollbar - */ - -void box_scrollbar_dimensions(const struct box * const box, - const int padding_width, const int padding_height, const int w, - bool * const vscroll, bool * const hscroll, - int * const well_height, - int * const bar_top, int * const bar_height, - int * const well_width, - int * const bar_left, int * const bar_width) -{ - *vscroll = box_vscrollbar_present(box); - *hscroll = box_hscrollbar_present(box); - *well_height = padding_height - w - w; - *bar_top = 0; - *bar_height = *well_height; - if (box->descendant_y1 - box->descendant_y0 != 0) { - *bar_top = (float) *well_height * (float) box->scroll_y / - (float) (box->descendant_y1 - - box->descendant_y0); - *bar_height = (float) *well_height * (float) box->height / - (float) (box->descendant_y1 - - box->descendant_y0); - } - *well_width = padding_width - w - w - (*vscroll ? w : 0); - *bar_left = 0; - *bar_width = *well_width; - if (box->descendant_x1 - box->descendant_x0 != 0) { - *bar_left = (float) *well_width * (float) box->scroll_x / - (float) (box->descendant_x1 - - box->descendant_x0); - *bar_width = (float) *well_width * (float) box->width / - (float) (box->descendant_x1 - - box->descendant_x0); - } -} diff --git a/render/layout.c b/render/layout.c index 3ba9b038d..f2c0c158e 100644 --- a/render/layout.c +++ b/render/layout.c @@ -44,6 +44,7 @@ #include "content/content.h" #include "desktop/gui.h" #include "desktop/options.h" +#include "desktop/scroll.h" #include "render/box.h" #include "render/font.h" #include "render/form.h" @@ -1989,8 +1990,9 @@ bool layout_line(struct box *first, int *width, int *y, if (opt_maxwidth < opt_width) opt_maxwidth =opt_width; } - b->width = opt_maxwidth; + if (option_core_select_menu) + b->width += SCROLLBAR_WIDTH; } else { font_func->font_width(&fstyle, b->text, b->length, &b->width); @@ -2624,6 +2626,9 @@ struct box *layout_minmax_line(struct box *first, } b->width = opt_maxwidth; + if (option_core_select_menu) + b->width += SCROLLBAR_WIDTH; + } else { font_func->font_width(&fstyle, b->text, b->length, &b->width); diff --git a/render/layout.h b/render/layout.h index 2d07a40e9..30d9757f6 100644 --- a/render/layout.h +++ b/render/layout.h @@ -27,8 +27,6 @@ #ifndef _NETSURF_RENDER_LAYOUT_H_ #define _NETSURF_RENDER_LAYOUT_H_ -#define SCROLLBAR_WIDTH 16 - struct box; bool layout_document(struct content *content, int width, int height); |