netsurf/framebuffer/fbtk.c

1368 lines
36 KiB
C

/*
* Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
*
* Framebuffer windowing toolkit
*
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
* NetSurf is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* NetSurf 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <libnsfb.h>
#include <libnsfb_plot.h>
#include <libnsfb_plot_util.h>
#include <libnsfb_event.h>
#include <libnsfb_cursor.h>
#include "utils/log.h"
#include "css/css.h"
#include "desktop/browser.h"
#include "desktop/plotters.h"
#include "framebuffer/gui.h"
#include "framebuffer/fbtk.h"
#include "framebuffer/bitmap.h"
#include "framebuffer/image_data.h"
static plot_font_style_t root_style = {
.family = PLOT_FONT_FAMILY_SANS_SERIF,
.size = 11 * FONT_SIZE_SCALE,
.weight = 400,
.flags = FONTF_NONE,
};
enum fbtk_widgettype_e {
FB_WIDGET_TYPE_ROOT = 0,
FB_WIDGET_TYPE_WINDOW,
FB_WIDGET_TYPE_BITMAP,
FB_WIDGET_TYPE_FILL,
FB_WIDGET_TYPE_TEXT,
FB_WIDGET_TYPE_HSCROLL,
FB_WIDGET_TYPE_VSCROLL,
FB_WIDGET_TYPE_USER,
};
typedef struct fbtk_widget_list_s fbtk_widget_list_t;
/* wrapper struct for all widget types */
struct fbtk_widget_s {
/* Generic properties */
int x;
int y;
int width;
int height;
colour bg;
colour fg;
/* handlers */
fbtk_mouseclick_t click;
void *clickpw; /* private data for callback */
fbtk_input_t input;
void *inputpw; /* private data for callback */
fbtk_move_t move;
void *movepw; /* private data for callback */
fbtk_redraw_t redraw;
void *redrawpw; /* private data for callback */
bool redraw_required;
fbtk_widget_t *parent; /* parent widget */
/* Widget specific */
enum fbtk_widgettype_e type;
union {
/* toolkit base handle */
struct {
nsfb_t *fb;
fbtk_widget_t *rootw;
fbtk_widget_t *input;
} root;
/* window */
struct {
/* widgets associated with this window */
fbtk_widget_list_t *widgets; /* begining of list */
fbtk_widget_list_t *widgets_end; /* end of list */
} window;
/* bitmap */
struct {
struct bitmap *bitmap;
} bitmap;
/* text */
struct {
char* text;
bool outline;
fbtk_enter_t enter;
void *pw;
int idx;
} text;
/* application driven widget */
struct {
void *pw; /* private data for user widget */
} user;
struct {
int pos;
int pct;
} scroll;
} u;
};
/* widget list */
struct fbtk_widget_list_s {
struct fbtk_widget_list_s *next;
struct fbtk_widget_list_s *prev;
fbtk_widget_t *widget;
} ;
enum {
POINT_LEFTOF_REGION = 1,
POINT_RIGHTOF_REGION = 2,
POINT_ABOVE_REGION = 4,
POINT_BELOW_REGION = 8,
};
#define REGION(x,y,cx1,cx2,cy1,cy2) \
(( (y) > (cy2) ? POINT_BELOW_REGION : 0) | \
( (y) < (cy1) ? POINT_ABOVE_REGION : 0) | \
( (x) > (cx2) ? POINT_RIGHTOF_REGION : 0) | \
( (x) < (cx1) ? POINT_LEFTOF_REGION : 0) )
#define SWAP(a, b) do { int t; t=(a); (a)=(b); (b)=t; } while(0)
/* clip a rectangle to another rectangle */
bool fbtk_clip_rect(const bbox_t * restrict clip, bbox_t * restrict box)
{
uint8_t region1;
uint8_t region2;
if (box->x1 < box->x0) SWAP(box->x0, box->x1);
if (box->y1 < box->y0) SWAP(box->y0, box->y1);
region1 = REGION(box->x0, box->y0, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1);
region2 = REGION(box->x1, box->y1, clip->x0, clip->x1 - 1, clip->y0, clip->y1 - 1);
/* area lies entirely outside the clipping rectangle */
if ((region1 | region2) && (region1 & region2))
return false;
if (box->x0 < clip->x0)
box->x0 = clip->x0;
if (box->x0 > clip->x1)
box->x0 = clip->x1;
if (box->x1 < clip->x0)
box->x1 = clip->x0;
if (box->x1 > clip->x1)
box->x1 = clip->x1;
if (box->y0 < clip->y0)
box->y0 = clip->y0;
if (box->y0 > clip->y1)
box->y0 = clip->y1;
if (box->y1 < clip->y0)
box->y1 = clip->y0;
if (box->y1 > clip->y1)
box->y1 = clip->y1;
return true;
}
/* clip a rectangle to a widgets area rectangle */
bool fbtk_clip_to_widget(fbtk_widget_t *widget, bbox_t * restrict box)
{
bbox_t wbox;
wbox.x0 = 0;
wbox.y0 = 0;
wbox.x1 = widget->width;
wbox.y1 = widget->height;
return fbtk_clip_rect(&wbox, box);
}
/* creates a new widget of a given type */
static fbtk_widget_t *
new_widget(enum fbtk_widgettype_e type)
{
fbtk_widget_t *neww;
neww = calloc(1, sizeof(fbtk_widget_t));
neww->type = type;
return neww;
}
/* find the root widget from any widget in the toolkits hierarchy */
static fbtk_widget_t *
get_root_widget(fbtk_widget_t *widget)
{
while (widget->parent != NULL)
widget = widget->parent;
/* check root widget was found */
if (widget->type != FB_WIDGET_TYPE_ROOT) {
LOG(("Widget with null parent that is not the root widget!"));
return NULL;
}
return widget;
}
/* set widget to be redrawn */
void
fbtk_request_redraw(fbtk_widget_t *widget)
{
widget->redraw_required = 1;
if (widget->type == FB_WIDGET_TYPE_WINDOW) {
fbtk_widget_list_t *lent = widget->u.window.widgets;
while (lent != NULL) {
lent->widget->redraw_required = 1;
lent = lent->next;
}
}
while (widget->parent != NULL) {
widget = widget->parent;
widget->redraw_required = 1;
}
}
static fbtk_widget_t *
add_widget_to_window(fbtk_widget_t *window, fbtk_widget_t *widget)
{
fbtk_widget_list_t *newent;
fbtk_widget_list_t **nextent;
fbtk_widget_list_t *prevent; /* previous entry pointer */
if (window->type == FB_WIDGET_TYPE_WINDOW) {
/* caller attached widget to a window */
nextent = &window->u.window.widgets;
prevent = NULL;
while (*nextent != NULL) {
prevent = (*nextent);
nextent = &(prevent->next);
}
newent = calloc(1, sizeof(struct fbtk_widget_list_s));
newent->widget = widget;
newent->next = NULL;
newent->prev = prevent;
*nextent = newent;
window->u.window.widgets_end = newent;
}
widget->parent = window;
fbtk_request_redraw(widget);
return widget;
}
static void
remove_widget_from_window(fbtk_widget_t *window, fbtk_widget_t *widget)
{
fbtk_widget_list_t *lent = window->u.window.widgets;
while ((lent != NULL) && (lent->widget != widget)) {
lent = lent->next;
}
if (lent != NULL) {
if (lent->prev == NULL) {
window->u.window.widgets = lent->next;
} else {
lent->prev->next = lent->next;
}
if (lent->next == NULL) {
window->u.window.widgets_end = lent->prev;
} else {
lent->next->prev = lent->prev;
}
free(lent);
}
}
static void
fbtk_redraw_widget(fbtk_widget_t *root, fbtk_widget_t *widget)
{
nsfb_bbox_t saved_plot_ctx;
nsfb_bbox_t plot_ctx;
//LOG(("widget %p type %d", widget, widget->type));
if (widget->redraw_required == false)
return;
widget->redraw_required = false;
/* ensure there is a redraw handler */
if (widget->redraw == NULL)
return;
/* get the current clipping rectangle */
nsfb_plot_get_clip(root->u.root.fb, &saved_plot_ctx);
plot_ctx.x0 = fbtk_get_x(widget);
plot_ctx.y0 = fbtk_get_y(widget);
plot_ctx.x1 = plot_ctx.x0 + widget->width;
plot_ctx.y1 = plot_ctx.y0 + widget->height;
/* clip widget to the current area and redraw if its exposed */
if (nsfb_plot_clip(&saved_plot_ctx, &plot_ctx )) {
nsfb_plot_set_clip(root->u.root.fb, &plot_ctx);
/* do our drawing according to type */
widget->redraw(root, widget, widget->redrawpw);
/* restore clipping rectangle */
nsfb_plot_set_clip(root->u.root.fb, &saved_plot_ctx);
//LOG(("OS redrawing %d,%d %d,%d", fb_plot_ctx.x0, fb_plot_ctx.y0, fb_plot_ctx.x1, fb_plot_ctx.y1));
}
}
/*************** redraw widgets **************/
static int
fb_redraw_fill(fbtk_widget_t *root, fbtk_widget_t *widget, void *pw)
{
nsfb_bbox_t bbox;
fbtk_get_bbox(widget, &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);
}
nsfb_release(root->u.root.fb, &bbox);
return 0;
}
static int
fb_redraw_hscroll(fbtk_widget_t *root, fbtk_widget_t *widget, void *pw)
{
int hscroll;
int hpos;
nsfb_bbox_t bbox;
nsfb_bbox_t rect;
fbtk_get_bbox(widget, &bbox);
nsfb_claim(root->u.root.fb, &bbox);
rect = bbox;
nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
rect.x0 = bbox.x0 + 1;
rect.y0 = bbox.y0 + 3;
rect.x1 = bbox.x1 - 1;
rect.y1 = bbox.y1 - 3;
nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->fg);
rect.x0 = bbox.x0;
rect.y0 = bbox.y0 + 2;
rect.x1 = bbox.x1 - 1;
rect.y1 = bbox.y1 - 5;
nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0xFF000000, false, false);
hscroll = ((widget->width - 4) * widget->u.scroll.pct) / 100 ;
hpos = ((widget->width - 4) * widget->u.scroll.pos) / 100 ;
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_release(root->u.root.fb, &bbox);
return 0;
}
static int
fb_redraw_vscroll(fbtk_widget_t *root, fbtk_widget_t *widget, void *pw)
{
int vscroll;
int vpos;
nsfb_bbox_t bbox;
nsfb_bbox_t rect;
fbtk_get_bbox(widget, &bbox);
nsfb_claim(root->u.root.fb, &bbox);
rect = bbox;
nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
rect.x0 = bbox.x0 + 1;
rect.y0 = bbox.y0 + 3;
rect.x1 = bbox.x1 - 1;
rect.y1 = bbox.y1 - 3;
nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->fg);
rect.x0 = bbox.x0;
rect.y0 = bbox.y0 + 2;
rect.x1 = bbox.x1 - 1;
rect.y1 = bbox.y1 - 5;
nsfb_plot_rectangle(root->u.root.fb, &rect, 1, 0xFF000000, false, false);
vscroll = ((widget->height - 4) * widget->u.scroll.pct) / 100 ;
vpos = ((widget->height - 4) * widget->u.scroll.pos) / 100 ;
LOG(("scroll %d",vscroll));
rect.x0 = bbox.x0 + 3 ;
rect.y0 = bbox.y0 + 5 + vpos;
rect.x1 = bbox.x0 + widget->width - 3;
rect.y1 = bbox.y0 + vscroll + vpos - 5;
nsfb_plot_rectangle_fill(root->u.root.fb, &rect, widget->bg);
nsfb_release(root->u.root.fb, &bbox);
return 0;
}
static int
fb_redraw_bitmap(fbtk_widget_t *root, fbtk_widget_t *widget, void *pw)
{
nsfb_bbox_t bbox;
nsfb_bbox_t rect;
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);
}
/* plot the image */
nsfb_plot_bitmap(root->u.root.fb, &rect, (nsfb_colour_t *)widget->u.bitmap.bitmap->pixdata, widget->u.bitmap.bitmap->width, widget->u.bitmap.bitmap->height, widget->u.bitmap.bitmap->width, !widget->u.bitmap.bitmap->opaque);
nsfb_release(root->u.root.fb, &bbox);
return 0;
}
static int
fbtk_window_default_redraw(fbtk_widget_t *root, fbtk_widget_t *window, void *pw)
{
fbtk_widget_list_t *lent;
int res = 0;
if (!window->redraw)
return res;
/* get the list of widgets */
lent = window->u.window.widgets;
while (lent != NULL) {
fbtk_redraw_widget(root, lent->widget);
lent = lent->next;
}
return res;
}
static int
fbtk_window_default_move(fbtk_widget_t *window, int x, int y, void *pw)
{
fbtk_widget_list_t *lent;
fbtk_widget_t *widget;
int res = 0;
/* get the list of widgets */
lent = window->u.window.widgets_end;
while (lent != NULL) {
widget = lent->widget;
if ((x > widget->x) &&
(y > widget->y) &&
(x < widget->x + widget->width) &&
(y < widget->y + widget->height)) {
if (widget->move != NULL) {
res = widget->move(widget,
x - widget->x,
y - widget->y,
widget->movepw);
}
break;
}
lent = lent->prev;
}
return res;
}
static int
fbtk_window_default_click(fbtk_widget_t *window, nsfb_event_t *event, int x, int y, void *pw)
{
fbtk_widget_list_t *lent;
fbtk_widget_t *widget;
int res = 0;
/* get the list of widgets */
lent = window->u.window.widgets;
while (lent != NULL) {
widget = lent->widget;
if ((x > widget->x) &&
(y > widget->y) &&
(x < widget->x + widget->width) &&
(y < widget->y + widget->height)) {
if (widget->input != NULL) {
fbtk_widget_t *root = get_root_widget(widget);
root->u.root.input = widget;
}
if (widget->click != NULL) {
res = widget->click(widget,
event,
x - widget->x,
y - widget->y,
widget->clickpw);
break;
}
}
lent = lent->next;
}
return res;
}
static int
fb_redraw_text(fbtk_widget_t *root, fbtk_widget_t *widget, void *pw)
{
nsfb_bbox_t bbox;
nsfb_bbox_t rect;
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);
}
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) {
root_style.background = widget->bg;
root_style.foreground = widget->fg;
plot.text(bbox.x0 + 3,
bbox.y0 + 17,
widget->u.text.text,
strlen(widget->u.text.text),
&root_style);
}
nsfb_release(root->u.root.fb, &bbox);
return 0;
}
static int
text_input(fbtk_widget_t *widget, nsfb_event_t *event, void *pw)
{
int value;
if (event == NULL) {
/* gain focus */
if (widget->u.text.text == NULL)
widget->u.text.text = calloc(1,1);
widget->u.text.idx = strlen(widget->u.text.text);
fbtk_request_redraw(widget);
return 0;
}
if (event->type != NSFB_EVENT_KEY_DOWN)
return 0;
value = event->value.keycode;
switch (value) {
case NSFB_KEY_BACKSPACE:
if (widget->u.text.idx <= 0)
break;
widget->u.text.idx--;
widget->u.text.text[widget->u.text.idx] = 0;
break;
case NSFB_KEY_RETURN:
widget->u.text.enter(widget->u.text.pw, widget->u.text.text);
break;
case NSFB_KEY_PAGEUP:
case NSFB_KEY_PAGEDOWN:
case NSFB_KEY_RIGHT:
case NSFB_KEY_LEFT:
case NSFB_KEY_UP:
case NSFB_KEY_DOWN:
case NSFB_KEY_RSHIFT:
case NSFB_KEY_LSHIFT:
/* Not handling any of these correctly yet, but avoid putting
* charcters in the text widget when they're pressed. */
break;
default:
/* allow for new character and null */
widget->u.text.text = realloc(widget->u.text.text, widget->u.text.idx + 2);
widget->u.text.text[widget->u.text.idx] = value;
widget->u.text.text[widget->u.text.idx + 1] = '\0';
widget->u.text.idx++;
break;
}
fbtk_request_redraw(widget);
return 0;
}
/* sets the enter action on a writable icon */
void
fbtk_writable_text(fbtk_widget_t *widget, fbtk_enter_t enter, void *pw)
{
widget->u.text.enter = enter;
widget->u.text.pw = pw;
widget->input = text_input;
widget->inputpw = widget;
}
/********** acessors ***********/
int
fbtk_get_height(fbtk_widget_t *widget)
{
return widget->height;
}
int
fbtk_get_width(fbtk_widget_t *widget)
{
return widget->width;
}
int
fbtk_get_x(fbtk_widget_t *widget)
{
int x = widget->x;
while (widget->parent != NULL) {
widget = widget->parent;
x += widget->x;
}
return x;
}
int
fbtk_get_y(fbtk_widget_t *widget)
{
int y = widget->y;
while (widget->parent != NULL) {
widget = widget->parent;
y += widget->y;
}
return y;
}
/* get widgets bounding box in screen co-ordinates */
bool
fbtk_get_bbox(fbtk_widget_t *widget, nsfb_bbox_t *bbox)
{
bbox->x0 = widget->x;
bbox->y0 = widget->y;
bbox->x1 = widget->x + widget->width;
bbox->y1 = widget->y + widget->height;
while (widget->parent != NULL) {
widget = widget->parent;
bbox->x0 += widget->x;
bbox->y0 += widget->y;
bbox->x1 += widget->x;
bbox->y1 += widget->y;
}
return true;
}
void
fbtk_set_handler_click(fbtk_widget_t *widget, fbtk_mouseclick_t click, void *pw)
{
widget->click = click;
widget->clickpw = pw;
}
void
fbtk_set_handler_input(fbtk_widget_t *widget, fbtk_input_t input, void *pw)
{
widget->input = input;
widget->inputpw = pw;
}
void
fbtk_set_handler_redraw(fbtk_widget_t *widget, fbtk_redraw_t redraw, void *pw)
{
widget->redraw = redraw;
widget->redrawpw = pw;
}
void
fbtk_set_handler_move(fbtk_widget_t *widget, fbtk_move_t move, void *pw)
{
widget->move = move;
widget->movepw = pw;
}
void *
fbtk_get_userpw(fbtk_widget_t *widget)
{
if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_USER))
return NULL;
return widget->u.user.pw;
}
void
fbtk_set_text(fbtk_widget_t *widget, const char *text)
{
if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_TEXT))
return;
if (widget->u.text.text != NULL) {
if (strcmp(widget->u.text.text, text) == 0)
return; /* text is being set to the same thing */
free(widget->u.text.text);
}
widget->u.text.text = strdup(text);
widget->u.text.idx = strlen(text);
fbtk_request_redraw(widget);
}
void
fbtk_set_scroll(fbtk_widget_t *widget, int pct)
{
if (widget == NULL)
return;
if ((widget->type == FB_WIDGET_TYPE_HSCROLL) ||
(widget->type == FB_WIDGET_TYPE_VSCROLL)) {
widget->u.scroll.pct = pct;
fbtk_request_redraw(widget);
}
}
void
fbtk_set_scroll_pos(fbtk_widget_t *widget, int pos)
{
if (widget == NULL)
return;
if ((widget->type == FB_WIDGET_TYPE_HSCROLL) ||
(widget->type == FB_WIDGET_TYPE_VSCROLL)) {
widget->u.scroll.pos = pos;
fbtk_request_redraw(widget);
}
}
void
fbtk_set_bitmap(fbtk_widget_t *widget, struct bitmap *image)
{
if ((widget == NULL) || (widget->type != FB_WIDGET_TYPE_BITMAP))
return;
widget->u.bitmap.bitmap = image;
fbtk_request_redraw(widget);
}
void
fbtk_set_pos_and_size(fbtk_widget_t *widget, int x, int y, int width, int height)
{
if ((widget->x != x) ||
(widget->y != y) ||
(widget->width != width) ||
(widget->height != height)) {
widget->x = x;
widget->y = y;
widget->width = width;
widget->height = height;
fbtk_request_redraw(widget);
LOG(("%d,%d %d,%d",x,y,width,height));
}
}
int
fbtk_count_children(fbtk_widget_t *widget)
{
int count = 0;
fbtk_widget_list_t *lent;
if (widget->type != FB_WIDGET_TYPE_WINDOW) {
if (widget->type != FB_WIDGET_TYPE_ROOT)
return -1;
widget = widget->u.root.rootw;
}
lent = widget->u.window.widgets;
while (lent != NULL) {
count++;
lent = lent->next;
}
return count;
}
void
fbtk_input(fbtk_widget_t *root, nsfb_event_t *event)
{
fbtk_widget_t *input;
root = get_root_widget(root);
/* obtain widget with input focus */
input = root->u.root.input;
if (input == NULL)
return; /* no widget with input */
if (input->input == NULL)
return;
/* call the widgets input method */
input->input(input, event, input->inputpw);
}
void
fbtk_click(fbtk_widget_t *widget, nsfb_event_t *event)
{
fbtk_widget_t *root;
fbtk_widget_t *window;
nsfb_bbox_t cloc;
/* Don't act on press and release, or everything happens twice */
if (event->type == NSFB_EVENT_KEY_UP)
return;
/* ensure we have the root widget */
root = get_root_widget(widget);
nsfb_cursor_loc_get(root->u.root.fb, &cloc);
/* get the root window */
window = root->u.root.rootw;
LOG(("click %d, %d",cloc.x0,cloc.y0));
if (window->click != NULL)
window->click(window, event, cloc.x0, cloc.y0, window->clickpw);
}
void
fbtk_move_pointer(fbtk_widget_t *widget, int x, int y, bool relative)
{
fbtk_widget_t *root;
fbtk_widget_t *window;
nsfb_bbox_t cloc;
/* ensure we have the root widget */
root = 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;
}
root->redraw_required = true;
nsfb_cursor_loc_set(root->u.root.fb, &cloc);
/* get the root window */
window = root->u.root.rootw;
if (window->move != NULL)
window->move(window, cloc.x0, cloc.y0, window->movepw);
}
int
fbtk_redraw(fbtk_widget_t *widget)
{
fbtk_widget_t *root;
/* ensure we have the root widget */
root = get_root_widget(widget);
if (!root->redraw_required)
return 0;
fbtk_redraw_widget(root, root->u.root.rootw);
return 1;
}
/****** widget destruction ********/
int fbtk_destroy_widget(fbtk_widget_t *widget)
{
if (widget->type == FB_WIDGET_TYPE_WINDOW) {
/* TODO: walk child widgets and destroy them */
}
remove_widget_from_window(widget->parent, widget);
free(widget);
return 0;
}
/************** Widget creation *************/
fbtk_widget_t *
fbtk_create_text(fbtk_widget_t *window,
int x, int y,
int width, int height,
colour bg, colour fg,
bool outline)
{
fbtk_widget_t *newt = new_widget(FB_WIDGET_TYPE_TEXT);
newt->x = x;
newt->y = y;
newt->width = width;
newt->height = height;
newt->u.text.outline = outline;
newt->fg = fg;
newt->bg = bg;
newt->redraw = fb_redraw_text;
return add_widget_to_window(window, newt);
}
fbtk_widget_t *
fbtk_create_bitmap(fbtk_widget_t *window, int x, int y, colour c, struct bitmap *image)
{
fbtk_widget_t *newb = new_widget(FB_WIDGET_TYPE_BITMAP);
newb->x = x;
newb->y = y;
newb->width = image->width;
newb->height = image->height;
newb->bg = c;
newb->u.bitmap.bitmap = image;
newb->redraw = fb_redraw_bitmap;
return add_widget_to_window(window, newb);
}
static void
fbtk_width_height(fbtk_widget_t *parent, int x, int y, int *width, int *height)
{
/* make widget fit inside parent */
if (*width == 0) {
*width = parent->width - x;
} else if (*width < 0) {
*width = parent->width + *width;
}
if ((*width + x) > parent->width) {
*width = parent->width - x;
}
if (*height == 0) {
*height = parent->height - y;
} else if (*height < 0) {
*height = parent->height + *height;
}
if ((*height + y) > parent->height) {
*height = parent->height - y;
}
}
fbtk_widget_t *
fbtk_create_fill(fbtk_widget_t *window, int x, int y, int width, int height, colour c)
{
fbtk_widget_t *neww = new_widget(FB_WIDGET_TYPE_FILL);
neww->x = x;
neww->y = y;
neww->width = width;
neww->height = height;
fbtk_width_height(window, x, y, &neww->width, &neww->height);
neww->bg = c;
neww->redraw = fb_redraw_fill;
return add_widget_to_window(window, neww);
}
fbtk_widget_t *
fbtk_create_hscroll(fbtk_widget_t *window, int x, int y, int width, int height, colour fg, colour bg)
{
fbtk_widget_t *neww = new_widget(FB_WIDGET_TYPE_HSCROLL);
neww->x = x;
neww->y = y;
neww->width = width;
neww->height = height;
neww->fg = fg;
neww->bg = bg;
neww->redraw = fb_redraw_hscroll;
return add_widget_to_window(window, neww);
}
fbtk_widget_t *
fbtk_create_vscroll(fbtk_widget_t *window, int x, int y, int width, int height, colour fg, colour bg)
{
fbtk_widget_t *neww = new_widget(FB_WIDGET_TYPE_VSCROLL);
neww->x = x;
neww->y = y;
neww->width = width;
neww->height = height;
neww->fg = fg;
neww->bg = bg;
neww->redraw = fb_redraw_vscroll;
return add_widget_to_window(window, neww);
}
fbtk_widget_t *
fbtk_create_button(fbtk_widget_t *window,
int x, int y,
colour c,
struct bitmap *image,
fbtk_mouseclick_t click,
void *pw)
{
fbtk_widget_t *newb = fbtk_create_bitmap(window, x, y, c, image);
newb->click = click;
newb->clickpw = pw;
return newb;
}
fbtk_widget_t *
fbtk_create_writable_text(fbtk_widget_t *window,
int x, int y,
int width, int height,
colour bg, colour fg,
bool outline,
fbtk_enter_t enter, void *pw)
{
fbtk_widget_t *newt = fbtk_create_text(window, x, y, width, height, bg,fg,outline);
newt->u.text.enter = enter;
newt->u.text.pw = pw;
newt->input = text_input;
newt->inputpw = newt;
return newt;
}
/* create user widget
*
* @param x coord relative to parent
*/
fbtk_widget_t *
fbtk_create_user(fbtk_widget_t *window,
int x, int y,
int width, int height,
void *pw)
{
fbtk_widget_t *newu = new_widget(FB_WIDGET_TYPE_USER);
/* make new window fit inside parent */
if (width == 0) {
width = window->width - x;
} else if (width < 0) {
width = window->width + width;
}
if ((width + x) > window->width) {
width = window->width - x;
}
if (height == 0) {
height = window->height - y;
} else if (height < 0) {
height = window->height + height;
}
if ((height + y) > window->height) {
height = window->height - y;
}
newu->x = x;
newu->y = y;
newu->width = width;
newu->height = height;
newu->u.user.pw = pw;
return add_widget_to_window(window, newu);
}
/* create new window
*
* @param x coord relative to parent
*/
fbtk_widget_t *
fbtk_create_window(fbtk_widget_t *parent,
int x, int y, int width, int height)
{
fbtk_widget_t *newwin;
LOG(("Creating window %p %d,%d %d,%d",parent,x,y,width,height));
if (parent == NULL)
return NULL;
if ((parent->type == FB_WIDGET_TYPE_ROOT) &&
(parent->u.root.rootw != NULL)) {
LOG(("Using root window"));
parent = parent->u.root.rootw;
}
newwin = new_widget(FB_WIDGET_TYPE_WINDOW);
/* make new window fit inside parent */
if (width == 0) {
width = parent->width - x;
} else if (width < 0) {
width = parent->width + width;
}
if ((width + x) > parent->width) {
width = parent->width - x;
}
if (height == 0) {
height = parent->height - y;
} else if (height < 0) {
height = parent->height + height;
}
if ((height + y) > parent->height) {
height = parent->height - y;
}
newwin->x = x;
newwin->y = y;
newwin->width = width;
newwin->height = height;
newwin->redraw = fbtk_window_default_redraw;
newwin->move = fbtk_window_default_move;
newwin->click = fbtk_window_default_click;
LOG(("Created window %p %d,%d %d,%d",newwin,x,y,width,height));
return add_widget_to_window(parent, newwin);
}
bool fbtk_event(fbtk_widget_t *root, nsfb_event_t *event, int timeout)
{
bool unused = false; /* is the event available */
/* ensure we have the root widget */
root = get_root_widget(root);
//LOG(("Reading event with timeout %d",timeout));
if (nsfb_event(root->u.root.fb, event, timeout) == false)
return false;
switch (event->type) {
case NSFB_EVENT_KEY_DOWN:
case NSFB_EVENT_KEY_UP:
if ((event->value.controlcode >= NSFB_KEY_MOUSE_1) &&
(event->value.controlcode <= 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:
fbtk_move_pointer(root, event->value.vector.x, event->value.vector.y, true);
break;
case NSFB_EVENT_MOVE_ABSOLUTE:
fbtk_move_pointer(root, event->value.vector.x, event->value.vector.y, false);
break;
default:
break;
}
return unused;
}
nsfb_t *
fbtk_get_nsfb(fbtk_widget_t *widget)
{
fbtk_widget_t *root;
/* ensure we have the root widget */
root = get_root_widget(widget);
return root->u.root.fb;
}
/* Initialise toolkit for use */
fbtk_widget_t *
fbtk_init(nsfb_t *fb)
{
fbtk_widget_t *root = new_widget(FB_WIDGET_TYPE_ROOT);
nsfb_get_geometry(fb, &root->width, &root->height, NULL);
LOG(("width %d height %d",root->width, root->height));
root->u.root.fb = fb;
root->x = 0;
root->y = 0;
root->u.root.rootw = fbtk_create_window(root, 0, 0, 0, 0);
return root;
}
static int keymap[] = {
/* 0 1 2 3 4 5 6 7 8 9 */
-1, -1, -1, -1, -1, -1, -1, -1, 8, 9, /* 0 - 9 */
-1, -1, -1, 13, -1, -1, -1, -1, -1, -1, /* 10 - 19 */
-1, -1, -1, -1, -1, -1, -1, 27, -1, -1, /* 20 - 29 */
-1, -1, ' ', '!', '"', '#', '$', -1, '&','\'', /* 30 - 39 */
'(', ')', '*', '+', ',', '-', '.', '/', '0', '1', /* 40 - 49 */
'2', '3', '4', '5', '6', '7', '8', '9', ':', ';', /* 50 - 59 */
'<', '=', '>', '?', '@', -1, -1, -1, -1, -1, /* 60 - 69 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */
-1, '[','\\', ']', '~', '_', '`', 'a', 'b', 'c', /* 90 - 99 */
'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', /* 100 - 109 */
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 110 - 119 */
'x', 'y', 'z', -1, -1, -1, -1, -1, -1, -1, /* 120 - 129 */
};
static int sh_keymap[] = {
/* 0 1 2 3 4 5 6 7 8 9 */
-1, -1, -1, -1, -1, -1, -1, -1, 8, 9, /* 0 - 9 */
-1, -1, -1, 13, -1, -1, -1, -1, -1, -1, /* 10 - 19 */
-1, -1, -1, -1, -1, -1, -1, 27, -1, -1, /* 20 - 29 */
-1, -1, ' ', '!', '"', '~', '$', -1, '&', '@', /* 30 - 39 */
'(', ')', '*', '+', '<', '_', '>', '?', ')', '!', /* 40 - 49 */
'"', 243, '$', '%', '^', '&', '*', '(', ';', ':', /* 50 - 59 */
'<', '+', '>', '?', '@', -1, -1, -1, -1, -1, /* 60 - 69 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */
-1, '{', '|', '}', '~', '_', 254, 'A', 'B', 'C', /* 90 - 99 */
'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', /* 100 - 109 */
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 110 - 119 */
'X', 'Y', 'Z', -1, -1, -1, -1, -1, -1, -1, /* 120 - 129 */
};
/* performs character mapping */
int fbtk_keycode_to_ucs4(int code, uint8_t mods)
{
int ucs4 = -1;
if (mods) {
if ((code >= 0) && (code < (int) sizeof(sh_keymap)))
ucs4 = sh_keymap[code];
} else {
if ((code >= 0) && (code < (int) sizeof(keymap)))
ucs4 = keymap[code];
}
return ucs4;
}
/*
* Local Variables:
* c-basic-offset:8
* End:
*/