summaryrefslogtreecommitdiff
path: root/desktop
diff options
context:
space:
mode:
authorOle Loots <ole@monochrom.net>2013-01-31 19:28:26 +0100
committerOle Loots <ole@monochrom.net>2013-01-31 19:28:26 +0100
commit0703404654d2a40cacae2d94c6e0e22ca28b4acf (patch)
treebfdbe1e8952814f73a6b0e190ae0a77fd0cfc7bd /desktop
parent54f719aa960159c5b1978846223732c9669ca7fa (diff)
parent89856f55929907642013e45435b9d18cd1fafe36 (diff)
downloadnetsurf-mono/removing-windom-dependency.tar.gz
netsurf-mono/removing-windom-dependency.tar.bz2
Merge branch 'master' into mono/removing-windom-dependencymono/removing-windom-dependency
Conflicts: atari/toolbar.c
Diffstat (limited to 'desktop')
-rw-r--r--desktop/mouse.h40
-rw-r--r--desktop/textarea.c1069
-rw-r--r--desktop/textarea.h109
-rw-r--r--desktop/tree.c114
4 files changed, 918 insertions, 414 deletions
diff --git a/desktop/mouse.h b/desktop/mouse.h
index 42603a67a..042db23a9 100644
--- a/desktop/mouse.h
+++ b/desktop/mouse.h
@@ -28,8 +28,12 @@
/* Mouse state. 1 is primary mouse button (e.g. Select on RISC OS).
* 2 is secondary mouse button (e.g. Adjust on RISC OS). */
typedef enum {
- BROWSER_MOUSE_PRESS_1 = 1, /* button 1 pressed */
- BROWSER_MOUSE_PRESS_2 = 2, /* button 2 pressed */
+ BROWSER_MOUSE_HOVER = 0, /* No mouse buttons pressed,
+ * May be used to indicate
+ * hover or end of drag. */
+
+ BROWSER_MOUSE_PRESS_1 = (1 << 0), /* button 1 pressed */
+ BROWSER_MOUSE_PRESS_2 = (1 << 1), /* button 2 pressed */
/* note: click meaning is different for
* different front ends. On RISC OS, it
@@ -40,27 +44,29 @@ typedef enum {
* click is fired when the mouse button
* is released, if the operation wasn't
* a drag. */
- BROWSER_MOUSE_CLICK_1 = 4, /* button 1 clicked. */
- BROWSER_MOUSE_CLICK_2 = 8, /* button 2 clicked. */
- BROWSER_MOUSE_DOUBLE_CLICK = 16, /* button 1 double clicked */
+ BROWSER_MOUSE_CLICK_1 = (1 << 2), /* button 1 clicked. */
+ BROWSER_MOUSE_CLICK_2 = (1 << 3), /* button 2 clicked. */
+
+ BROWSER_MOUSE_DOUBLE_CLICK = (1 << 4), /* button 1 double clicked */
- BROWSER_MOUSE_DRAG_1 = 32, /* start of button 1 drag operation */
- BROWSER_MOUSE_DRAG_2 = 64, /* start of button 2 drag operation */
+ BROWSER_MOUSE_DRAG_1 = (1 << 5), /* start of button 1 drag */
+ BROWSER_MOUSE_DRAG_2 = (1 << 6), /* start of button 2 drag */
- BROWSER_MOUSE_DRAG_ON = 128, /* a drag operation was started and
- * a mouse button is still pressed */
+ BROWSER_MOUSE_DRAG_ON = (1 << 7), /* a drag operation was started
+ * and a mouse button is still
+ * pressed */
- BROWSER_MOUSE_HOLDING_1 = 256, /* while button 1 drag is in progress */
- BROWSER_MOUSE_HOLDING_2 = 512, /* while button 2 drag is in progress */
+ BROWSER_MOUSE_HOLDING_1 = (1 << 8), /* during button 1 drag */
+ BROWSER_MOUSE_HOLDING_2 = (1 << 9), /* during button 2 drag */
- BROWSER_MOUSE_MOD_1 = 1024, /* primary modifier key pressed
- * (eg. Shift) */
- BROWSER_MOUSE_MOD_2 = 2048, /* secondary modifier key pressed
- * (eg. Ctrl) */
- BROWSER_MOUSE_MOD_3 = 4096 /* secondary modifier key pressed
- * (eg. Alt) */
+ BROWSER_MOUSE_MOD_1 = (1 << 10), /* 1st modifier key pressed
+ * (eg. Shift) */
+ BROWSER_MOUSE_MOD_2 = (1 << 11), /* 2nd modifier key pressed
+ * (eg. Ctrl) */
+ BROWSER_MOUSE_MOD_3 = (1 << 12) /* 3rd modifier key pressed
+ * (eg. Alt) */
} browser_mouse_state;
diff --git a/desktop/textarea.c b/desktop/textarea.c
index 31effdc3c..2c7b7484d 100644
--- a/desktop/textarea.c
+++ b/desktop/textarea.c
@@ -29,30 +29,13 @@
#include "desktop/textarea.h"
#include "desktop/textinput.h"
#include "desktop/plotters.h"
+#include "desktop/scrollbar.h"
#include "render/font.h"
#include "utils/log.h"
#include "utils/utf8.h"
#include "utils/utils.h"
-#define MARGIN_LEFT 4
-#define MARGIN_RIGHT 4
#define CARET_COLOR 0x0000FF
-/* background color for readonly textarea */
-#define READONLY_BG 0xD9D9D9
-#define BACKGROUND_COL 0xFFFFFF
-#define BORDER_COLOR 0x000000
-#define SELECTION_COL 0xFFDDDD
-
-static plot_style_t pstyle_fill_selection = {
- .fill_type = PLOT_OP_TYPE_SOLID,
- .fill_colour = SELECTION_COL,
-};
-
-static plot_style_t pstyle_stroke_border = {
- .stroke_type = PLOT_OP_TYPE_SOLID,
- .stroke_colour = BORDER_COLOR,
- .stroke_width = 1,
-};
static plot_style_t pstyle_stroke_caret = {
.stroke_type = PLOT_OP_TYPE_SOLID,
@@ -64,47 +47,65 @@ struct line_info {
unsigned int b_start; /**< Byte offset of line start */
unsigned int b_length; /**< Byte length of line */
};
+struct textarea_drag {
+ textarea_drag_type type;
+ union {
+ struct scrollbar* scrollbar;
+ } data;
+};
struct textarea {
- int scroll_x, scroll_y; /**< scroll offsets of the textarea
- * content
- */
+ int scroll_x, scroll_y; /**< scroll offsets for the textarea */
+
+ struct scrollbar *bar_x; /**< Horizontal scroll. */
+ struct scrollbar *bar_y; /**< Vertical scroll. */
unsigned int flags; /**< Textarea flags */
int vis_width; /**< Visible width, in pixels */
int vis_height; /**< Visible height, in pixels */
+ int pad_top; /**< Top padding, inside border, in pixels */
+ int pad_right; /**< Right padding, inside border, in pixels */
+ int pad_bottom; /**< Bottom padding, inside border, in pixels */
+ int pad_left; /**< Left padding, inside border, in pixels */
+
+ int border_width; /**< Border width, in pixels */
+ colour border_col; /**< Border colour */
+
+ plot_font_style_t fstyle; /**< Text style, inc. textarea bg col */
+ plot_font_style_t sel_fstyle; /**< Selected text style */
+
char *text; /**< UTF-8 text */
unsigned int text_alloc; /**< Size of allocated text */
unsigned int text_len; /**< Length of text, in bytes */
unsigned int text_utf8_len; /**< Length of text, in characters
- * without the trailing NUL */
+ * without the trailing NULL */
struct {
int line; /**< Line caret is on */
- int char_off; /**< Character index of caret within the
- * specified line */
+ int char_off; /**< Character index of caret on line */
} caret_pos;
int caret_x; /**< cached X coordinate of the caret */
int caret_y; /**< cached Y coordinate of the caret */
- int sel_start; /**< Character index of sel start(inclusive) */
- int sel_end; /**< Character index of sel end(exclusive) */
-
- plot_font_style_t fstyle; /**< Text style */
+ int sel_start; /**< Character index of sel start (inclusive) */
+ int sel_end; /**< Character index of sel end (exclusive) */
+ int h_extent; /**< Width of content in px */
+ int v_extent; /**< Height of content in px */
int line_count; /**< Count of lines */
#define LINE_CHUNK_SIZE 16
struct line_info *lines; /**< Line info array */
int line_height; /**< Line height obtained from style */
/** Callback function for a redraw request */
- textarea_redraw_request_callback redraw_request;
+ textarea_client_callback callback;
- void *data; /** < Callback data for both callback functions */
+ void *data; /**< Client data for callback */
- int drag_start_char; /**< Character index of drag start */
+ int drag_start_char; /**< Character index of drag start */
+ struct textarea_drag drag_info; /**< Drag information */
};
@@ -161,6 +162,7 @@ static void textarea_normalise_text(struct textarea *ta,
static bool textarea_select(struct textarea *ta, int c_start, int c_end)
{
int swap;
+ struct textarea_msg msg;
/* Ensure start is the beginning of the selection */
if (c_start > c_end) {
@@ -169,10 +171,20 @@ static bool textarea_select(struct textarea *ta, int c_start, int c_end)
c_end = swap;
}
+ if (ta->sel_start == c_start && ta->sel_end == c_end)
+ return true;
+
ta->sel_start = c_start;
ta->sel_end = c_end;
- ta->redraw_request(ta->data, 0, 0, ta->vis_width, ta->vis_height);
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_REDRAW_REQUEST;
+ msg.data.redraw.x0 = 0;
+ msg.data.redraw.y0 = 0;
+ msg.data.redraw.x1 = ta->vis_width;
+ msg.data.redraw.y1 = ta->vis_height;
+
+ ta->callback(ta->data, &msg);
return true;
}
@@ -237,51 +249,79 @@ static bool textarea_select_fragment(struct textarea * ta)
*/
static bool textarea_scroll_visible(struct textarea *ta)
{
- int x0, x1, y0, y1, x, y;
- int index, b_off;
+ int x0, x1, y0, y1; /* area we want caret inside */
+ int xc, yc; /* area centre */
+ int x, y; /* caret pos */
+ int xs = ta->scroll_x;
+ int ys = ta->scroll_y;
bool scrolled = false;
if (ta->caret_pos.char_off == -1)
return false;
- x0 = MARGIN_LEFT;
- x1 = ta->vis_width - MARGIN_RIGHT;
+ x0 = ta->border_width + ta->pad_left;
+ x1 = ta->vis_width - (ta->border_width + ta->pad_right);
y0 = 0;
- y1 = ta->vis_height;
-
- index = textarea_get_caret(ta);
-
- /* find byte offset of caret position */
- for (b_off = 0; index-- > 0;
- b_off = utf8_next(ta->text, ta->text_len, b_off))
- ; /* do nothing */
-
- nsfont.font_width(&ta->fstyle,
- ta->text + ta->lines[ta->caret_pos.line].b_start,
- b_off - ta->lines[ta->caret_pos.line].b_start,
- &x);
-
- /* top-left of caret */
- x += MARGIN_LEFT - ta->scroll_x;
- y = ta->line_height * ta->caret_pos.line - ta->scroll_y;
+ y1 = ta->vis_height - 2 * ta->border_width -
+ ta->pad_top - ta->pad_bottom;
+ xc = (x1 - x0) / 2 + x0;
+ yc = (y1 - y0) / 2 + y0;
+
+ x = ta->caret_x - xs;
+ y = ta->caret_y + ta->line_height / 2 - ys;
+
+ /* horizontal scroll; centre caret */
+ xs += x - xc;
+
+ /* force back into range */
+ if (xs < 0)
+ xs = 0;
+ else if (xs > ta->h_extent - (x1 - x0))
+ xs = ta->h_extent - (x1 - x0);
+
+ /* If scrolled, set new pos. */
+ if (xs != ta->scroll_x && ta->bar_x != NULL) {
+ scrollbar_set(ta->bar_x, xs, false);
+ xs = scrollbar_get_offset(ta->bar_x);
+ if (xs != ta->scroll_x) {
+ ta->scroll_x = xs;
+ scrolled = true;
+ }
- /* check and change vertical scroll */
- if (y < y0) {
- ta->scroll_y -= y0 - y;
+ } else if (ta->flags & TEXTAREA_MULTILINE && ta->bar_x == NULL &&
+ ta->scroll_x != 0) {
+ ta->scroll_x = 0;
scrolled = true;
- } else if (y + ta->line_height > y1) {
- ta->scroll_y += y + ta->line_height - y1;
+
+ } else if (xs != ta->scroll_x && !(ta->flags & TEXTAREA_MULTILINE)) {
+ ta->scroll_x = xs;
scrolled = true;
}
+ /* check and change vertical scroll */
+ if (ta->flags & TEXTAREA_MULTILINE) {
+ /* vertical scroll; centre caret */
+ ys += y - yc;
+
+ /* force back into range */
+ if (ys < 0)
+ ys = 0;
+ else if (ys > ta->v_extent - (y1 - y0))
+ ys = ta->v_extent - (y1 - y0);
+
+ /* If scrolled, set new pos. */
+ if (ys != ta->scroll_y && ta->bar_y != NULL) {
+ scrollbar_set(ta->bar_y, ys, false);
+ ys = scrollbar_get_offset(ta->bar_y);
+ if (ys != ta->scroll_y) {
+ ta->scroll_y = ys;
+ scrolled = true;
+ }
- /* check and change horizontal scroll */
- if (x < x0) {
- ta->scroll_x -= x0 - x ;
- scrolled = true;
- } else if (x > x1 - 1) {
- ta->scroll_x += x - (x1 - 1);
- scrolled = true;
+ } else if (ta->bar_y == NULL && ta->scroll_y != 0) {
+ ta->scroll_y = 0;
+ scrolled = true;
+ }
}
return scrolled;
@@ -289,25 +329,80 @@ static bool textarea_scroll_visible(struct textarea *ta)
/**
+ * Callback for scrollbar widget.
+ */
+static void textarea_scrollbar_callback(void *client_data,
+ struct scrollbar_msg_data *scrollbar_data)
+{
+ struct textarea *ta = client_data;
+ struct textarea_msg msg;
+
+ switch(scrollbar_data->msg) {
+ case SCROLLBAR_MSG_MOVED:
+ /* Scrolled; redraw everything */
+ ta->scroll_x = scrollbar_get_offset(ta->bar_x);
+ ta->scroll_y = scrollbar_get_offset(ta->bar_y);
+
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_REDRAW_REQUEST;
+ msg.data.redraw.x0 = 0;
+ msg.data.redraw.y0 = 0;
+ msg.data.redraw.x1 = ta->vis_width;
+ msg.data.redraw.y1 = ta->vis_height;
+
+ ta->callback(ta->data, &msg);
+ break;
+
+ case SCROLLBAR_MSG_SCROLL_START:
+ ta->drag_info.type = TEXTAREA_DRAG_SCROLLBAR;
+ ta->drag_info.data.scrollbar = scrollbar_data->scrollbar;
+
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_DRAG_REPORT;
+ msg.data.drag = ta->drag_info.type;
+
+ /* Tell client we're handling a drag */
+ ta->callback(ta->data, &msg);
+ break;
+
+ case SCROLLBAR_MSG_SCROLL_FINISHED:
+ ta->drag_info.type = TEXTAREA_DRAG_NONE;
+
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_DRAG_REPORT;
+ msg.data.drag = ta->drag_info.type;
+
+ /* Tell client we finished handling the drag */
+ ta->callback(ta->data, &msg);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+
+/**
* Reflow a text area from the given line onwards
*
* \param ta Text area to reflow
- * \param line Line number to begin reflow on
+ * \param start Line number to begin reflow on
* \return true on success false otherwise
*/
-static bool textarea_reflow(struct textarea *ta, unsigned int line)
+static bool textarea_reflow(struct textarea *ta, unsigned int start)
{
char *text;
unsigned int len;
size_t b_off;
int x;
- char *space;
- unsigned int line_count = 0;
-
- /** \todo pay attention to line parameter */
- /** \todo create horizontal scrollbar if needed */
-
- ta->line_count = 0;
+ char *space, *para_end;
+ unsigned int line; /* line count */
+ unsigned int scroll_lines;
+ int avail_width;
+ int h_extent; /* horizontal extent */
+ int v_extent; /* vertical extent */
+ bool restart;
if (ta->lines == NULL) {
ta->lines =
@@ -320,71 +415,190 @@ static bool textarea_reflow(struct textarea *ta, unsigned int line)
if (!(ta->flags & TEXTAREA_MULTILINE)) {
/* Single line */
- ta->lines[line_count].b_start = 0;
- ta->lines[line_count++].b_length = ta->text_len - 1;
+ int w = ta->vis_width - 2 * ta->border_width -
+ ta->pad_left - ta->pad_right;
+ ta->lines[0].b_start = 0;
+ ta->lines[0].b_length = ta->text_len - 1;
- ta->line_count = line_count;
+ nsfont.font_width(&ta->fstyle, ta->text, ta->text_len, &x);
+ if (x > w)
+ w = x;
+ ta->h_extent = w + ta->pad_left - ta->pad_right;
+ ta->line_count = 1;
return true;
}
- for (len = ta->text_len - 1, text = ta->text; len > 0;
- len -= b_off, text += b_off) {
+ /* Find max number of lines before vertical scrollbar is required */
+ scroll_lines = (ta->vis_height - 2 * ta->border_width -
+ ta->pad_top - ta->pad_bottom) /
+ ta->line_height;
+
+ if ((signed)start > ta->line_count)
+ start = 0;
+ /** \todo pay attention to start param, for now force start at zero */
+ start = 0;
+
+ do {
+ /* Set line count to start point */
+ line = start;
+
+ /* Find available width */
+ avail_width = ta->vis_width - 2 * ta->border_width -
+ ta->pad_left - ta->pad_right;
+ if (avail_width < 0)
+ avail_width = 0;
+ h_extent = avail_width;
+
+ restart = false;
+ for (len = ta->text_len - 1, text = ta->text; len > 0;
+ len -= b_off, text += b_off) {
+
+ /* Find end of paragraph */
+ for (para_end = text; para_end < text + len;
+ para_end++) {
+ if (*para_end == '\n')
+ break;
+ }
- nsfont.font_split(&ta->fstyle, text, len,
- ta->vis_width - MARGIN_LEFT - MARGIN_RIGHT,
- &b_off, &x);
+ /* Wrap current line in paragraph */
+ nsfont.font_split(&ta->fstyle, text, para_end - text,
+ avail_width, &b_off, &x);
+ /* b_off now marks space, or end of paragraph */
- if (line_count > 0 && line_count % LINE_CHUNK_SIZE == 0) {
- struct line_info *temp = realloc(ta->lines,
- (line_count + LINE_CHUNK_SIZE) *
- sizeof(struct line_info));
- if (temp == NULL) {
- LOG(("realloc failed"));
- return false;
+ if (x > h_extent) {
+ h_extent = x;
+ }
+ if (x > avail_width && ta->bar_x == NULL) {
+ /* We need to insert a horizontal scrollbar */
+ int w = ta->vis_width - 2 * ta->border_width;
+ if (!scrollbar_create(true, w, w, w,
+ ta, textarea_scrollbar_callback,
+ &(ta->bar_x)))
+ return false;
+ if (ta->bar_y != NULL)
+ scrollbar_make_pair(ta->bar_x,
+ ta->bar_y);
+ ta->pad_bottom += SCROLLBAR_WIDTH;
+
+ /* Find new max visible lines */
+ scroll_lines = (ta->vis_height -
+ 2 * ta->border_width -
+ ta->pad_top - ta->pad_bottom) /
+ ta->line_height;
}
- ta->lines = temp;
- }
+ if (line > 0 && line % LINE_CHUNK_SIZE == 0) {
+ struct line_info *temp = realloc(ta->lines,
+ (line + LINE_CHUNK_SIZE) *
+ sizeof(struct line_info));
+ if (temp == NULL) {
+ LOG(("realloc failed"));
+ return false;
+ }
- /* handle LF */
- for (space = text; space <= text + b_off; space++) {
- if (*space == '\n')
- break;
- }
+ ta->lines = temp;
+ }
+
+ if (para_end == text + b_off && *para_end == '\n') {
+ /* Not found any spaces to wrap at, and we
+ * have a newline char */
+ ta->lines[line].b_start = text - ta->text;
+ ta->lines[line++].b_length = para_end - text;
+
+ /* Jump newline */
+ b_off++;
+
+ if (len - b_off == 0) {
+ /* reached end of input;
+ * add last line */
+ ta->lines[line].b_start =
+ text + b_off - ta->text;
+ ta->lines[line++].b_length = 0;
+ }
- if (space <= text + b_off) {
- /* Found newline; use it */
- ta->lines[line_count].b_start = text - ta->text;
- ta->lines[line_count++].b_length = space - text;
+ if (line > scroll_lines && ta->bar_y == NULL)
+ break;
- b_off = space + 1 - text;
+ continue;
- if (len - b_off == 0) {
- /* reached end of input => add last line */
- ta->lines[line_count].b_start =
- text + b_off - ta->text;
- ta->lines[line_count++].b_length = 0;
+ } else if (len - b_off > 0) {
+ /* soft wraped, find last space (if any) */
+ for (space = text + b_off; space > text;
+ space--) {
+ if (*space == ' ')
+ break;
+ }
+
+ if (space != text)
+ b_off = space + 1 - text;
}
- continue;
+ ta->lines[line].b_start = text - ta->text;
+ ta->lines[line++].b_length = b_off;
+
+ if (line > scroll_lines && ta->bar_y == NULL)
+ break;
}
- if (len - b_off > 0) {
- /* find last space (if any) */
- for (space = text + b_off; space > text; space--)
- if (*space == ' ')
- break;
+ if (h_extent <= avail_width && ta->bar_x != NULL) {
+ /* We need to remove a horizontal scrollbar */
+ scrollbar_destroy(ta->bar_x);
+ ta->bar_x = NULL;
+ ta->pad_bottom -= SCROLLBAR_WIDTH;
- if (space != text)
- b_off = space + 1 - text;
+ /* Find new max visible lines */
+ scroll_lines = (ta->vis_height - 2 * ta->border_width -
+ ta->pad_top - ta->pad_bottom) /
+ ta->line_height;
}
- ta->lines[line_count].b_start = text - ta->text;
- ta->lines[line_count++].b_length = b_off;
+ if (line > scroll_lines && ta->bar_y == NULL) {
+ /* Add vertical scrollbar */
+ int h = ta->vis_height - 2 * ta->border_width;
+ if (!scrollbar_create(false, h, h, h,
+ ta, textarea_scrollbar_callback,
+ &(ta->bar_y)))
+ return false;
+ if (ta->bar_x != NULL)
+ scrollbar_make_pair(ta->bar_x,
+ ta->bar_y);
+ ta->pad_right += SCROLLBAR_WIDTH;
+ restart = true;
+
+ } else if (line <= scroll_lines && ta->bar_y != NULL) {
+ /* Remove vertical scrollbar */
+ scrollbar_destroy(ta->bar_y);
+ ta->bar_y = NULL;
+ ta->pad_right -= SCROLLBAR_WIDTH;
+ restart = true;
+ }
+ } while (restart);
+
+ h_extent += ta->pad_left + ta->pad_right -
+ (ta->bar_y != NULL ? SCROLLBAR_WIDTH : 0);
+ v_extent = line * ta->line_height + ta->pad_top +
+ ta->pad_bottom -
+ (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0);
+
+ if (ta->bar_x != NULL) {
+ /* Set horizontal scrollbar extents */
+ int w = ta->vis_width - 2 * ta->border_width -
+ (ta->bar_y != NULL ? SCROLLBAR_WIDTH : 0);
+ scrollbar_set_extents(ta->bar_x, w, w, h_extent);
}
- ta->line_count = line_count;
+ if (ta->bar_y != NULL) {
+ /* Set vertical scrollbar extents */
+ int h = ta->vis_height - 2 * ta->border_width;
+ scrollbar_set_extents(ta->bar_y, h,
+ h - (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0),
+ v_extent);
+ }
+
+ ta->h_extent = h_extent;
+ ta->v_extent = v_extent;
+ ta->line_count = line;
return true;
}
@@ -411,8 +625,8 @@ static void textarea_get_xy_offset(struct textarea *ta, int x, int y,
return;
}
- x = x - MARGIN_LEFT + ta->scroll_x;
- y = y + ta->scroll_y;
+ x = x - ta->border_width - ta->pad_left + ta->scroll_x;
+ y = y - ta->border_width - ta->pad_top + ta->scroll_y;
if (x < 0)
x = 0;
@@ -602,7 +816,7 @@ static bool textarea_replace_text(struct textarea *ta, unsigned int start,
/* Insert new text */
memcpy(ta->text + b_start, rep, rep_len);
- ta->text_len += rep_len - (b_end - b_start);
+ ta->text_len += (int)rep_len - (b_end - b_start);
ta->text_utf8_len = utf8_length(ta->text);
textarea_normalise_text(ta, b_start, rep_len);
@@ -611,16 +825,80 @@ static bool textarea_replace_text(struct textarea *ta, unsigned int start,
}
+/**
+ * Handles the end of a drag operation
+ *
+ * \param ta Text area
+ * \param mouse the mouse state at drag end moment
+ * \param x X coordinate
+ * \param y Y coordinate
+ * \return true if drag end was handled false otherwise
+ */
+static bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse,
+ int x, int y)
+{
+ int c_end;
+ size_t b_off;
+ unsigned int c_off;
+ struct textarea_msg msg;
+
+ assert(ta->drag_info.type != TEXTAREA_DRAG_NONE);
+
+ switch (ta->drag_info.type) {
+ case TEXTAREA_DRAG_SCROLLBAR:
+ if (ta->drag_info.data.scrollbar == ta->bar_x) {
+ x -= ta->border_width;
+ y -= ta->vis_height - ta->border_width -
+ SCROLLBAR_WIDTH;
+ } else {
+ x -= ta->vis_width - ta->border_width -
+ SCROLLBAR_WIDTH;
+ y -= ta->border_width;
+ }
+ scrollbar_mouse_drag_end(ta->drag_info.data.scrollbar,
+ mouse, x, y);
+ assert(ta->drag_info.type == TEXTAREA_DRAG_NONE);
+
+ /* Return, since drag end already reported to textarea client */
+ return true;
+
+ case TEXTAREA_DRAG_SELECTION:
+ ta->drag_info.type = TEXTAREA_DRAG_NONE;
+
+ textarea_get_xy_offset(ta, x, y, &b_off, &c_off);
+ c_end = c_off;
+
+ if (!textarea_select(ta, ta->drag_start_char, c_end))
+ return false;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ /* Report drag end to client, if not already reported */
+ assert(ta->drag_info.type == TEXTAREA_DRAG_NONE);
+
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_DRAG_REPORT;
+ msg.data.drag = ta->drag_info.type;
+
+ ta->callback(ta->data, &msg);
+
+ return true;
+}
+
+
/* exported interface, documented in textarea.h */
-struct textarea *textarea_create(int width, int height,
- textarea_flags flags, const plot_font_style_t *style,
- textarea_redraw_request_callback redraw_request, void *data)
+struct textarea *textarea_create(const textarea_setup *setup,
+ textarea_client_callback callback, void *data)
{
struct textarea *ret;
- if (redraw_request == NULL) {
+ if (callback == NULL) {
LOG(("no callback provided"));
return NULL;
}
@@ -631,16 +909,35 @@ struct textarea *textarea_create(int width, int height,
return NULL;
}
- ret->redraw_request = redraw_request;
+ ret->callback = callback;
ret->data = data;
- ret->vis_width = width;
- ret->vis_height = height;
+
+ ret->flags = setup->flags;
+ ret->vis_width = setup->width;
+ ret->vis_height = setup->height;
+
+ ret->pad_top = setup->pad_top;
+ ret->pad_right = setup->pad_right;
+ ret->pad_bottom = setup->pad_bottom;
+ ret->pad_left = setup->pad_left;
+
+ ret->border_width = setup->border_width;
+ ret->border_col = setup->border_col;
+
+ ret->fstyle = setup->text;
+
+ ret->sel_fstyle = setup->text;
+ ret->sel_fstyle.foreground = setup->selected_text;
+ ret->sel_fstyle.background = setup->selected_bg;
+
ret->scroll_x = 0;
ret->scroll_y = 0;
+ ret->bar_x = NULL;
+ ret->bar_y = NULL;
ret->drag_start_char = 0;
+ ret->drag_info.type = TEXTAREA_DRAG_NONE;
- ret->flags = flags;
ret->text = malloc(64);
if (ret->text == NULL) {
LOG(("malloc failed"));
@@ -652,14 +949,13 @@ struct textarea *textarea_create(int width, int height,
ret->text_len = 1;
ret->text_utf8_len = 0;
- ret->fstyle = *style;
-
ret->line_height = FIXTOINT(FDIV((FMUL(FLTTOFIX(1.2),
FMUL(nscss_screen_dpi,
- INTTOFIX((style->size / FONT_SIZE_SCALE))))), F_72));
+ INTTOFIX((setup->text.size /
+ FONT_SIZE_SCALE))))), F_72));
- ret->caret_pos.line = ret->caret_pos.char_off = 0;
- ret->caret_x = MARGIN_LEFT;
+ ret->caret_pos.line = ret->caret_pos.char_off = -1;
+ ret->caret_x = 0;
ret->caret_y = 0;
ret->sel_start = -1;
ret->sel_end = -1;
@@ -674,6 +970,10 @@ struct textarea *textarea_create(int width, int height,
/* exported interface, documented in textarea.h */
void textarea_destroy(struct textarea *ta)
{
+ if (ta->bar_x)
+ scrollbar_destroy(ta->bar_x);
+ if (ta->bar_y)
+ scrollbar_destroy(ta->bar_y);
free(ta->text);
free(ta->lines);
free(ta);
@@ -738,6 +1038,7 @@ bool textarea_set_caret(struct textarea *ta, int caret)
int x0, y0, x1, y1;
int text_y_offset;
int width, height;
+ struct textarea_msg msg;
if (ta->flags & TEXTAREA_READONLY)
return true;
@@ -749,48 +1050,28 @@ bool textarea_set_caret(struct textarea *ta, int caret)
if (ta->flags & TEXTAREA_MULTILINE) {
/* Multiline textarea */
- text_y_offset = 0;
+ text_y_offset = ta->border_width + ta->pad_top;
} else {
/* Single line text area; text is vertically centered */
- text_y_offset = (ta->vis_height - ta->line_height + 1) / 2;
+ text_y_offset = ta->border_width;
+ text_y_offset += (ta->vis_height - ta->line_height + 1) / 2;
}
/* Delete the old caret */
if (ta->caret_pos.char_off != -1) {
- index = textarea_get_caret(ta);
- if (index == -1)
- return false;
-
- /* the redraw might happen in response to a text-change and
- the caret position might be beyond the current text */
- if ((unsigned)index > c_len)
- index = c_len;
-
- /* find byte offset of caret position */
- for (b_off = 0; index-- > 0;
- b_off = utf8_next(ta->text,
- ta->text_len, b_off))
- ; /* do nothing */
-
- nsfont.font_width(&ta->fstyle,
- ta->text +
- ta->lines[ta->caret_pos.line].b_start,
- b_off - ta->lines[ta->caret_pos.line].b_start,
- &x);
-
- x += MARGIN_LEFT - ta->scroll_x;
-
- y = ta->line_height * ta->caret_pos.line - ta->scroll_y;
-
- /* set the caret coordinate beyond the redraw rectangle */
- ta->caret_x = x - 2;
-
- x0 = x - 1;
- y0 = y + text_y_offset;
+ x0 = ta->caret_x - ta->scroll_x;
+ y0 = ta->caret_y + text_y_offset - ta->scroll_y;
width = 2;
height = ta->line_height;
- ta->redraw_request(ta->data, x0, y0, width, height);
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_REDRAW_REQUEST;
+ msg.data.redraw.x0 = x0;
+ msg.data.redraw.y0 = y0;
+ msg.data.redraw.x1 = x0 + width;
+ msg.data.redraw.y1 = y0 + height;
+
+ ta->callback(ta->data, &msg);
}
/* check if the caret has to be drawn at all */
@@ -831,19 +1112,18 @@ bool textarea_set_caret(struct textarea *ta, int caret)
b_off - ta->lines[ta->caret_pos.line].b_start,
&x);
- x += MARGIN_LEFT - ta->scroll_x;
+ x += ta->border_width + ta->pad_left;
ta->caret_x = x;
- y = ta->line_height * ta->caret_pos.line - ta->scroll_y;
+ y = ta->line_height * ta->caret_pos.line;
ta->caret_y = y;
- if (textarea_scroll_visible(ta)) {
- ta->redraw_request(ta->data, 0, 0,
- ta->vis_width,
- ta->vis_height);
- } else {
- x0 = max(x - 1, MARGIN_LEFT);
+ if (!textarea_scroll_visible(ta)) {
+ /* No scroll, just caret moved, redraw it */
+ x -= ta->scroll_x;
+ y -= ta->scroll_y;
+ x0 = max(x - 1, ta->border_width);
y0 = max(y + text_y_offset, 0);
- x1 = min(x + 1, ta->vis_width - MARGIN_RIGHT);
+ x1 = min(x + 1, ta->vis_width - ta->border_width);
y1 = min(y + ta->line_height + text_y_offset,
ta->vis_height);
@@ -851,8 +1131,14 @@ bool textarea_set_caret(struct textarea *ta, int caret)
height = y1 - y0;
if (width > 0 && height > 0) {
- ta->redraw_request(ta->data, x0, y0,
- width, height);
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_REDRAW_REQUEST;
+ msg.data.redraw.x0 = x0;
+ msg.data.redraw.y0 = y0;
+ msg.data.redraw.x1 = x0 + width;
+ msg.data.redraw.y1 = y0 + height;
+
+ ta->callback(ta->data, &msg);
}
}
}
@@ -866,6 +1152,9 @@ int textarea_get_caret(struct textarea *ta)
{
unsigned int c_off = 0, b_off;
+ /* Ensure caret isn't hidden */
+ if (ta->caret_pos.char_off < 0)
+ textarea_set_caret(ta, 0);
/* if the text is a trailing NUL only */
if (ta->text_utf8_len == 0)
@@ -881,19 +1170,26 @@ int textarea_get_caret(struct textarea *ta)
/* exported interface, documented in textarea.h */
-void textarea_redraw(struct textarea *ta, int x, int y,
+void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
const struct rect *clip, const struct redraw_context *ctx)
{
+ struct textarea_msg msg;
const struct plotter_table *plot = ctx->plot;
- int line0, line1, line;
- int chars, offset, text_y_offset, text_y_offset_baseline;
- unsigned int c_pos, c_len, b_start, b_end, line_len;
+ int line0, line1, line, left, right;
+ int chars, text_y_offset, text_y_offset_baseline;
+ unsigned int c_pos, c_len, c_len_part, b_start, b_end, line_len;
+ unsigned int sel_start, sel_end;
char *line_text;
- struct rect r;
- plot_style_t plot_style_fill_bg = {
- .fill_type = PLOT_OP_TYPE_SOLID,
- .fill_colour = BACKGROUND_COL,
- };
+ struct rect r, s;
+ bool selected = false;
+ plot_font_style_t *fstyle;
+ plot_style_t plot_style_fill_bg = {
+ .stroke_type = PLOT_OP_TYPE_NONE,
+ .stroke_width = 0,
+ .stroke_colour = NS_TRANSPARENT,
+ .fill_type = PLOT_OP_TYPE_SOLID,
+ .fill_colour = ta->border_col
+ };
r = *clip;
@@ -906,9 +1202,6 @@ void textarea_redraw(struct textarea *ta, int x, int y,
/* Nothing to redraw */
return;
- if (ta->flags & TEXTAREA_READONLY)
- plot_style_fill_bg.fill_colour = READONLY_BG;
-
line0 = (r.y0 - y + ta->scroll_y) / ta->line_height - 1;
line1 = (r.y1 - y + ta->scroll_y) / ta->line_height + 1;
@@ -933,145 +1226,213 @@ void textarea_redraw(struct textarea *ta, int x, int y,
r.y1 = y + ta->vis_height;
plot->clip(&r);
- plot->rectangle(r.x0, r.y0, r.x1, r.y1, &plot_style_fill_bg);
- plot->rectangle(x, y,
- x + ta->vis_width - 1, y + ta->vis_height - 1,
- &pstyle_stroke_border);
-
- if (r.x0 < x + MARGIN_LEFT)
- r.x0 = x + MARGIN_LEFT;
- if (r.x1 > x + ta->vis_width - MARGIN_RIGHT)
- r.x1 = x + ta->vis_width - MARGIN_RIGHT;
- plot->clip(&r);
+ if (ta->border_col != NS_TRANSPARENT &&
+ ta->border_width > 0) {
+ /* Plot border */
+ plot->rectangle(x, y, x + ta->vis_width, y + ta->vis_height,
+ &plot_style_fill_bg);
+ }
+ if (ta->fstyle.background != NS_TRANSPARENT) {
+ /* Plot background */
+ plot_style_fill_bg.fill_colour = ta->fstyle.background;
+ plot->rectangle(x + ta->border_width, y + ta->border_width,
+ x + ta->vis_width - ta->border_width,
+ y + ta->vis_height - ta->border_width,
+ &plot_style_fill_bg);
+ }
+
+ if (r.x0 < x + ta->border_width)
+ r.x0 = x + ta->border_width;
+ if (r.x1 > x + ta->vis_width - ta->border_width)
+ r.x1 = x + ta->vis_width - ta->border_width;
+ if (r.y0 < y + ta->border_width)
+ r.y0 = y + ta->border_width;
+ if (r.y1 > y + ta->vis_height - ta->border_width -
+ (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0))
+ r.y1 = y + ta->vis_height - ta->border_width -
+ (ta->bar_x != NULL ? SCROLLBAR_WIDTH : 0);
if (line0 > 0)
- c_pos = utf8_bounded_length(ta->text,
- ta->lines[line0].b_start - 1);
+ c_pos = utf8_bounded_length(ta->text, ta->lines[line0].b_start);
else
c_pos = 0;
+ text_y_offset = text_y_offset_baseline = ta->border_width;
if (ta->flags & TEXTAREA_MULTILINE) {
/* Multiline textarea */
- text_y_offset = 0;
- text_y_offset_baseline = (ta->line_height * 3 + 2) / 4;
+ text_y_offset += ta->pad_top;
+ text_y_offset_baseline += (ta->line_height * 3 + 2) / 4 +
+ ta->pad_top;
} else {
/* Single line text area; text is vertically centered */
- text_y_offset = (ta->vis_height - ta->line_height + 1) / 2;
- text_y_offset_baseline = (ta->vis_height * 3 + 2) / 4;
+ int vis_height = ta->vis_height - 2 * ta->border_width;
+ text_y_offset += (vis_height - ta->line_height + 1) / 2;
+ text_y_offset_baseline += (vis_height * 3 + 2) / 4;
}
+ plot_style_fill_bg.fill_colour = ta->sel_fstyle.background;
+
for (line = line0; (line <= line1) &&
(y + line * ta->line_height <= r.y1 + ta->scroll_y);
line++) {
if (ta->lines[line].b_length == 0)
continue;
+ /* reset clip rectangle */
+ plot->clip(&r);
+
c_len = utf8_bounded_length(
&(ta->text[ta->lines[line].b_start]),
ta->lines[line].b_length);
- /* if there is a newline between the lines count it too */
- if (line < ta->line_count - 1 && ta->lines[line + 1].b_start !=
- ta->lines[line].b_start +
- ta->lines[line].b_length)
- c_len++;
-
- /* check if a part of the line is selected, won't happen if no
- selection (ta->sel_end = -1) */
- if (ta->sel_end != -1 &&
- c_pos < (unsigned)ta->sel_end &&
- c_pos + c_len > (unsigned)ta->sel_start) {
+ b_end = 0;
+ right = x + ta->border_width + ta->pad_left - ta->scroll_x;
+
+ do {
+ sel_start = ta->sel_start;
+ sel_end = ta->sel_end;
+ /* get length of part of line */
+ if (ta->sel_end == -1 || ta->sel_end == ta->sel_start ||
+ sel_end <= c_pos ||
+ sel_start > c_pos + c_len) {
+ /* rest of line unselected */
+ selected = false;
+ c_len_part = c_len;
+ fstyle = &ta->fstyle;
+
+ } else if (sel_start <= c_pos &&
+ sel_end > c_pos + c_len) {
+ /* rest of line selected */
+ selected = true;
+ c_len_part = c_len;
+ fstyle = &ta->sel_fstyle;
+
+ } else if (sel_start > c_pos) {
+ /* next part of line unselected */
+ selected = false;
+ c_len_part = sel_start - c_pos;
+ fstyle = &ta->fstyle;
+
+ } else if (sel_end > c_pos) {
+ /* next part of line selected */
+ selected = true;
+ c_len_part = sel_end - c_pos;
+ fstyle = &ta->sel_fstyle;
- /* offset from the beginning of the line */
- offset = ta->sel_start - c_pos;
- chars = ta->sel_end - c_pos -
- (offset > 0 ? offset:0);
+ } else {
+ assert(0);
+ }
line_text = &(ta->text[ta->lines[line].b_start]);
line_len = ta->lines[line].b_length;
- if (offset > 0) {
-
- /* find byte start of the selected part */
- for (b_start = 0; offset > 0; offset--)
- b_start = utf8_next(line_text,
- line_len,
- b_start);
- nsfont.font_width(&ta->fstyle, line_text,
- b_start, &r.x0);
- r.x0 += x + MARGIN_LEFT;
- }
- else {
- r.x0 = x + MARGIN_LEFT;
- b_start = 0;
+ /* find b_start and b_end for this part of the line */
+ b_start = b_end;
+
+ chars = c_len_part;
+ for (b_end = b_start; chars > 0; chars--)
+ b_end = utf8_next(line_text, line_len, b_end);
+
+ /* find clip left/right for this part of line */
+ left = right;
+ nsfont.font_width(&ta->fstyle, line_text,
+ b_end, &right);
+ right += x + ta->border_width + ta->pad_left -
+ ta->scroll_x;
+
+ /* set clip rectangle for line part */
+ s = r;
+ if (s.x0 < left)
+ s.x0 = left;
+ if (s.x1 > right)
+ s.x1 = right;
+ plot->clip(&s);
+
+ if (selected) {
+ /* draw selection fill */
+ plot->rectangle(s.x0,
+ y + line * ta->line_height + 1 -
+ ta->scroll_y + text_y_offset,
+ s.x1,
+ y + (line + 1) * ta->line_height + 1 -
+ ta->scroll_y + text_y_offset,
+ &plot_style_fill_bg);
}
+ /* draw text */
+ plot->text(x + ta->border_width + ta->pad_left -
+ ta->scroll_x,
+ y + line * ta->line_height +
+ text_y_offset_baseline - ta->scroll_y,
+ ta->text + ta->lines[line].b_start,
+ ta->lines[line].b_length, fstyle);
+
+ c_pos += c_len_part;
+ c_len -= c_len_part;
+
+ } while (c_pos < c_pos + c_len);
+
+ /* if there is a newline between the lines, skip it */
+ if (line < ta->line_count - 1 &&
+ ta->lines[line + 1].b_start !=
+ ta->lines[line].b_start +
+ ta->lines[line].b_length)
+ c_pos++;
+ }
- if (chars >= 0) {
+ plot->clip(clip);
- /* find byte end of the selected part */
- for (b_end = b_start; chars > 0 &&
- b_end < line_len;
- chars--) {
- b_end = utf8_next(line_text, line_len,
- b_end);
- }
- }
- else
- b_end = ta->lines[line].b_length;
-
- b_end -= b_start;
- nsfont.font_width(&ta->fstyle,
- &(ta->text[ta->lines[line].b_start +
- b_start]),
- b_end, &r.x1);
- r.x1 += r.x0;
- plot->rectangle(r.x0 - ta->scroll_x, y +
- line * ta->line_height +
- 1 - ta->scroll_y + text_y_offset,
- r.x1 - ta->scroll_x,
- y + (line + 1) * ta->line_height -
- 1 - ta->scroll_y + text_y_offset,
- &pstyle_fill_selection);
+ if ((ta->sel_end == -1 || ta->sel_start == ta->sel_end) &&
+ ta->caret_pos.char_off >= 0) {
+ /* There is no selection, and caret visible: show caret */
+ int caret_y = y - ta->scroll_y + ta->caret_y + text_y_offset;
+ if (ta->flags & TEXTAREA_INTERNAL_CARET) {
+ /* Render our own caret */
+ plot->line(x - ta->scroll_x + ta->caret_x, caret_y,
+ x - ta->scroll_x + ta->caret_x,
+ caret_y + ta->line_height,
+ &pstyle_stroke_caret);
+ } else {
+ /* Tell client where caret should be placed */
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_MOVED_CARET;
+ msg.data.caret.hidden = false;
+ msg.data.caret.x = x - ta->scroll_x + ta->caret_x;
+ msg.data.caret.y = caret_y;
+ msg.data.caret.height = ta->line_height;
+
+ ta->callback(ta->data, &msg);
}
+ } else if (!(ta->flags & TEXTAREA_INTERNAL_CARET)) {
+ /* Caret hidden, and client is responsible: tell client */
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_MOVED_CARET;
+ msg.data.caret.hidden = true;
- c_pos += c_len;
-
- r.y0 = y + line * ta->line_height + text_y_offset_baseline;
-
- ta->fstyle.background =
- (ta->flags & TEXTAREA_READONLY) ?
- READONLY_BG : BACKGROUND_COL,
-
- plot->text(x + MARGIN_LEFT - ta->scroll_x,
- r.y0 - ta->scroll_y,
- ta->text + ta->lines[line].b_start,
- ta->lines[line].b_length,
- &ta->fstyle);
+ ta->callback(ta->data, &msg);
}
- if ((ta->sel_end == -1 ||
- ta->sel_start == ta->sel_end) &&
- x + ta->caret_x >= clip->x0 &&
- x + ta->caret_x <= clip->x1) {
- /* There is no selection and caret is in horizontal
- * clip range. */
- int caret_height = ta->line_height - 1;
- y += ta->caret_y + text_y_offset;
- if (y + caret_height >= clip->y0 && y <= clip->y1)
- /* Caret in vertical clip range; plot */
- plot->line(x + ta->caret_x, y + ta->caret_y,
- x + ta->caret_x,
- y + ta->caret_y + caret_height,
- &pstyle_stroke_caret);
- }
+ if (ta->bar_x != NULL)
+ scrollbar_redraw(ta->bar_x,
+ x + ta->border_width,
+ y + ta->vis_height - ta->border_width -
+ SCROLLBAR_WIDTH,
+ clip, 1.0, ctx);
+
+ if (ta->bar_y != NULL)
+ scrollbar_redraw(ta->bar_y,
+ x + ta->vis_width - ta->border_width -
+ SCROLLBAR_WIDTH,
+ y + ta->border_width,
+ clip, 1.0, ctx);
}
/* exported interface, documented in textarea.h */
bool textarea_keypress(struct textarea *ta, uint32_t key)
{
+ struct textarea_msg msg;
char utf8[6];
unsigned int caret, caret_init, length, l_len, b_off, b_len;
int c_line, c_chars, line;
@@ -1082,7 +1443,6 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
line = ta->caret_pos.line;
readonly = (ta->flags & TEXTAREA_READONLY ? true:false);
-
if (!(key <= 0x001F || (0x007F <= key && key <= 0x009F))) {
/* normal character insertion */
length = utf8_from_ucs4(key, utf8);
@@ -1110,10 +1470,10 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
case KEY_SELECT_ALL:
caret = ta->text_utf8_len;
- ta->sel_start = 0;
+ ta->sel_start = 0;
ta->sel_end = ta->text_utf8_len;
- redraw = true;
- break;
+ redraw = true;
+ break;
case KEY_COPY_SELECTION:
if (ta->sel_start != -1) {
if (!textarea_replace_text(ta,
@@ -1157,13 +1517,16 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
break;
case KEY_PASTE:
{
- if (readonly)
- break;
char *clipboard;
size_t clipboard_length;
size_t clipboard_chars;
+ if (readonly)
+ break;
+
gui_get_clipboard(&clipboard, &clipboard_length);
+ if (clipboard == NULL)
+ return false;
clipboard_chars = utf8_bounded_length(clipboard,
clipboard_length);
@@ -1336,7 +1699,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
break;
if (ta->sel_start != -1) {
if (!textarea_replace_text(ta,
- ta->sel_start,
+ ta->sel_start,
ta->sel_end, "", 0, false))
return false;
@@ -1404,7 +1767,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
break;
if (ta->sel_start != -1) {
if (!textarea_replace_text(ta,
- ta->sel_start,
+ ta->sel_start,
ta->sel_end, "", 0, false))
return false;
ta->sel_start = ta->sel_end = -1;
@@ -1424,7 +1787,7 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
break;
if (ta->sel_start != -1) {
if (!textarea_replace_text(ta,
- ta->sel_start,
+ ta->sel_start,
ta->sel_end, "", 0, false))
return false;
ta->sel_start = ta->sel_end = -1;
@@ -1446,8 +1809,14 @@ bool textarea_keypress(struct textarea *ta, uint32_t key)
textarea_set_caret(ta, caret);
//TODO:redraw only the important part
if (redraw) {
- ta->redraw_request(ta->data, 0, 0,
- ta->vis_width, ta->vis_height);
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_REDRAW_REQUEST;
+ msg.data.redraw.x0 = 0;
+ msg.data.redraw.y0 = 0;
+ msg.data.redraw.x1 = ta->vis_width;
+ msg.data.redraw.y1 = ta->vis_height;
+
+ ta->callback(ta->data, &msg);
}
return true;
@@ -1459,23 +1828,83 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse,
int x, int y)
{
int c_start, c_end;
+ int sx, sy; /* xy coord offset for scrollbar */
+ int sl; /* scrollbar length */
size_t b_off;
unsigned int c_off;
+ struct textarea_msg msg;
+
+ if (ta->drag_info.type != TEXTAREA_DRAG_NONE &&
+ mouse == BROWSER_MOUSE_HOVER) {
+ /* There is a drag that we must end */
+ textarea_drag_end(ta, mouse, x, y);
+ }
+
+ if (ta->drag_info.type == TEXTAREA_DRAG_SCROLLBAR) {
+ /* Scrollbar drag in progress; pass input to scrollbar */
+ if (ta->drag_info.data.scrollbar == ta->bar_x) {
+ x -= ta->border_width;
+ y -= ta->vis_height - ta->border_width -
+ SCROLLBAR_WIDTH;
+ } else {
+ x -= ta->vis_width - ta->border_width -
+ SCROLLBAR_WIDTH;
+ y -= ta->border_width;
+ }
+ scrollbar_mouse_action(ta->drag_info.data.scrollbar,
+ mouse, x, y);
+ return true;
+ }
+
+ /* Horizontal scrollbar */
+ if (ta->bar_x != NULL && ta->drag_info.type == TEXTAREA_DRAG_NONE) {
+ /* No drag happening, but mouse input is over scrollbar;
+ * pass input to scrollbar */
+ sx = x - ta->border_width;
+ sy = y - (ta->vis_height - ta->border_width - SCROLLBAR_WIDTH);
+ sl = ta->vis_width - 2 * ta->border_width -
+ (ta->bar_y != NULL ? SCROLLBAR_WIDTH : 0);
+
+ if (sx >= 0 && sy >= 0 && sx < sl && sy < SCROLLBAR_WIDTH) {
+ scrollbar_mouse_action(ta->bar_x, mouse, sx, sy);
+ return true;
+ }
+ }
+
+ /* Vertical scrollbar */
+ if (ta->bar_y != NULL && ta->drag_info.type == TEXTAREA_DRAG_NONE) {
+ /* No drag happening, but mouse input is over scrollbar;
+ * pass input to scrollbar */
+ sx = x - (ta->vis_width - ta->border_width - SCROLLBAR_WIDTH);
+ sy = y - ta->border_width;
+ sl = ta->vis_height - 2 * ta->border_width;
+
+ if (sx >= 0 && sy >= 0 && sx < SCROLLBAR_WIDTH && sy < sl) {
+ scrollbar_mouse_action(ta->bar_y, mouse, sx, sy);
+ return true;
+ }
+ }
/* mouse button pressed above the text area, move caret */
if (mouse & BROWSER_MOUSE_PRESS_1) {
if (!(ta->flags & TEXTAREA_READONLY)) {
- textarea_set_caret_xy(ta, x, y);
-
textarea_get_xy_offset(ta, x, y, &b_off, &c_off);
ta->drag_start_char = c_off;
+
+ textarea_set_caret(ta, c_off);
}
if (ta->sel_start != -1) {
/* remove selection */
ta->sel_start = ta->sel_end = -1;
- ta->redraw_request(ta->data, 0, 0,
- ta->vis_width,
- ta->vis_height);
+
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_REDRAW_REQUEST;
+ msg.data.redraw.x0 = 0;
+ msg.data.redraw.y0 = 0;
+ msg.data.redraw.x1 = ta->vis_width;
+ msg.data.redraw.y1 = ta->vis_height;
+
+ ta->callback(ta->data, &msg);
}
} else if (mouse & BROWSER_MOUSE_DOUBLE_CLICK) {
@@ -1488,24 +1917,18 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse,
textarea_get_xy_offset(ta, x, y, &b_off, &c_off);
c_start = ta->drag_start_char;
c_end = c_off;
- return textarea_select(ta, c_start, c_end);
- }
+ ta->drag_info.type = TEXTAREA_DRAG_SELECTION;
- return true;
-}
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_DRAG_REPORT;
+ msg.data.drag = ta->drag_info.type;
+ ta->callback(ta->data, &msg);
-/* exported interface, documented in textarea.h */
-bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse,
- int x, int y)
-{
- int c_end;
- size_t b_off;
- unsigned int c_off;
+ return textarea_select(ta, c_start, c_end);
+ }
- textarea_get_xy_offset(ta, x, y, &b_off, &c_off);
- c_end = c_off;
- return textarea_select(ta, ta->drag_start_char, c_end);
+ return true;
}
@@ -1522,8 +1945,18 @@ void textarea_get_dimensions(struct textarea *ta, int *width, int *height)
/* exported interface, documented in textarea.h */
void textarea_set_dimensions(struct textarea *ta, int width, int height)
{
+ struct textarea_msg msg;
+
ta->vis_width = width;
ta->vis_height = height;
textarea_reflow(ta, 0);
- ta->redraw_request(ta->data, 0, 0, ta->vis_width, ta->vis_height);
+
+ msg.ta = ta;
+ msg.type = TEXTAREA_MSG_REDRAW_REQUEST;
+ msg.data.redraw.x0 = 0;
+ msg.data.redraw.y0 = 0;
+ msg.data.redraw.x1 = ta->vis_width;
+ msg.data.redraw.y1 = ta->vis_height;
+
+ ta->callback(ta->data, &msg);
}
diff --git a/desktop/textarea.h b/desktop/textarea.h
index e4fa2c7aa..6a1a4714d 100644
--- a/desktop/textarea.h
+++ b/desktop/textarea.h
@@ -29,34 +29,83 @@
#include "desktop/browser.h"
#include "desktop/plot_style.h"
+
+struct textarea;
+
/* Text area flags */
-typedef enum textarea_flags {
- TEXTAREA_DEFAULT = (1 << 0),
- TEXTAREA_MULTILINE = (1 << 1),
- TEXTAREA_READONLY = (1 << 2)
+typedef enum {
+ TEXTAREA_DEFAULT = (1 << 0), /**< Standard input */
+ TEXTAREA_MULTILINE = (1 << 1), /**< Multiline area */
+ TEXTAREA_READONLY = (1 << 2), /**< Non-editable */
+ TEXTAREA_INTERNAL_CARET = (1 << 3) /**< Render own caret */
} textarea_flags;
+typedef enum {
+ TEXTAREA_DRAG_NONE,
+ TEXTAREA_DRAG_SCROLLBAR,
+ TEXTAREA_DRAG_SELECTION
+} textarea_drag_type;
+
+typedef enum {
+ TEXTAREA_MSG_DRAG_REPORT, /**< Textarea drag start/end report */
+ TEXTAREA_MSG_REDRAW_REQUEST, /**< Textarea redraw request */
+ TEXTAREA_MSG_MOVED_CARET /**< Textarea caret moved */
+} textarea_msg_type;
+
+struct textarea_msg {
+ struct textarea *ta;
+
+ textarea_msg_type type;
+ union {
+ textarea_drag_type drag;
+ struct rect redraw;
+ struct {
+ bool hidden;
+ int x;
+ int y;
+ int height;
+ } caret;
+ } data;
+};
+
+typedef struct textarea_setup {
+ textarea_flags flags; /**< Setup flags */
+
+ int width; /**< Textarea width */
+ int height; /**< Textarea height */
+
+ int pad_top; /**< Textarea top padding */
+ int pad_right; /**< Textarea right padding */
+ int pad_bottom; /**< Textarea bottom padding */
+ int pad_left; /**< Textarea left padding */
+
+ int border_width; /**< Textarea border width */
+ colour border_col; /**< Textarea border colour */
+
+ colour selected_text; /**< Textarea selected text colour */
+ colour selected_bg; /**< Textarea selection background colour */
+ plot_font_style_t text; /**< Textarea background colour and font */
+
+} textarea_setup;
-struct textarea;
-
-typedef void(*textarea_redraw_request_callback)(void *data, int x, int y,
- int width, int height);
+/**
+ * Client callback for the textarea
+ *
+ * \param data user data passed at textarea creation
+ * \param textarea_msg textarea message data
+ */
+typedef void(*textarea_client_callback)(void *data, struct textarea_msg *msg);
/**
* Create a text area
*
- * \param width width of the text area
- * \param height width of the text area
- * \param flags text area flags
- * \param style font style
- * \param redraw_start_callback will be called when textarea wants to redraw
- * \param redraw_end_callback will be called when textarea finisjes redrawing
- * \param data user specified data which will be passed to redraw callbacks
+ * \param setup textarea settings and style
+ * \param redraw_callback will be called when textarea wants to redraw
+ * \param data user specified data which will be passed to callbacks
* \return Opaque handle for textarea or 0 on error
*/
-struct textarea *textarea_create(int width, int height,
- textarea_flags flags, const plot_font_style_t *style,
- textarea_redraw_request_callback redraw_request, void *data);
+struct textarea *textarea_create(const textarea_setup *setup,
+ textarea_client_callback callback, void *data);
/**
* Destroy a text area
@@ -106,14 +155,14 @@ int textarea_get_caret(struct textarea *ta);
/**
* Handle redraw requests for text areas
*
- * \param redraw Redraw request block
- * \param x0 left X coordinate of redraw area
- * \param y0 top Y coordinate of redraw area
- * \param x1 right X coordinate of redraw area
- * \param y1 bottom Y coordinate of redraw area
+ * \param ta textarea to render
+ * \param x x coordinate of textarea top
+ * \param y y coordinate of textarea left
+ * \param bg background colour under textarea
+ * \param clip clip rectangle
* \param ctx current redraw context
*/
-void textarea_redraw(struct textarea *ta, int x, int y,
+void textarea_redraw(struct textarea *ta, int x, int y, colour bg,
const struct rect *clip, const struct redraw_context *ctx);
/**
@@ -138,18 +187,6 @@ bool textarea_mouse_action(struct textarea *ta, browser_mouse_state mouse,
int x, int y);
/**
- * Handles the end of a drag operation
- *
- * \param ta Text area
- * \param mouse the mouse state at drag end moment
- * \param x X coordinate
- * \param y Y coordinate
- * \return true if drag end was handled false otherwise
- */
-bool textarea_drag_end(struct textarea *ta, browser_mouse_state mouse,
- int x, int y);
-
-/**
* Gets the dimensions of a textarea
*
* \param width if not NULL, gets updated to the width of the textarea
diff --git a/desktop/tree.c b/desktop/tree.c
index 250bdd861..959b9870a 100644
--- a/desktop/tree.c
+++ b/desktop/tree.c
@@ -160,8 +160,7 @@ struct tree {
int height; /* Tree height */
unsigned int flags; /* Tree flags */
struct textarea *textarea; /* Handle for UTF-8 textarea */
- bool textarea_drag_start; /* whether the start of a mouse drag
- was in the textarea */
+ int ta_height; /* Textarea height */
struct node_element *editing; /* Node element being edited */
bool redraw; /* Flag indicating whether the tree
@@ -263,7 +262,6 @@ struct tree *tree_create(unsigned int flags,
tree->height = 0;
tree->flags = flags;
tree->textarea = NULL;
- tree->textarea_drag_start = false;
tree->editing = NULL;
tree->redraw = false;
tree->drag = TREE_NO_DRAG;
@@ -2078,7 +2076,10 @@ void tree_draw(struct tree *tree, int x, int y,
y = y + tree->editing->box.y;
if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON)
x += NODE_INSTEP;
- textarea_redraw(tree->textarea, x, y, &clip, &new_ctx);
+ textarea_redraw(tree->textarea, x, y,
+ plot_style_fill_tree_background.
+ fill_colour,
+ &clip, &new_ctx);
}
}
@@ -2427,9 +2428,9 @@ bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse, int x,
x0 += NODE_INSTEP;
x1 = tree->editing->box.x + tree->editing->box.width;
y0 = tree->editing->box.y;
- y1 = tree->editing->box.y + tree->editing->box.height;
+ y1 = tree->editing->box.y + tree->ta_height;
- if (tree->textarea_drag_start &&
+ if (tree->drag == TREE_TEXTAREA_DRAG &&
(mouse & (BROWSER_MOUSE_HOLDING_1 |
BROWSER_MOUSE_HOLDING_2))) {
/* Track the drag path */
@@ -2438,19 +2439,7 @@ bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse, int x,
return true;
}
-
-
if ((x >= x0) && (x < x1) && (y >= y0) && (y < y1)) {
- /* Inside the textarea */
- if (mouse & (BROWSER_MOUSE_DRAG_1 |
- BROWSER_MOUSE_DRAG_2)) {
- /* Drag starting */
- tree->textarea_drag_start = true;
- tree->drag = TREE_TEXTAREA_DRAG;
- } else {
- /* Other action */
- tree->textarea_drag_start = false;
- }
textarea_mouse_action(tree->textarea, mouse,
x - x0, y - y0);
return true;
@@ -2458,8 +2447,6 @@ bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse, int x,
}
}
- tree->textarea_drag_start = false;
-
/* we are not interested in the drag path, return */
if (mouse & (BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_HOLDING_2))
return true;
@@ -2832,25 +2819,25 @@ void tree_drag_end(struct tree *tree, browser_mouse_state mouse, int x0, int y0,
struct node *node;
int x, y;
- if (tree->textarea_drag_start) {
+ switch (tree->drag) {
+ case TREE_NO_DRAG:
+ case TREE_UNKNOWN_DRAG:
+ break;
+
+ case TREE_TEXTAREA_DRAG:
x = tree->editing->box.x;
y = tree->editing->box.y;
if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON)
x += NODE_INSTEP;
- textarea_drag_end(tree->textarea, mouse, x1 - x, y1 - y);
- }
-
- tree->textarea_drag_start = false;
-
- switch (tree->drag) {
- case TREE_NO_DRAG:
- case TREE_TEXTAREA_DRAG:
- case TREE_UNKNOWN_DRAG:
+ textarea_mouse_action(tree->textarea, BROWSER_MOUSE_HOVER,
+ x1 - x, y1 - y);
break;
+
case TREE_SELECT_DRAG:
tree_handle_selection_area(tree, y0, y1 - y0,
(mouse | BROWSER_MOUSE_HOLDING_2));
break;
+
case TREE_MOVE_DRAG:
if (!(tree->flags & TREE_MOVABLE))
return;
@@ -2872,7 +2859,6 @@ void tree_drag_end(struct tree *tree, browser_mouse_state mouse, int x0, int y0,
*/
bool tree_keypress(struct tree *tree, uint32_t key)
{
-
if (tree->editing != NULL)
switch (key) {
case KEY_ESCAPE:
@@ -2909,19 +2895,43 @@ int tree_alphabetical_sort(struct node *n1, struct node *n2)
* check the redraw flag of the tree before requesting a redraw and change the
* position to tree origin relative.
*/
-static void tree_textarea_redraw_request(void *data, int x, int y,
- int width, int height)
+
+static void tree_textarea_callback(void *data, struct textarea_msg *msg)
{
struct tree *tree = data;
- x = x + tree->editing->box.x;
- y = y + tree->editing->box.y;
- if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON)
- x += NODE_INSTEP;
+ int x, y;
- if (tree->redraw)
- tree->callbacks->redraw_request(x, y,
- width, height,
- tree->client_data);
+ switch (msg->type) {
+ case TEXTAREA_MSG_DRAG_REPORT:
+ if (msg->data.drag == TEXTAREA_DRAG_NONE) {
+ /* Textarea drag finished */
+ tree->drag = TREE_NO_DRAG;
+ } else {
+ /* Textarea drag started */
+ tree->drag = TREE_TEXTAREA_DRAG;
+ }
+ break;
+
+ case TEXTAREA_MSG_REDRAW_REQUEST:
+ x = msg->data.redraw.x0 + tree->editing->box.x;
+ y = msg->data.redraw.y0 + tree->editing->box.y;
+
+ if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON)
+ x += NODE_INSTEP;
+
+ /* Redraw the textarea */
+ if (tree->redraw)
+ tree->callbacks->redraw_request(x, y,
+ msg->data.redraw.x1 -
+ msg->data.redraw.x0,
+ msg->data.redraw.y1 -
+ msg->data.redraw.y0,
+ tree->client_data);
+ break;
+
+ default:
+ break;
+ }
}
@@ -2935,6 +2945,7 @@ void tree_start_edit(struct tree *tree, struct node_element *element)
{
struct node *parent;
int width, height;
+ textarea_setup ta_setup;
assert(tree != NULL);
assert(element != NULL);
@@ -2959,8 +2970,25 @@ void tree_start_edit(struct tree *tree, struct node_element *element)
if (element->type == NODE_ELEMENT_TEXT_PLUS_ICON)
width -= NODE_INSTEP;
- tree->textarea = textarea_create(width, height, TEXTAREA_DEFAULT,
- &plot_fstyle, tree_textarea_redraw_request, tree);
+ tree->ta_height = height;
+
+ ta_setup.flags = TEXTAREA_INTERNAL_CARET;
+ ta_setup.width = width;
+ ta_setup.height = tree->ta_height;
+ ta_setup.pad_top = 0;
+ ta_setup.pad_right = 4;
+ ta_setup.pad_bottom = 0;
+ ta_setup.pad_left = 4;
+ ta_setup.border_width = 1;
+ ta_setup.border_col = 0x000000;
+ ta_setup.selected_text = 0xffffff;
+ ta_setup.selected_bg = 0x000000;
+ ta_setup.text = plot_fstyle;
+ ta_setup.text.foreground = 0x000000;
+ ta_setup.text.background = 0xffffff;
+
+ tree->textarea = textarea_create(&ta_setup,
+ tree_textarea_callback, tree);
if (tree->textarea == NULL) {
tree_stop_edit(tree, false);
return;