/* exported function docuemnted in fbtk.h */ void fbtk_set_caret(fbtk_widget_t *widget, bool set, int x, int y, int height, void (*remove_caret)(fbtk_widget_t *widget)) { fbtk_widget_t *root; assert(widget != NULL); root = fbtk_get_root_widget(widget); if (root->u.root.caret.owner != NULL && root->u.root.caret.remove_cb != NULL) root->u.root.caret.remove_cb(widget); if (set) { assert(remove_caret != NULL); root->u.root.caret.owner = widget; root->u.root.caret.x = x; root->u.root.caret.y = y; root->u.root.caret.height = height; root->u.root.caret.remove_cb = remove_caret; } else { root->u.root.caret.owner = NULL; root->u.root.caret.remove_cb = NULL; } }
/* exported function documented in fbtk.h */ void fbtk_click(fbtk_widget_t *widget, nsfb_event_t *event) { fbtk_widget_t *root; fbtk_widget_t *clicked; nsfb_bbox_t cloc; int x, y; /* ensure we have the root widget */ root = fbtk_get_root_widget(widget); nsfb_cursor_loc_get(root->u.root.fb, &cloc); clicked = fbtk_get_widget_at(root, cloc.x0, cloc.y0); if (clicked == NULL) return; if (fbtk_get_handler(clicked, FBTK_CBT_INPUT) != NULL) { fbtk_set_focus(clicked); } x = fbtk_get_absx(clicked); y = fbtk_get_absy(clicked); LOG("clicked %p at %d,%d", clicked, x, y); /* post the click */ fbtk_post_callback(clicked, FBTK_CBT_CLICK, event, cloc.x0 - x, cloc.y0 - y); }
/* exported function documented in fbtk.h */ fbtk_widget_t * fbtk_get_widget_at(fbtk_widget_t *nwid, int x, int y) { fbtk_widget_t *widget = NULL; /* found widget */ /* require the root widget to start */ nwid = fbtk_get_root_widget(nwid); while (nwid != NULL) { if ((nwid->mapped) && (x >= nwid->x) && (y >= nwid->y) && (x < (nwid->x + nwid->width)) && (y < (nwid->y + nwid->height))) { widget = nwid; x -= nwid->x; y -= nwid->y; nwid = nwid->first_child; } else { nwid = nwid->next; } } return widget; }
/* tree dump debug, also example of depth first tree walk */ static void dump_tk_tree(fbtk_widget_t *widget) { widget = fbtk_get_root_widget(widget); int indent = 0; while (widget != NULL) { LOG("%*s%p", indent, "", widget); if (widget->first_child != NULL) { widget = widget->first_child; indent += 6; } else if (widget->next != NULL) { widget = widget->next; } else { while ((widget->parent != NULL) && (widget->parent->next == NULL)) { widget = widget->parent; indent -= 6; } if (widget->parent != NULL) { indent -= 6; widget = widget->parent->next; } else { widget = NULL; } } } }
/* exported function documented in fbtk.h */ int fbtk_redraw(fbtk_widget_t *widget) { fbtk_widget_t *root; /* ensure we have the root widget */ root = fbtk_get_root_widget(widget); return do_redraw(root->u.root.fb, root); }
/* exported function documented in fbtk.h */ bool fbtk_get_redraw_pending(fbtk_widget_t *widget) { fbtk_widget_t *root; /* ensure we have the root widget */ root = fbtk_get_root_widget(widget); return root->redraw.needed | root->redraw.child; }
static int hscroll_redraw(fbtk_widget_t *widget, fbtk_callback_info *cbi) { int hscroll; int hpos; nsfb_bbox_t bbox; nsfb_bbox_t rect; fbtk_widget_t *root = fbtk_get_root_widget(widget); fbtk_get_bbox(widget, &bbox); nsfb_claim(root->u.root.fb, &bbox); rect = bbox; /* background */ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); /* scroll well */ rect.x0 = bbox.x0 + 1; rect.y0 = bbox.y0 + 2; rect.x1 = bbox.x1 - 2; rect.y1 = bbox.y1 - 3; nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->fg); /* scroll well outline */ nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0xFF999999, false, false); if ((widget->u.scroll.maximum - widget->u.scroll.minimum) > 0) { hscroll = ((widget->width - 4) * widget->u.scroll.thumb) / (widget->u.scroll.maximum - widget->u.scroll.minimum) ; hpos = ((widget->width - 4) * widget->u.scroll.position) / (widget->u.scroll.maximum - widget->u.scroll.minimum) ; } else { hscroll = (widget->width - 4); hpos = 0; } LOG(("hscroll %d", hscroll)); rect.x0 = bbox.x0 + 3 + hpos; rect.y0 = bbox.y0 + 5; rect.x1 = bbox.x0 + hscroll + hpos; rect.y1 = bbox.y0 + widget->height - 5; nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); nsfb_update(root->u.root.fb, &bbox); return 0; }
/* internally exported function documented in widget.h */ int fbtk_set_ptr(fbtk_widget_t *widget, fbtk_callback_info *cbi) { fbtk_widget_t *root = fbtk_get_root_widget(widget); struct fbtk_bitmap *bm = cbi->context; nsfb_cursor_set(root->u.root.fb, (nsfb_colour_t *)bm->pixdata, bm->width, bm->height, bm->width, bm->hot_x, bm->hot_y); return 0; }
/* exported function documented in fbtk.h */ void fbtk_input(fbtk_widget_t *root, nsfb_event_t *event) { fbtk_widget_t *input; root = fbtk_get_root_widget(root); /* obtain widget with input focus */ input = root->u.root.input; if (input == NULL) { LOG("No widget has input focus."); return; /* no widget with input */ } fbtk_post_callback(input, FBTK_CBT_INPUT, event); }
/* exported function documented in fbtk.h */ bool fbtk_tgrab_pointer(fbtk_widget_t *widget) { fbtk_widget_t *root; /* ensure we have the root widget */ root = fbtk_get_root_widget(widget); if (root->u.root.grabbed == widget) { /* release pointer grab */ root->u.root.grabbed = NULL; return true; } else if (root->u.root.grabbed == NULL) { /* set pointer grab */ root->u.root.grabbed = widget; return true; } /* pointer was already grabbed */ return false; }
bool fbtk_get_caret(fbtk_widget_t *widget, int *x, int *y, int *height) { fbtk_widget_t *root = fbtk_get_root_widget(widget); if (root->u.root.caret.owner == widget) { *x = root->u.root.caret.x; *y = root->u.root.caret.y; *height = root->u.root.caret.height; return true; } else { *x = 0; *y = 0; *height = 0; return false; } }
/** Text button redraw callback. * * Called when a text widget requires redrawing. * * @param widget The widget to be redrawn. * @param cbi The callback parameters. * @return The callback result. */ static int fb_redraw_text_button(fbtk_widget_t *widget, fbtk_callback_info *cbi ) { nsfb_bbox_t bbox; nsfb_bbox_t rect; nsfb_bbox_t line; nsfb_plot_pen_t pen; plot_font_style_t font_style; int fh; int border; fbtk_widget_t *root = fbtk_get_root_widget(widget); if (widget->height < 20) { border = 0; } else { border = (widget->height * 10) / 90; } pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID; pen.stroke_width = 1; pen.stroke_colour = brighten_colour(widget->bg); fbtk_get_bbox(widget, &bbox); rect = bbox; rect.x1--; rect.y1--; nsfb_claim(root->u.root.fb, &bbox); /* clear background */ if ((widget->bg & 0xFF000000) != 0) { /* transparent polygon filling isnt working so fake it */ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); } if (widget->u.text.outline) { line.x0 = rect.x0; line.y0 = rect.y0; line.x1 = rect.x0; line.y1 = rect.y1; nsfb_plot_line(root->u.root.fb, &line, &pen); line.x0 = rect.x0; line.y0 = rect.y0; line.x1 = rect.x1; line.y1 = rect.y0; nsfb_plot_line(root->u.root.fb, &line, &pen); pen.stroke_colour = darken_colour(widget->bg); line.x0 = rect.x0; line.y0 = rect.y1; line.x1 = rect.x1; line.y1 = rect.y1; nsfb_plot_line(root->u.root.fb, &line, &pen); line.x0 = rect.x1; line.y0 = rect.y0; line.x1 = rect.x1; line.y1 = rect.y1; nsfb_plot_line(root->u.root.fb, &line, &pen); border++; } if (widget->u.text.text != NULL) { fh = widget->height - border - border; font_style.family = PLOT_FONT_FAMILY_SANS_SERIF; font_style.size = px_to_pt(fh) * FONT_SIZE_SCALE; font_style.weight = 400; font_style.flags = FONTF_NONE; font_style.background = widget->bg; font_style.foreground = widget->fg; LOG(("plotting %p at %d,%d %d,%d w/h %d,%d font h %d border %d", widget, bbox.x0, bbox.y0, bbox.x1, bbox.y1, widget->width, widget->height, fh, border)); /* Call the fb text plotting, baseline is 3/4 down the * font, somewhere along the way the co-ordinate * system for the baseline is to the "higher value * pixel co-ordinate" due to this the + 1 is neccessary. */ fb_plotters.text(bbox.x0 + border, bbox.y0 + (((fh * 3) + 3)/4) + border + 1, widget->u.text.text, strlen(widget->u.text.text), &font_style); } nsfb_update(root->u.root.fb, &bbox); return 0; }
/** Text button redraw callback. * * Called when a text widget requires redrawing. * * @param widget The widget to be redrawn. * @param cbi The callback parameters. * @return The callback result. */ static int fb_redraw_text_button(fbtk_widget_t *widget, fbtk_callback_info *cbi ) { nsfb_bbox_t bbox; nsfb_bbox_t rect; nsfb_bbox_t line; nsfb_plot_pen_t pen; plot_font_style_t font_style; int fh; int border; fbtk_widget_t *root = fbtk_get_root_widget(widget); fb_text_font_style(widget, &fh, &border, &font_style); pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID; pen.stroke_width = 1; pen.stroke_colour = brighten_colour(widget->bg); fbtk_get_bbox(widget, &bbox); rect = bbox; rect.x1--; rect.y1--; nsfb_claim(root->u.root.fb, &bbox); /* clear background */ if ((widget->bg & 0xFF000000) != 0) { /* transparent polygon filling isnt working so fake it */ nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg); } if (widget->u.text.outline) { line.x0 = rect.x0; line.y0 = rect.y0; line.x1 = rect.x0; line.y1 = rect.y1; nsfb_plot_line(root->u.root.fb, &line, &pen); line.x0 = rect.x0; line.y0 = rect.y0; line.x1 = rect.x1; line.y1 = rect.y0; nsfb_plot_line(root->u.root.fb, &line, &pen); pen.stroke_colour = darken_colour(widget->bg); line.x0 = rect.x0; line.y0 = rect.y1; line.x1 = rect.x1; line.y1 = rect.y1; nsfb_plot_line(root->u.root.fb, &line, &pen); line.x0 = rect.x1; line.y0 = rect.y0; line.x1 = rect.x1; line.y1 = rect.y1; nsfb_plot_line(root->u.root.fb, &line, &pen); } if (widget->u.text.text != NULL) { /* Call the fb text plotting, baseline is 3/4 down the font */ fb_plotters.text(bbox.x0 + border, bbox.y0 + ((fh * 3) / 4) + border, widget->u.text.text, widget->u.text.len, &font_style); } nsfb_update(root->u.root.fb, &bbox); return 0; }
/** Text redraw callback. * * Called when a text widget requires redrawing. * * @param widget The widget to be redrawn. * @param cbi The callback parameters. * @return The callback result. */ static int fb_redraw_text(fbtk_widget_t *widget, fbtk_callback_info *cbi ) { nsfb_bbox_t bbox; nsfb_bbox_t rect; fbtk_widget_t *root; plot_font_style_t font_style; int caret_x, caret_y, caret_h; int fh; int padding; int scroll = 0; bool caret = false; fb_text_font_style(widget, &fh, &padding, &font_style); if (fbtk_get_caret(widget, &caret_x, &caret_y, &caret_h)) { caret = true; } root = fbtk_get_root_widget(widget); fbtk_get_bbox(widget, &bbox); rect = bbox; nsfb_claim(root->u.root.fb, &bbox); /* clear background */ if ((widget->bg & 0xFF000000) != 0) { /* transparent polygon filling isnt working so fake it */ nsfb_plot_rectangle_fill(root->u.root.fb, &bbox, widget->bg); } /* widget can have a single pixel outline border */ if (widget->u.text.outline) { rect.x1--; rect.y1--; nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0x00000000, false, false); } if (widget->u.text.text != NULL) { int x = bbox.x0 + padding; int y = bbox.y0 + ((fh * 3 + 2) / 4) + padding; #ifdef FB_USE_FREETYPE /* Freetype renders text higher */ y += 1; #endif if (caret && widget->width - padding - padding < caret_x) { scroll = (widget->width - padding - padding) - caret_x; x += scroll; } /* Call the fb text plotting, baseline is 3/4 down the font */ fb_plotters.text(x, y, widget->u.text.text, widget->u.text.len, &font_style); } if (caret) { /* This widget has caret, so render it */ nsfb_t *nsfb = fbtk_get_nsfb(widget); nsfb_bbox_t line; nsfb_plot_pen_t pen; line.x0 = bbox.x0 + caret_x + scroll; line.y0 = bbox.y0 + caret_y; line.x1 = bbox.x0 + caret_x + scroll; line.y1 = bbox.y0 + caret_y + caret_h; pen.stroke_type = NFSB_PLOT_OPTYPE_SOLID; pen.stroke_width = 1; pen.stroke_colour = 0xFF0000FF; nsfb_plot_line(nsfb, &line, &pen); } nsfb_update(root->u.root.fb, &bbox); return 0; }
/* exported function documented in fbtk.h */ bool fbtk_event(fbtk_widget_t *root, nsfb_event_t *event, int timeout) { nsfb_bbox_t cloc; bool unused = false; /* is the event available */ bool move_pointer = false; /* whether pointer move events occured */ /* ensure we have the root widget */ root = fbtk_get_root_widget(root); do { if (nsfb_event(root->u.root.fb, event, timeout) == false) { if (move_pointer) fbtk_warp_pointer(root, cloc.x0, cloc.y0, false); return false; } if (move_pointer && event->type != NSFB_EVENT_MOVE_RELATIVE && event->type != NSFB_EVENT_MOVE_ABSOLUTE) { /* Flush the movements */ fbtk_warp_pointer(root, cloc.x0, cloc.y0, false); } else if (!move_pointer && event->type == NSFB_EVENT_MOVE_RELATIVE) { /* Get current pointer coords */ nsfb_cursor_loc_get(root->u.root.fb, &cloc); } switch (event->type) { case NSFB_EVENT_KEY_DOWN: case NSFB_EVENT_KEY_UP: if ((event->value.keycode >= NSFB_KEY_MOUSE_1) && (event->value.keycode <= NSFB_KEY_MOUSE_5)) { fbtk_click(root, event); } else { fbtk_input(root, event); } break; case NSFB_EVENT_CONTROL: unused = true; break; case NSFB_EVENT_MOVE_RELATIVE: /* Consecutive move events are consolidated into a * single pointer warp */ move_pointer = true; cloc.x0 += event->value.vector.x; cloc.y0 += event->value.vector.y; timeout = 0; break; case NSFB_EVENT_MOVE_ABSOLUTE: /* Consecutive move events are consolidated into a * single pointer warp */ move_pointer = true; cloc.x0 = event->value.vector.x; cloc.y0 = event->value.vector.y; timeout = 0; break; case NSFB_EVENT_RESIZE: /* Try to resize framebuffer */ gui_resize(root, event->value.resize.w, event->value.resize.h); break; default: break; } } while (event->type == NSFB_EVENT_MOVE_RELATIVE || event->type == NSFB_EVENT_MOVE_ABSOLUTE); return unused; }
/* exported function documented in fbtk.h */ void fbtk_warp_pointer(fbtk_widget_t *widget, int x, int y, bool relative) { fbtk_widget_t *root; fbtk_widget_t *moved; nsfb_bbox_t cloc; /* ensure we have the root widget */ root = fbtk_get_root_widget(widget); if (relative) { nsfb_cursor_loc_get(root->u.root.fb, &cloc); cloc.x0 += x; cloc.y0 += y; } else { cloc.x0 = x; cloc.y0 = y; } /* ensure cursor location lies within the root widget */ if (cloc.x0 < root->x) cloc.x0 = root->x; if (cloc.x0 >= (root->x + root->width)) cloc.x0 = (root->x + root->width) - 1; if (cloc.y0 < root->y) cloc.y0 = root->y; if (cloc.y0 >= (root->y + root->height)) cloc.y0 = (root->y + root->height) - 1; if (root->u.root.grabbed == NULL) { /* update the pointer cursor */ nsfb_cursor_loc_set(root->u.root.fb, &cloc); moved = fbtk_get_widget_at(root, cloc.x0, cloc.y0); x = fbtk_get_absx(moved); y = fbtk_get_absy(moved); /* post enter and leaving messages */ if (moved != root->u.root.prev) { fbtk_post_callback(root->u.root.prev, FBTK_CBT_POINTERLEAVE); root->u.root.prev = moved; fbtk_post_callback(root->u.root.prev, FBTK_CBT_POINTERENTER); } } else { /* pointer movement has been grabbed by a widget */ moved = root->u.root.grabbed; /* ensure pointer remains within widget boundary */ x = fbtk_get_absx(moved); y = fbtk_get_absy(moved); if (cloc.x0 < x) cloc.x0 = x; if (cloc.y0 < y) cloc.y0 = y; if (cloc.x0 > (x + moved->width)) cloc.x0 = (x + moved->width); if (cloc.y0 > (y + moved->height)) cloc.y0 = (y + moved->height); /* update the pointer cursor */ nsfb_cursor_loc_set(root->u.root.fb, &cloc); } /* post the movement */ fbtk_post_callback(moved, FBTK_CBT_POINTERMOVE, cloc.x0 - x, cloc.y0 - y); }
/** Text redraw callback. * * Called when a text widget requires redrawing. * * @param widget The widget to be redrawn. * @param cbi The callback parameters. * @return The callback result. */ static int fb_redraw_text(fbtk_widget_t *widget, fbtk_callback_info *cbi ) { nsfb_bbox_t bbox; nsfb_bbox_t rect; fbtk_widget_t *root; plot_font_style_t font_style; int fh; int padding; padding = (widget->height * FBTK_WIDGET_PADDING) / 200; root = fbtk_get_root_widget(widget); fbtk_get_bbox(widget, &bbox); rect = bbox; nsfb_claim(root->u.root.fb, &bbox); /* clear background */ if ((widget->bg & 0xFF000000) != 0) { /* transparent polygon filling isnt working so fake it */ nsfb_plot_rectangle_fill(root->u.root.fb, &bbox, widget->bg); } /* widget can have a single pixel outline border */ if (widget->u.text.outline) { rect.x1--; rect.y1--; nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0x00000000, false, false); padding++; } if (widget->u.text.text != NULL) { fh = widget->height - padding - padding; font_style.family = PLOT_FONT_FAMILY_SANS_SERIF; font_style.size = px_to_pt(fh) * FONT_SIZE_SCALE; font_style.weight = 400; font_style.flags = FONTF_NONE; font_style.background = widget->bg; font_style.foreground = widget->fg; FBTK_LOG(("plotting %p at %d,%d %d,%d w/h %d,%d font h %d padding %d", widget, bbox.x0, bbox.y0, bbox.x1, bbox.y1, widget->width, widget->height, fh, padding)); /* Call the fb text plotting, baseline is 3/4 down the * font, somewhere along the way the co-ordinate * system for the baseline is to the "higher value * pixel co-ordinate" due to this the + 1 is neccessary. */ fb_plotters.text(bbox.x0 + padding, bbox.y0 + (((fh * 3) + 3)/4) + padding + 1, widget->u.text.text, strlen(widget->u.text.text), &font_style); } nsfb_update(root->u.root.fb, &bbox); return 0; }