HelenOS sources
This source file includes following definitions.
- ui_entry_create
- ui_entry_destroy
- ui_entry_ctl
- ui_entry_set_rect
- ui_entry_set_halign
- ui_entry_set_read_only
- ui_entry_set_text
- ui_entry_get_text
- ui_entry_paint_cursor
- ui_entry_lwidth
- ui_entry_paint
- ui_entry_find_pos
- ui_entry_ctl_destroy
- ui_entry_ctl_paint
- ui_entry_delete_sel
- ui_entry_insert_str
- ui_entry_backspace
- ui_entry_delete
- ui_entry_copy
- ui_entry_cut
- ui_entry_paste
- ui_entry_key_press_unmod
- ui_entry_key_press_shift
- ui_entry_key_press_ctrl
- ui_entry_kbd_event
- ui_entry_pos_event
- ui_entry_ctl_kbd_event
- ui_entry_ctl_pos_event
- ui_entry_get_geom
- ui_entry_activate
- ui_entry_seek_start
- ui_entry_seek_end
- ui_entry_seek_prev_char
- ui_entry_seek_next_char
- ui_entry_deactivate
- ui_entry_scroll_update
#include <clipboard.h>
#include <errno.h>
#include <gfx/context.h>
#include <gfx/cursor.h>
#include <gfx/render.h>
#include <gfx/text.h>
#include <macros.h>
#include <stdlib.h>
#include <str.h>
#include <ui/control.h>
#include <ui/paint.h>
#include <ui/entry.h>
#include <ui/ui.h>
#include <ui/window.h>
#include "../private/entry.h"
#include "../private/resource.h"
static void ui_entry_ctl_destroy(void *);
static errno_t ui_entry_ctl_paint(void *);
static ui_evclaim_t ui_entry_ctl_kbd_event(void *, kbd_event_t *);
static ui_evclaim_t ui_entry_ctl_pos_event(void *, pos_event_t *);
enum {
ui_entry_hpad = 4,
ui_entry_vpad = 4,
ui_entry_hpad_text = 1,
ui_entry_vpad_text = 0,
ui_entry_cursor_overshoot = 1,
ui_entry_cursor_width = 2,
ui_entry_sel_hpad = 0,
ui_entry_sel_vpad = 2,
ui_entry_left_scroll_margin = 30
};
ui_control_ops_t ui_entry_ops = {
.destroy = ui_entry_ctl_destroy,
.paint = ui_entry_ctl_paint,
.kbd_event = ui_entry_ctl_kbd_event,
.pos_event = ui_entry_ctl_pos_event
};
errno_t ui_entry_create(ui_window_t *window, const char *text,
ui_entry_t **rentry)
{
ui_entry_t *entry;
errno_t rc;
entry = calloc(1, sizeof(ui_entry_t));
if (entry == NULL)
return ENOMEM;
rc = ui_control_new(&ui_entry_ops, (void *) entry, &entry->control);
if (rc != EOK) {
free(entry);
return rc;
}
entry->text = str_dup(text);
if (entry->text == NULL) {
ui_control_delete(entry->control);
free(entry);
return ENOMEM;
}
entry->window = window;
entry->halign = gfx_halign_left;
*rentry = entry;
return EOK;
}
void ui_entry_destroy(ui_entry_t *entry)
{
if (entry == NULL)
return;
ui_control_delete(entry->control);
free(entry);
}
ui_control_t *ui_entry_ctl(ui_entry_t *entry)
{
return entry->control;
}
void ui_entry_set_rect(ui_entry_t *entry, gfx_rect_t *rect)
{
entry->rect = *rect;
}
void ui_entry_set_halign(ui_entry_t *entry, gfx_halign_t halign)
{
entry->halign = halign;
ui_entry_scroll_update(entry, true);
ui_entry_paint(entry);
}
void ui_entry_set_read_only(ui_entry_t *entry, bool read_only)
{
entry->read_only = read_only;
}
errno_t ui_entry_set_text(ui_entry_t *entry, const char *text)
{
char *tcopy;
tcopy = str_dup(text);
if (tcopy == NULL)
return ENOMEM;
free(entry->text);
entry->text = tcopy;
entry->pos = str_size(text);
entry->sel_start = entry->pos;
ui_entry_scroll_update(entry, false);
ui_entry_paint(entry);
return EOK;
}
const char *ui_entry_get_text(ui_entry_t *entry)
{
return entry->text;
}
static errno_t ui_entry_paint_cursor(ui_entry_t *entry, gfx_coord2_t *pos)
{
ui_resource_t *res;
gfx_rect_t rect;
gfx_font_metrics_t metrics;
errno_t rc;
res = ui_window_get_res(entry->window);
if (res->textmode) {
rc = gfx_cursor_set_pos(res->gc, pos);
return rc;
}
gfx_font_get_metrics(res->font, &metrics);
rect.p0.x = pos->x;
rect.p0.y = pos->y - ui_entry_cursor_overshoot;
rect.p1.x = pos->x + ui_entry_cursor_width;
rect.p1.y = pos->y + metrics.ascent + metrics.descent + 1 +
ui_entry_cursor_overshoot;
rc = gfx_set_color(res->gc, res->entry_fg_color);
if (rc != EOK)
goto error;
rc = gfx_fill_rect(res->gc, &rect);
if (rc != EOK)
goto error;
return EOK;
error:
return rc;
}
static gfx_coord_t ui_entry_lwidth(ui_entry_t *entry)
{
ui_resource_t *res;
uint8_t tmp;
gfx_coord_t width;
res = ui_window_get_res(entry->window);
tmp = entry->text[entry->pos];
entry->text[entry->pos] = '\0';
width = gfx_text_width(res->font, entry->text);
entry->text[entry->pos] = tmp;
return width;
}
errno_t ui_entry_paint(ui_entry_t *entry)
{
ui_resource_t *res;
ui_entry_geom_t geom;
gfx_text_fmt_t fmt;
gfx_coord2_t pos;
gfx_text_fmt_t cfmt;
gfx_coord2_t cpos;
gfx_rect_t inside;
unsigned off1, off2;
gfx_rect_t sel;
char c;
errno_t rc;
res = ui_window_get_res(entry->window);
ui_entry_get_geom(entry, &geom);
if (res->textmode == false) {
rc = ui_paint_inset_frame(res, &entry->rect, &inside);
if (rc != EOK)
goto error;
} else {
inside = entry->rect;
}
rc = gfx_set_color(res->gc, res->entry_bg_color);
if (rc != EOK)
goto error;
rc = gfx_fill_rect(res->gc, &inside);
if (rc != EOK)
goto error;
pos = geom.text_pos;
gfx_text_fmt_init(&fmt);
fmt.font = res->font;
fmt.color = res->entry_fg_color;
fmt.halign = gfx_halign_left;
fmt.valign = gfx_valign_top;
rc = gfx_set_clip_rect(res->gc, &inside);
if (rc != EOK)
goto error;
off1 = min(entry->pos, entry->sel_start);
off2 = max(entry->pos, entry->sel_start);
c = entry->text[off1];
entry->text[off1] = '\0';
rc = gfx_puttext(&pos, &fmt, entry->text);
if (rc != EOK) {
(void) gfx_set_clip_rect(res->gc, NULL);
goto error;
}
gfx_text_cont(&pos, &fmt, entry->text, &cpos, &cfmt);
entry->text[off1] = c;
if (off1 != off2) {
c = entry->text[off2];
entry->text[off2] = '\0';
cfmt.color = res->entry_sel_text_fg_color;
gfx_text_rect(&cpos, &cfmt, entry->text + off1, &sel);
sel.p0.x -= ui_entry_sel_hpad;
sel.p0.y -= ui_entry_sel_vpad;
sel.p1.x += ui_entry_sel_hpad;
sel.p1.y += ui_entry_sel_vpad;
rc = gfx_set_color(res->gc, res->entry_sel_text_bg_color);
if (rc != EOK)
goto error;
rc = gfx_fill_rect(res->gc, &sel);
if (rc != EOK)
goto error;
rc = gfx_puttext(&cpos, &cfmt, entry->text + off1);
if (rc != EOK) {
(void) gfx_set_clip_rect(res->gc, NULL);
goto error;
}
gfx_text_cont(&cpos, &cfmt, entry->text + off1, &cpos, &cfmt);
entry->text[off2] = c;
}
cfmt.color = res->entry_fg_color;
rc = gfx_puttext(&cpos, &cfmt, entry->text + off2);
if (rc != EOK) {
(void) gfx_set_clip_rect(res->gc, NULL);
goto error;
}
if (entry->active) {
pos.x += ui_entry_lwidth(entry);
rc = ui_entry_paint_cursor(entry, &pos);
if (rc != EOK) {
(void) gfx_set_clip_rect(res->gc, NULL);
goto error;
}
}
rc = gfx_set_clip_rect(res->gc, NULL);
if (rc != EOK)
goto error;
rc = gfx_update(res->gc);
if (rc != EOK)
goto error;
return EOK;
error:
return rc;
}
size_t ui_entry_find_pos(ui_entry_t *entry, gfx_coord2_t *fpos)
{
ui_resource_t *res;
ui_entry_geom_t geom;
gfx_text_fmt_t fmt;
res = ui_window_get_res(entry->window);
ui_entry_get_geom(entry, &geom);
gfx_text_fmt_init(&fmt);
fmt.font = res->font;
fmt.halign = gfx_halign_left;
fmt.valign = gfx_valign_top;
return gfx_text_find_pos(&geom.text_pos, &fmt, entry->text, fpos);
}
void ui_entry_ctl_destroy(void *arg)
{
ui_entry_t *entry = (ui_entry_t *) arg;
ui_entry_destroy(entry);
}
errno_t ui_entry_ctl_paint(void *arg)
{
ui_entry_t *entry = (ui_entry_t *) arg;
return ui_entry_paint(entry);
}
void ui_entry_delete_sel(ui_entry_t *entry)
{
size_t off1;
size_t off2;
off1 = min(entry->sel_start, entry->pos);
off2 = max(entry->sel_start, entry->pos);
memmove(entry->text + off1, entry->text + off2,
str_size(entry->text + off2) + 1);
entry->pos = off1;
entry->sel_start = off1;
ui_entry_scroll_update(entry, false);
ui_entry_paint(entry);
}
errno_t ui_entry_insert_str(ui_entry_t *entry, const char *str)
{
uint8_t tmp;
char *ltext = NULL;
char *newtext;
char *oldtext;
int rc;
if (entry->sel_start != entry->pos)
ui_entry_delete_sel(entry);
tmp = entry->text[entry->pos];
entry->text[entry->pos] = '\0';
ltext = str_dup(entry->text);
if (ltext == NULL)
return ENOMEM;
entry->text[entry->pos] = tmp;
rc = asprintf(&newtext, "%s%s%s", ltext, str, entry->text + entry->pos);
if (rc < 0) {
free(ltext);
return ENOMEM;
}
oldtext = entry->text;
entry->text = newtext;
entry->pos += str_size(str);
free(oldtext);
free(ltext);
entry->sel_start = entry->pos;
ui_entry_scroll_update(entry, false);
ui_entry_paint(entry);
return EOK;
}
void ui_entry_backspace(ui_entry_t *entry)
{
size_t off;
if (entry->sel_start != entry->pos) {
ui_entry_delete_sel(entry);
return;
}
if (entry->pos == 0)
return;
off = entry->pos;
(void) str_decode_reverse(entry->text, &off,
str_size(entry->text));
memmove(entry->text + off, entry->text + entry->pos,
str_size(entry->text + entry->pos) + 1);
entry->pos = off;
entry->sel_start = off;
ui_entry_scroll_update(entry, false);
ui_entry_paint(entry);
}
void ui_entry_delete(ui_entry_t *entry)
{
size_t off;
if (entry->sel_start != entry->pos) {
ui_entry_delete_sel(entry);
return;
}
off = entry->pos;
(void) str_decode(entry->text, &off,
str_size(entry->text));
memmove(entry->text + entry->pos, entry->text + off,
str_size(entry->text + off) + 1);
ui_entry_scroll_update(entry, false);
ui_entry_paint(entry);
}
void ui_entry_copy(ui_entry_t *entry)
{
unsigned off1;
unsigned off2;
char c;
off1 = min(entry->pos, entry->sel_start);
off2 = max(entry->pos, entry->sel_start);
c = entry->text[off2];
entry->text[off2] = '\0';
(void) clipboard_put_str(entry->text + off1);
entry->text[off2] = c;
}
void ui_entry_cut(ui_entry_t *entry)
{
ui_entry_copy(entry);
ui_entry_delete_sel(entry);
}
void ui_entry_paste(ui_entry_t *entry)
{
char *str;
errno_t rc;
rc = clipboard_get_str(&str);
if (rc != EOK)
return;
ui_entry_insert_str(entry, str);
free(str);
}
ui_evclaim_t ui_entry_key_press_unmod(ui_entry_t *entry, kbd_event_t *event)
{
assert(event->type == KEY_PRESS);
switch (event->key) {
case KC_BACKSPACE:
ui_entry_backspace(entry);
break;
case KC_DELETE:
ui_entry_delete(entry);
break;
case KC_ESCAPE:
ui_entry_deactivate(entry);
break;
case KC_HOME:
ui_entry_seek_start(entry, false);
break;
case KC_END:
ui_entry_seek_end(entry, false);
break;
case KC_LEFT:
ui_entry_seek_prev_char(entry, false);
break;
case KC_RIGHT:
ui_entry_seek_next_char(entry, false);
break;
default:
break;
}
return ui_claimed;
}
ui_evclaim_t ui_entry_key_press_shift(ui_entry_t *entry, kbd_event_t *event)
{
assert(event->type == KEY_PRESS);
switch (event->key) {
case KC_HOME:
ui_entry_seek_start(entry, true);
break;
case KC_END:
ui_entry_seek_end(entry, true);
break;
case KC_LEFT:
ui_entry_seek_prev_char(entry, true);
break;
case KC_RIGHT:
ui_entry_seek_next_char(entry, true);
break;
default:
break;
}
return ui_claimed;
}
ui_evclaim_t ui_entry_key_press_ctrl(ui_entry_t *entry, kbd_event_t *event)
{
assert(event->type == KEY_PRESS);
switch (event->key) {
case KC_C:
ui_entry_copy(entry);
break;
case KC_V:
ui_entry_paste(entry);
break;
case KC_X:
ui_entry_cut(entry);
break;
default:
break;
}
return ui_claimed;
}
ui_evclaim_t ui_entry_kbd_event(ui_entry_t *entry, kbd_event_t *event)
{
char buf[STR_BOUNDS(1) + 1];
size_t off;
errno_t rc;
if (!entry->active)
return ui_unclaimed;
if (event->type == KEY_PRESS && event->key == KC_LSHIFT)
entry->lshift_held = true;
if (event->type == KEY_RELEASE && event->key == KC_LSHIFT)
entry->lshift_held = false;
if (event->type == KEY_PRESS && event->key == KC_RSHIFT)
entry->rshift_held = true;
if (event->type == KEY_RELEASE && event->key == KC_RSHIFT)
entry->rshift_held = false;
if (event->type == KEY_PRESS &&
(event->mods & (KM_CTRL | KM_ALT)) == 0 && event->c >= ' ') {
off = 0;
rc = chr_encode(event->c, buf, &off, sizeof(buf));
if (rc == EOK) {
buf[off] = '\0';
(void) ui_entry_insert_str(entry, buf);
}
}
if (event->type == KEY_PRESS &&
(event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)
return ui_entry_key_press_unmod(entry, event);
if (event->type == KEY_PRESS &&
(event->mods & KM_SHIFT) != 0 &&
(event->mods & (KM_CTRL | KM_ALT)) == 0)
return ui_entry_key_press_shift(entry, event);
if (event->type == KEY_PRESS &&
(event->mods & KM_CTRL) != 0 &&
(event->mods & (KM_ALT | KM_SHIFT)) == 0)
return ui_entry_key_press_ctrl(entry, event);
return ui_claimed;
}
ui_evclaim_t ui_entry_pos_event(ui_entry_t *entry, pos_event_t *event)
{
gfx_coord2_t pos;
if (entry->read_only)
return ui_unclaimed;
if (event->type == POS_UPDATE) {
pos.x = event->hpos;
pos.y = event->vpos;
if (gfx_pix_inside_rect(&pos, &entry->rect)) {
if (!entry->pointer_inside) {
ui_window_set_ctl_cursor(entry->window,
ui_curs_ibeam);
entry->pointer_inside = true;
}
} else {
if (entry->pointer_inside) {
ui_window_set_ctl_cursor(entry->window,
ui_curs_arrow);
entry->pointer_inside = false;
}
}
if (entry->held) {
entry->pos = ui_entry_find_pos(entry, &pos);
ui_entry_paint(entry);
}
}
if (event->type == POS_PRESS) {
pos.x = event->hpos;
pos.y = event->vpos;
if (gfx_pix_inside_rect(&pos, &entry->rect)) {
entry->held = true;
entry->pos = ui_entry_find_pos(entry, &pos);
if (!entry->lshift_held && !entry->rshift_held)
entry->sel_start = entry->pos;
if (entry->active)
ui_entry_paint(entry);
else
ui_entry_activate(entry);
return ui_claimed;
} else {
ui_entry_deactivate(entry);
}
}
if (event->type == POS_RELEASE) {
entry->held = false;
}
return ui_unclaimed;
}
static ui_evclaim_t ui_entry_ctl_kbd_event(void *arg, kbd_event_t *event)
{
ui_entry_t *entry = (ui_entry_t *) arg;
return ui_entry_kbd_event(entry, event);
}
static ui_evclaim_t ui_entry_ctl_pos_event(void *arg, pos_event_t *event)
{
ui_entry_t *entry = (ui_entry_t *) arg;
return ui_entry_pos_event(entry, event);
}
void ui_entry_get_geom(ui_entry_t *entry, ui_entry_geom_t *geom)
{
gfx_coord_t hpad;
gfx_coord_t vpad;
ui_resource_t *res;
res = ui_window_get_res(entry->window);
if (res->textmode) {
hpad = ui_entry_hpad_text;
vpad = ui_entry_vpad_text;
} else {
hpad = ui_entry_hpad;
vpad = ui_entry_vpad;
}
if (res->textmode == false) {
ui_paint_get_inset_frame_inside(res, &entry->rect,
&geom->interior_rect);
} else {
geom->interior_rect = entry->rect;
}
geom->text_rect.p0.x = geom->interior_rect.p0.x + hpad;
geom->text_rect.p0.y = geom->interior_rect.p0.y + vpad;
geom->text_rect.p1.x = geom->interior_rect.p1.x - hpad;
geom->text_rect.p1.y = geom->interior_rect.p1.y - vpad;
geom->text_pos.x = geom->interior_rect.p0.x + hpad +
entry->scroll_pos;
geom->text_pos.y = geom->interior_rect.p0.y + vpad;
switch (entry->halign) {
case gfx_halign_left:
case gfx_halign_justify:
geom->anchor_x = geom->text_rect.p0.x;
break;
case gfx_halign_center:
geom->anchor_x = (geom->text_rect.p0.x +
geom->text_rect.p1.x) / 2;
break;
case gfx_halign_right:
geom->anchor_x = geom->text_rect.p1.x;
break;
}
}
void ui_entry_activate(ui_entry_t *entry)
{
ui_resource_t *res;
res = ui_window_get_res(entry->window);
if (entry->active)
return;
entry->active = true;
(void) ui_entry_paint(entry);
if (res->textmode)
gfx_cursor_set_visible(res->gc, true);
}
void ui_entry_seek_start(ui_entry_t *entry, bool shift)
{
entry->pos = 0;
if (!shift)
entry->sel_start = entry->pos;
ui_entry_scroll_update(entry, false);
(void) ui_entry_paint(entry);
}
void ui_entry_seek_end(ui_entry_t *entry, bool shift)
{
entry->pos = str_size(entry->text);
if (!shift)
entry->sel_start = entry->pos;
ui_entry_scroll_update(entry, false);
(void) ui_entry_paint(entry);
}
void ui_entry_seek_prev_char(ui_entry_t *entry, bool shift)
{
size_t off;
off = entry->pos;
(void) str_decode_reverse(entry->text, &off,
str_size(entry->text));
entry->pos = off;
if (!shift)
entry->sel_start = entry->pos;
ui_entry_scroll_update(entry, false);
(void) ui_entry_paint(entry);
}
void ui_entry_seek_next_char(ui_entry_t *entry, bool shift)
{
size_t off;
off = entry->pos;
(void) str_decode(entry->text, &off,
str_size(entry->text));
entry->pos = off;
if (!shift)
entry->sel_start = entry->pos;
ui_entry_scroll_update(entry, false);
(void) ui_entry_paint(entry);
}
void ui_entry_deactivate(ui_entry_t *entry)
{
ui_resource_t *res;
res = ui_window_get_res(entry->window);
if (!entry->active)
return;
entry->active = false;
entry->sel_start = entry->pos;
(void) ui_entry_paint(entry);
if (res->textmode)
gfx_cursor_set_visible(res->gc, false);
}
void ui_entry_scroll_update(ui_entry_t *entry, bool realign)
{
ui_entry_geom_t geom;
gfx_coord_t x;
gfx_coord_t width;
gfx_coord2_t tpos;
gfx_coord2_t anchor;
gfx_text_fmt_t fmt;
ui_resource_t *res;
res = ui_window_get_res(entry->window);
ui_entry_get_geom(entry, &geom);
x = geom.text_pos.x + ui_entry_lwidth(entry);
if (x < geom.text_rect.p0.x) {
entry->scroll_pos += geom.text_rect.p0.x - x +
ui_entry_left_scroll_margin;
if (entry->scroll_pos > 0)
entry->scroll_pos = 0;
}
if (x > geom.text_rect.p1.x)
entry->scroll_pos -= x - geom.text_rect.p1.x;
width = gfx_text_width(res->font, entry->text);
if (width < geom.text_rect.p1.x - geom.text_rect.p0.x &&
(realign || entry->halign != gfx_halign_left)) {
anchor.x = geom.anchor_x;
anchor.y = 0;
gfx_text_fmt_init(&fmt);
fmt.font = res->font;
fmt.halign = entry->halign;
gfx_text_start_pos(&anchor, &fmt, entry->text, &tpos);
entry->scroll_pos = tpos.x - geom.text_rect.p0.x;
} else if (geom.text_pos.x + width < geom.text_rect.p1.x &&
entry->halign != gfx_halign_left) {
entry->scroll_pos += geom.text_rect.p1.x -
geom.text_pos.x - width;
}
}
HelenOS homepage, sources at GitHub