From 9f2ea3be4c50f0e30153fc648f3219b5bd7f3fa3 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Tue, 5 Jul 2011 20:13:28 +0000 Subject: Iframe scrollbars. svn path=/trunk/netsurf/; revision=12571 --- desktop/browser.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++++----- desktop/browser.h | 5 + desktop/frames.c | 131 ++++++++++++++++++++++++- desktop/frames.h | 11 +++ 4 files changed, 401 insertions(+), 24 deletions(-) diff --git a/desktop/browser.c b/desktop/browser.c index fd2e28ebc..8b7741ca2 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -50,6 +50,7 @@ #include "desktop/gui.h" #include "desktop/knockout.h" #include "desktop/options.h" +#include "desktop/scrollbar.h" #include "desktop/selection.h" #include "desktop/textinput.h" #include "desktop/plotters.h" @@ -92,6 +93,46 @@ static void browser_window_find_target_internal(struct browser_window *bw, static void browser_window_mouse_drag_end(struct browser_window *bw, browser_mouse_state mouse, int x, int y); + +/** + * Get position of scrollbar widget within browser window. + * + * \param bw The browser window + * \param horizontal Whether to get position of horizontal scrollbar + * \param x Updated to x-coord of top left of scrollbar widget + * \param y Updated to y-coord of top left of scrollbar widget + */ + +static inline void browser_window_get_scrollbar_pos(struct browser_window *bw, + bool horizontal, int *x, int *y) +{ + if (horizontal) { + *x = 0; + *y = bw->height - SCROLLBAR_WIDTH; + } else { + *x = bw->width - SCROLLBAR_WIDTH; + *y = 0; + } +} + + +/** + * Get browser window scrollbar widget length + * + * \param bw The browser window + * \param horizontal Whether to get length of horizontal scrollbar + * \return the scrollbar's length + */ + +static inline int browser_window_get_scrollbar_len(struct browser_window *bw, + bool horizontal) +{ + if (horizontal) + return bw->width - (bw->scroll_y != NULL ? SCROLLBAR_WIDTH : 0); + else + return bw->height; +} + /* exported interface, documented in browser.h */ bool browser_window_redraw(struct browser_window *bw, int x, int y, const struct rect *clip, const struct redraw_context *ctx) @@ -102,6 +143,7 @@ bool browser_window_redraw(struct browser_window *bw, int x, int y, bool plot_ok = true; content_type content_type; struct content_redraw_data data; + struct rect content_clip; if (bw == NULL) { LOG(("NULL browser window")); @@ -138,8 +180,8 @@ bool browser_window_redraw(struct browser_window *bw, int x, int y, } /* Set up content redraw data */ - data.x = x; - data.y = y; + data.x = x - scrollbar_get_offset(bw->scroll_x); + data.y = y - scrollbar_get_offset(bw->scroll_y); data.width = width; data.height = height; @@ -147,9 +189,48 @@ bool browser_window_redraw(struct browser_window *bw, int x, int y, data.scale = bw->scale; data.repeat_x = false; data.repeat_y = false; + + content_clip = *clip; + + if (!bw->window) { + int x0 = x * bw->scale; + int y0 = y * bw->scale; + int x1 = (x + bw->width - ((bw->scroll_y != NULL) ? + SCROLLBAR_WIDTH : 0)) * bw->scale; + int y1 = (y + bw->height - ((bw->scroll_x != NULL) ? + SCROLLBAR_WIDTH : 0)) * bw->scale; + + if (content_clip.x0 < x0) content_clip.x0 = x0; + if (content_clip.y0 < y0) content_clip.y0 = y0; + if (x1 < content_clip.x1) content_clip.x1 = x1; + if (y1 < content_clip.y1) content_clip.y1 = y1; + } /* Render the content */ - plot_ok &= content_redraw(bw->current_content, &data, clip, &new_ctx); + plot_ok &= content_redraw(bw->current_content, &data, + &content_clip, &new_ctx); + + /* Back to full clip rect */ + new_ctx.plot->clip(clip); + + if (!bw->window) { + /* Render scrollbars */ + int off_x, off_y; + if (bw->scroll_x != NULL) { + browser_window_get_scrollbar_pos(bw, true, + &off_x, &off_y); + plot_ok &= scrollbar_redraw(bw->scroll_x, + x + off_x, y + off_y, clip, + bw->scale, &new_ctx); + } + if (bw->scroll_y != NULL) { + browser_window_get_scrollbar_pos(bw, false, + &off_x, &off_y); + plot_ok &= scrollbar_redraw(bw->scroll_y, + x + off_x, y + off_y, clip, + bw->scale, &new_ctx); + } + } if (bw->browser_window_type != BROWSER_WINDOW_IFRAME && ctx->plot->option_knockout) { @@ -183,7 +264,7 @@ void browser_window_update_extent(struct browser_window *bw) gui_window_update_extent(bw->window); break; case BROWSER_WINDOW_IFRAME: - /* TODO */ + browser_window_handle_scrollbars(bw); break; } } @@ -207,8 +288,8 @@ void browser_window_get_position(struct browser_window *bw, bool root, break; case BROWSER_WINDOW_IFRAME: - *pos_x += bw->x * bw->scale; - *pos_y += bw->y * bw->scale; + *pos_x += (bw->x - scrollbar_get_offset(bw->scroll_x)) * bw->scale; + *pos_y += (bw->y - scrollbar_get_offset(bw->scroll_y)) * bw->scale; break; } @@ -257,6 +338,7 @@ struct browser_window * browser_window_get_root(struct browser_window *bw) } return bw; } + /* exported interface, documented in browser.h */ bool browser_window_has_selection(struct browser_window *bw) { @@ -272,6 +354,26 @@ bool browser_window_has_selection(struct browser_window *bw) } } +/** + * Set scroll offsets for a browser window. + * + * \param bw The browser window + * \param x The x scroll offset to set + * \param y The y scroll offset to set + */ + +static void browser_window_set_scroll(struct browser_window *bw, int x, int y) +{ + if (bw->window != NULL) { + gui_window_set_scroll(bw->window, x, y); + } else { + if (bw->scroll_x != NULL) + scrollbar_set(bw->scroll_x, x, false); + if (bw->scroll_y != NULL) + scrollbar_set(bw->scroll_y, y, false); + } +} + /** * Create and open a new root browser window with the given page. * @@ -351,6 +453,9 @@ void browser_window_initialise_common(struct browser_window *bw, bw->drag_type = DRAGGING_NONE; bw->scale = (float) option_scale / 100.0; + bw->scroll_x = NULL; + bw->scroll_y = NULL; + bw->focus = NULL; /* initialise status text cache */ @@ -716,6 +821,14 @@ nserror browser_window_callback(hlcache_handle *c, case CONTENT_MSG_DONE: assert(bw->current_content == c); + if (bw->window == NULL) { + /* Updated browser window's scrollbars. + * TODO: do this before CONTENT_MSG_DONE */ + browser_window_reformat(bw, true, + bw->width, bw->height); + browser_window_handle_scrollbars(bw); + } + browser_window_update(bw, false); browser_window_set_status(bw, content_get_status_message(c)); browser_window_stop_throbber(bw); @@ -1033,13 +1146,13 @@ void browser_window_update(struct browser_window *bw, bool scroll_to_top) browser_window_update_extent(bw); if (scroll_to_top) - gui_window_set_scroll(bw->window, 0, 0); + browser_window_set_scroll(bw, 0, 0); /* if frag_id exists, then try to scroll to it */ /** \TODO don't do this if the user has scrolled */ if (bw->frag_id && html_get_id_offset(bw->current_content, bw->frag_id, &x, &y)) { - gui_window_set_scroll(bw->window, x, y); + browser_window_set_scroll(bw, x, y); } gui_window_redraw_window(bw->window); @@ -1048,7 +1161,17 @@ void browser_window_update(struct browser_window *bw, bool scroll_to_top) case BROWSER_WINDOW_IFRAME: /* Internal iframe browser window */ - /** \TODO handle scrollbar extents, scroll offset */ + browser_window_update_extent(bw); + + if (scroll_to_top) + browser_window_set_scroll(bw, 0, 0); + + /* if frag_id exists, then try to scroll to it */ + /** \TODO don't do this if the user has scrolled */ + if (bw->frag_id && html_get_id_offset(bw->current_content, + bw->frag_id, &x, &y)) { + browser_window_set_scroll(bw, x, y); + } html_redraw_a_box(bw->parent->current_content, bw->box); break; @@ -1409,8 +1532,14 @@ void browser_window_reformat(struct browser_window *bw, bool background, if (bw->browser_window_type != BROWSER_WINDOW_IFRAME) { /* Iframe dimensions are already scaled in parent's layout */ - width /= bw->scale; + width /= bw->scale; height /= bw->scale; + } else { + width -= bw->scroll_y ? SCROLLBAR_WIDTH : 0; + height -= bw->scroll_x ? SCROLLBAR_WIDTH : 0; + + width = width > 0 ? width : 0; + height = height > 0 ? height : 0; } content_reformat(c, background, width, height); @@ -1688,6 +1817,8 @@ void browser_window_mouse_track(struct browser_window *bw, browser_mouse_state mouse, int x, int y) { hlcache_handle *c = bw->current_content; + const char *status = NULL; + gui_pointer_shape pointer = GUI_POINTER_DEFAULT; if (c == NULL && bw->drag_type != DRAGGING_FRAME) return; @@ -1696,6 +1827,51 @@ void browser_window_mouse_track(struct browser_window *bw, browser_window_mouse_drag_end(bw, mouse, x, y); } + if (bw->scroll_x != NULL) { + int scr_x, scr_y; + browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y); + scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x); + scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y); + + if ((scr_x > 0 && scr_x < browser_window_get_scrollbar_len(bw, + true) && + scr_y > 0 && scr_y < SCROLLBAR_WIDTH) || + bw->drag_type == DRAGGING_SCR_X) { + status = scrollbar_mouse_action(bw->scroll_x, mouse, + scr_x, scr_y); + pointer = GUI_POINTER_DEFAULT; + + if (status != NULL) + browser_window_set_status(bw, status); + + browser_window_set_pointer(bw, pointer); + return; + } + } + + if (bw->scroll_y != NULL) { + int scr_x, scr_y; + browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y); + scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x); + scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y); + + if ((scr_y > 0 && scr_y < browser_window_get_scrollbar_len(bw, + false) && + scr_x > 0 && scr_x < SCROLLBAR_WIDTH) || + bw->drag_type == DRAGGING_SCR_Y) { + + status = scrollbar_mouse_action(bw->scroll_y, mouse, + scr_x, scr_y); + pointer = GUI_POINTER_DEFAULT; + + if (status != NULL) + browser_window_set_status(bw, status); + + browser_window_set_pointer(bw, pointer); + return; + } + } + if (bw->drag_type == DRAGGING_FRAME) { browser_window_resize_frame(bw, bw->x0 + x, bw->y0 + y); } else if (bw->drag_type == DRAGGING_PAGE_SCROLL) { @@ -1710,17 +1886,7 @@ void browser_window_mouse_track(struct browser_window *bw, bw->drag_start_scroll_x = scrollx; bw->drag_start_scroll_y = scrolly; - switch (bw->browser_window_type) { - default: - /* Fall through to normal, until frame(set)s are - * handled in the core */ - case BROWSER_WINDOW_NORMAL: - gui_window_set_scroll(bw->window, scrollx, scrolly); - break; - case BROWSER_WINDOW_IFRAME: - /* TODO */ - break; - } + browser_window_set_scroll(bw, scrollx, scrolly); } else { assert(c != NULL); content_mouse_track(c, bw, mouse, x, y); @@ -1741,10 +1907,54 @@ void browser_window_mouse_click(struct browser_window *bw, browser_mouse_state mouse, int x, int y) { hlcache_handle *c = bw->current_content; + const char *status = NULL; + gui_pointer_shape pointer = GUI_POINTER_DEFAULT; if (!c) return; + if (bw->scroll_x != NULL) { + int scr_x, scr_y; + browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y); + scr_x = x - scr_x; + scr_y = y - scr_y; + + if (scr_x > 0 && scr_x < browser_window_get_scrollbar_len(bw, + true) && + scr_y > 0 && scr_y < SCROLLBAR_WIDTH) { + status = scrollbar_mouse_action(bw->scroll_x, mouse, + scr_x, scr_y); + pointer = GUI_POINTER_DEFAULT; + + if (status != NULL) + browser_window_set_status(bw, status); + + browser_window_set_pointer(bw, pointer); + return; + } + } + + if (bw->scroll_y != NULL) { + int scr_x, scr_y; + browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y); + scr_x = x - scr_x; + scr_y = y - scr_y; + + if (scr_y > 0 && scr_y < browser_window_get_scrollbar_len(bw, + false) && + scr_x > 0 && scr_x < SCROLLBAR_WIDTH) { + status = scrollbar_mouse_action(bw->scroll_y, mouse, + scr_x, scr_y); + pointer = GUI_POINTER_DEFAULT; + + if (status != NULL) + browser_window_set_status(bw, status); + + browser_window_set_pointer(bw, pointer); + return; + } + } + switch (content_get_type(c)) { case CONTENT_HTML: case CONTENT_TEXTPLAIN: @@ -1784,6 +1994,8 @@ void browser_window_mouse_click(struct browser_window *bw, void browser_window_mouse_drag_end(struct browser_window *bw, browser_mouse_state mouse, int x, int y) { + int scr_x, scr_y; + switch (bw->drag_type) { case DRAGGING_SELECTION: { @@ -1817,6 +2029,30 @@ void browser_window_mouse_drag_end(struct browser_window *bw, /* Drag handled by content handler */ break; + case DRAGGING_SCR_X: + + browser_window_get_scrollbar_pos(bw, true, &scr_x, &scr_y); + + scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x); + scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y); + + scrollbar_mouse_drag_end(bw->scroll_x, mouse, scr_x, scr_y); + + bw->drag_type = DRAGGING_NONE; + break; + + case DRAGGING_SCR_Y: + + browser_window_get_scrollbar_pos(bw, false, &scr_x, &scr_y); + + scr_x = x - scr_x - scrollbar_get_offset(bw->scroll_x); + scr_y = y - scr_y - scrollbar_get_offset(bw->scroll_y); + + scrollbar_mouse_drag_end(bw->scroll_y, mouse, scr_x, scr_y); + + bw->drag_type = DRAGGING_NONE; + break; + default: bw->drag_type = DRAGGING_NONE; break; diff --git a/desktop/browser.h b/desktop/browser.h index 0949e856e..ced12af49 100644 --- a/desktop/browser.h +++ b/desktop/browser.h @@ -60,6 +60,8 @@ typedef enum { DRAGGING_SELECTION, DRAGGING_PAGE_SCROLL, DRAGGING_FRAME, + DRAGGING_SCR_X, + DRAGGING_SCR_Y, DRAGGING_OTHER } browser_drag_type; @@ -135,6 +137,9 @@ struct browser_window { int width; int height; + struct scrollbar *scroll_x; /**< Horizontal scroll. */ + struct scrollbar *scroll_y; /**< Vertical scroll. */ + /** scale of window contents */ float scale; diff --git a/desktop/frames.c b/desktop/frames.c index d8e2c992b..afcd84e01 100644 --- a/desktop/frames.c +++ b/desktop/frames.c @@ -34,6 +34,7 @@ #include "desktop/frames.h" #include "desktop/history_core.h" #include "desktop/gui.h" +#include "desktop/scrollbar.h" #include "desktop/selection.h" #include "utils/log.h" #include "utils/messages.h" @@ -47,6 +48,121 @@ static bool browser_window_resolve_frame_dimension(struct browser_window *bw, bool height); +/** + * Callback for (i)frame scrollbars. + */ +void browser_window_scroll_callback(void *client_data, + struct scrollbar_msg_data *scrollbar_data) +{ + struct browser_window *bw = client_data; + + switch(scrollbar_data->msg) { + case SCROLLBAR_MSG_REDRAW: + /* TODO: Is this needed? */ + break; + case SCROLLBAR_MSG_MOVED: + html_redraw_a_box(bw->parent->current_content, bw->box); + break; + case SCROLLBAR_MSG_SCROLL_START: + if (scrollbar_is_horizontal(scrollbar_data->scrollbar)) + browser_window_set_drag_type(bw, DRAGGING_SCR_X); + else + browser_window_set_drag_type(bw, DRAGGING_SCR_Y); + + break; + case SCROLLBAR_MSG_SCROLL_FINISHED: + browser_window_set_drag_type(bw, DRAGGING_NONE); + + browser_window_set_pointer(bw, GUI_POINTER_DEFAULT); + break; + } +} + +/* exported interface, documented in browser.h */ +void browser_window_handle_scrollbars(struct browser_window *bw) +{ + hlcache_handle *h = bw->current_content; + bool scroll_x; + bool scroll_y; + int c_width = 0; + int c_height = 0; + + assert(!bw->window); /* Core-handled windows only */ + + if (h != NULL) { + c_width = content_get_width(h); + c_height = content_get_height(h); + } + + if (bw->scrolling == SCROLLING_YES) { + scroll_x = true; + scroll_y = true; + } else if (bw->scrolling == SCROLLING_AUTO && + bw->current_content) { + int bw_width = bw->width; + int bw_height = bw->height; + + /* subtract existing scrollbar width */ + bw_width -= bw->scroll_y ? SCROLLBAR_WIDTH : 0; + bw_height -= bw->scroll_x ? SCROLLBAR_WIDTH : 0; + + scroll_y = (c_height > bw_height) ? true : false; + scroll_x = (c_width > bw_width) ? true : false; + } else { + /* No scrollbars */ + scroll_x = false; + scroll_y = false; + } + + if (!scroll_x && bw->scroll_x != NULL) { + scrollbar_destroy(bw->scroll_x); + bw->scroll_x = NULL; + } + + if (!scroll_y && bw->scroll_y != NULL) { + scrollbar_destroy(bw->scroll_y); + bw->scroll_y = NULL; + } + + if (scroll_y) { + int length = bw->height; + int visible = bw->height - (scroll_x ? SCROLLBAR_WIDTH : 0); + + if (bw->scroll_y == NULL) { + /* create vertical scrollbar */ + if (!scrollbar_create(false, length, c_height, visible, + bw, browser_window_scroll_callback, + &(bw->scroll_y))) + return; + } else { + /* update vertical scrollbar */ + scrollbar_set_extents(bw->scroll_y, length, + visible, c_height); + } + } + + if (scroll_x) { + int length = bw->width - (scroll_y ? SCROLLBAR_WIDTH : 0); + int visible = length; + + if (bw->scroll_x == NULL) { + /* create horizontal scrollbar */ + if (!scrollbar_create(true, length, c_width, visible, + bw, browser_window_scroll_callback, + &(bw->scroll_x))) + return; + } else { + /* update horizontal scrollbar */ + scrollbar_set_extents(bw->scroll_x, length, + visible, c_width); + } + } + + if (scroll_x && scroll_y) + scrollbar_make_pair(bw->scroll_x, bw->scroll_y); +} + + /** * Create and open a iframes for a browser window. * @@ -55,7 +171,8 @@ static bool browser_window_resolve_frame_dimension(struct browser_window *bw, */ void browser_window_create_iframes(struct browser_window *bw, - struct content_html_iframe *iframe) { + struct content_html_iframe *iframe) +{ struct browser_window *window; struct content_html_iframe *cur; struct rect rect; @@ -130,8 +247,16 @@ void browser_window_create_iframes(struct browser_window *bw, void browser_window_recalculate_iframes(struct browser_window *bw) { - /* TODO: decide if this is still needed after scrollbars are - * implemented */ + struct browser_window *window; + int index; + + for (index = 0; index < bw->iframe_count; index++) { + window = &(bw->iframes[index]); + + if (window != NULL) { + browser_window_handle_scrollbars(window); + } + } } diff --git a/desktop/frames.h b/desktop/frames.h index bb50b56e2..11fbcea2f 100644 --- a/desktop/frames.h +++ b/desktop/frames.h @@ -38,4 +38,15 @@ bool browser_window_resize_frames(struct browser_window *bw, gui_pointer_shape *pointer, const char **status, bool *action); void browser_window_resize_frame(struct browser_window *bw, int x, int y); +void browser_window_scroll_callback(void *client_data, + struct scrollbar_msg_data *scrollbar_data); + + +/** + * Create, remove, and update browser window scrollbars + * + * \param bw The browser window + */ +void browser_window_handle_scrollbars(struct browser_window *bw); + #endif -- cgit v1.2.3