// handle a keypress in edit mode // 0 = unhandled // 1 = handled // -1 = handled and return pressed (finished) int textbox_keypress(textbox *tb, XEvent *ev) { if (!(tb->flags & TB_EDITABLE)) return 0; KeySym key; Status stat; char pad[32]; int len = XmbLookupString(tb->xic, &ev->xkey, pad, sizeof(pad), &key, &stat); pad[len] = 0; switch (key) { case XK_Left : textbox_cursor_dec(tb); return 1; case XK_Right : textbox_cursor_inc(tb); return 1; case XK_Home : textbox_cursor_home(tb); return 1; case XK_End : textbox_cursor_end(tb); return 1; case XK_Delete : textbox_cursor_del(tb); return 1; case XK_BackSpace : textbox_cursor_bkspc(tb); return 1; case XK_Return : return -1; default: if (!iscntrl(*pad)) { textbox_insert(tb, tb->cursor, pad); textbox_cursor_inc(tb); return 1; } } return 0; }
textbox* textbox_create ( TextboxFlags flags, short x, short y, short w, short h, TextBoxFontType tbft, const char *text ) { textbox *tb = g_malloc0 ( sizeof ( textbox ) ); tb->flags = flags; tb->x = x; tb->y = y; tb->w = MAX ( 1, w ); tb->h = MAX ( 1, h ); tb->changed = FALSE; tb->main_surface = cairo_image_surface_create ( get_format (), tb->w, tb->h ); tb->main_draw = cairo_create ( tb->main_surface ); tb->layout = pango_cairo_create_layout ( tb->main_draw ); PangoFontDescription *pfd = pango_font_description_from_string ( config.menu_font ); pango_layout_set_font_description ( tb->layout, pfd ); pango_font_description_free ( pfd ); textbox_font ( tb, tbft ); if ( ( flags & TB_WRAP ) == TB_WRAP ) { pango_layout_set_wrap ( tb->layout, PANGO_WRAP_WORD_CHAR ); } textbox_text ( tb, text ? text : "" ); textbox_cursor_end ( tb ); // auto height/width modes get handled here textbox_moveresize ( tb, tb->x, tb->y, tb->w, tb->h ); return tb; }
// handle a keypress in edit mode // 0 = unhandled // 1 = handled // -1 = handled and return pressed (finished) int textbox_keypress(textbox *tb, XEvent *ev) { KeySym key; Status stat; char pad[32]; int len; if (!(tb->flags & TB_EDITABLE)) return 0; len = XmbLookupString(tb->xic, &ev->xkey, pad, sizeof(pad), &key, &stat); pad[len] = 0; if (key == XK_Left) { textbox_cursor_dec(tb); return 1; } else if (key == XK_Right) { textbox_cursor_inc(tb); return 1; } else if (key == XK_Home) { textbox_cursor_home(tb); return 1; } else if (key == XK_End) { textbox_cursor_end(tb); return 1; } else if (key == XK_Delete) { textbox_cursor_del(tb); return 1; } else if (key == XK_BackSpace) { textbox_cursor_bkspc(tb); return 1; } else if (key == XK_Return) { return -1; } else if (!iscntrl(*pad)) { textbox_insert(tb, tb->cursor, pad); textbox_cursor_inc(tb); return 1; } return 0; }
textbox* textbox_create ( const char *name, TextboxFlags flags, TextBoxFontType tbft, const char *text ) { textbox *tb = g_slice_new0 ( textbox ); widget_init ( WIDGET ( tb ), name ); tb->widget.draw = textbox_draw; tb->widget.free = textbox_free; tb->widget.resize = textbox_resize; tb->widget.get_width = textbox_get_width; tb->widget.get_height = _textbox_get_height; tb->widget.get_desired_height = textbox_get_desired_height; tb->flags = flags; tb->changed = FALSE; tb->main_surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, tb->widget.w, tb->widget.h ); tb->main_draw = cairo_create ( tb->main_surface ); tb->layout = pango_layout_new ( p_context ); textbox_font ( tb, tbft ); if ( ( flags & TB_WRAP ) == TB_WRAP ) { pango_layout_set_wrap ( tb->layout, PANGO_WRAP_WORD_CHAR ); } textbox_text ( tb, text ? text : "" ); textbox_cursor_end ( tb ); // auto height/width modes get handled here textbox_moveresize ( tb, tb->widget.x, tb->widget.y, tb->widget.w, tb->widget.h ); tb->blink_timeout = 0; tb->blink = 1; if ( ( flags & TB_EDITABLE ) == TB_EDITABLE ) { tb->blink_timeout = g_timeout_add ( 1200, textbox_blink, tb ); } // Enabled by default tb->widget.enabled = TRUE; return tb; }
// Xft text box, optionally editable textbox* textbox_create( Window parent, unsigned long flags, short x, short y, short w, short h, char *font, char *fg, char *bg, char *text, char *prompt ) { textbox *tb = calloc( 1, sizeof( textbox ) ); tb->flags = flags; tb->parent = parent; tb->x = x; tb->y = y; tb->w = MAX( 1, w ); tb->h = MAX( 1, h ); XColor color; Colormap map = DefaultColormap( display, DefaultScreen( display ) ); unsigned int cp = XAllocNamedColor( display, map, bg, &color, &color ) ? color.pixel: None; tb->window = XCreateSimpleWindow( display, tb->parent, tb->x, tb->y, tb->w, tb->h, 0, None, cp ); // need to preload the font to calc line height textbox_font( tb, font, fg, bg ); tb->prompt = strdup( prompt ? prompt: "" ); textbox_text( tb, text ? text: "" ); textbox_cursor_end( tb ); // auto height/width modes get handled here textbox_moveresize( tb, tb->x, tb->y, tb->w, tb->h ); // edit mode controls if ( tb->flags & TB_EDITABLE ) { tb->xim = XOpenIM( display, NULL, NULL, NULL ); tb->xic = XCreateIC( tb->xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, tb->window, XNFocusWindow, tb->window, NULL ); } return tb; }
// handle a keypress in edit mode // 2 = nav // 0 = unhandled // 1 = handled // -1 = handled and return pressed (finished) int textbox_keypress ( textbox *tb, XIC xic, XEvent *ev ) { KeySym key; Status stat; char pad[32]; int len; if ( !( tb->flags & TB_EDITABLE ) ) { return 0; } len = Xutf8LookupString ( xic, &ev->xkey, pad, sizeof ( pad ), &key, &stat ); pad[len] = 0; // Left or Ctrl-b if ( abe_test_action ( MOVE_CHAR_BACK, ev->xkey.state, key ) ) { textbox_cursor_dec ( tb ); return 2; } // Right or Ctrl-F else if ( abe_test_action ( MOVE_CHAR_FORWARD, ev->xkey.state, key ) ) { textbox_cursor_inc ( tb ); return 2; } // Ctrl-U: Kill from the beginning to the end of the line. else if ( abe_test_action ( CLEAR_LINE, ev->xkey.state, key ) ) { textbox_text ( tb, "" ); return 1; } // Ctrl-A else if ( abe_test_action ( MOVE_FRONT, ev->xkey.state, key ) ) { textbox_cursor ( tb, 0 ); return 2; } // Ctrl-E else if ( abe_test_action ( MOVE_END, ev->xkey.state, key ) ) { textbox_cursor_end ( tb ); return 2; } // Ctrl-Alt-h else if ( abe_test_action ( REMOVE_WORD_BACK, ev->xkey.state, key ) ) { textbox_cursor_bkspc_word ( tb ); return 1; } // Ctrl-Alt-d else if ( abe_test_action ( REMOVE_WORD_FORWARD, ev->xkey.state, key ) ) { textbox_cursor_del_word ( tb ); return 1; } // Delete or Ctrl-D else if ( abe_test_action ( REMOVE_CHAR_FORWARD, ev->xkey.state, key ) ) { textbox_cursor_del ( tb ); return 1; } // Alt-B else if ( abe_test_action ( MOVE_WORD_BACK, ev->xkey.state, key ) ) { textbox_cursor_dec_word ( tb ); return 2; } // Alt-F else if ( abe_test_action ( MOVE_WORD_FORWARD, ev->xkey.state, key ) ) { textbox_cursor_inc_word ( tb ); return 2; } // BackSpace, Ctrl-h else if ( abe_test_action ( REMOVE_CHAR_BACK, ev->xkey.state, key ) ) { textbox_cursor_bkspc ( tb ); return 1; } else if ( abe_test_action ( ACCEPT_CUSTOM, ev->xkey.state, key ) ) { return -2; } else if ( abe_test_action ( ACCEPT_ENTRY_CONTINUE, ev->xkey.state, key ) ) { return -3; } else if ( abe_test_action ( ACCEPT_ENTRY, ev->xkey.state, key ) ) { return -1; } else if ( !iscntrl ( *pad ) ) { textbox_insert ( tb, tb->cursor, pad ); textbox_cursor_inc ( tb ); return 1; } return 0; }
// handle a keypress in edit mode // 2 = nav // 0 = unhandled // 1 = handled // -1 = handled and return pressed (finished) int textbox_keybinding ( textbox *tb, KeyBindingAction action ) { if ( !( tb->flags & TB_EDITABLE ) ) { return 0; } switch ( action ) { // Left or Ctrl-b case MOVE_CHAR_BACK: textbox_cursor_dec ( tb ); return 2; // Right or Ctrl-F case MOVE_CHAR_FORWARD: textbox_cursor_inc ( tb ); return 2; // Ctrl-U: Kill from the beginning to the end of the line. case CLEAR_LINE: textbox_text ( tb, "" ); return 1; // Ctrl-A case MOVE_FRONT: textbox_cursor ( tb, 0 ); return 2; // Ctrl-E case MOVE_END: textbox_cursor_end ( tb ); return 2; // Ctrl-Alt-h case REMOVE_WORD_BACK: textbox_cursor_bkspc_word ( tb ); return 1; // Ctrl-Alt-d case REMOVE_WORD_FORWARD: textbox_cursor_del_word ( tb ); return 1; case REMOVE_TO_EOL: textbox_cursor_del_eol ( tb ); return 1; case REMOVE_TO_SOL: textbox_cursor_del_sol ( tb ); return 1; // Delete or Ctrl-D case REMOVE_CHAR_FORWARD: textbox_cursor_del ( tb ); return 1; // Alt-B case MOVE_WORD_BACK: textbox_cursor_dec_word ( tb ); return 2; // Alt-F case MOVE_WORD_FORWARD: textbox_cursor_inc_word ( tb ); return 2; // BackSpace, Ctrl-h case REMOVE_CHAR_BACK: textbox_cursor_bkspc ( tb ); return 1; default: g_return_val_if_reached ( 0 ); } }
// handle a keypress in edit mode // 2 = nav // 0 = unhandled // 1 = handled // -1 = handled and return pressed (finished) int textbox_keypress ( textbox *tb, XEvent *ev, char *pad, KeySym key, Status stat ) { if ( !( tb->flags & TB_EDITABLE ) ) { return 0; } if ( stat == XLookupKeySym || stat == XLookupBoth ) { // Left or Ctrl-b if ( abe_test_action ( MOVE_CHAR_BACK, ev->xkey.state, key ) ) { textbox_cursor_dec ( tb ); return 2; } // Right or Ctrl-F else if ( abe_test_action ( MOVE_CHAR_FORWARD, ev->xkey.state, key ) ) { textbox_cursor_inc ( tb ); return 2; } // Ctrl-U: Kill from the beginning to the end of the line. else if ( abe_test_action ( CLEAR_LINE, ev->xkey.state, key ) ) { textbox_text ( tb, "" ); return 1; } // Ctrl-A else if ( abe_test_action ( MOVE_FRONT, ev->xkey.state, key ) ) { textbox_cursor ( tb, 0 ); return 2; } // Ctrl-E else if ( abe_test_action ( MOVE_END, ev->xkey.state, key ) ) { textbox_cursor_end ( tb ); return 2; } // Ctrl-Alt-h else if ( abe_test_action ( REMOVE_WORD_BACK, ev->xkey.state, key ) ) { textbox_cursor_bkspc_word ( tb ); return 1; } // Ctrl-Alt-d else if ( abe_test_action ( REMOVE_WORD_FORWARD, ev->xkey.state, key ) ) { textbox_cursor_del_word ( tb ); return 1; } // Delete or Ctrl-D else if ( abe_test_action ( REMOVE_CHAR_FORWARD, ev->xkey.state, key ) ) { textbox_cursor_del ( tb ); return 1; } // Alt-B else if ( abe_test_action ( MOVE_WORD_BACK, ev->xkey.state, key ) ) { textbox_cursor_dec_word ( tb ); return 2; } // Alt-F else if ( abe_test_action ( MOVE_WORD_FORWARD, ev->xkey.state, key ) ) { textbox_cursor_inc_word ( tb ); return 2; } // BackSpace, Ctrl-h else if ( abe_test_action ( REMOVE_CHAR_BACK, ev->xkey.state, key ) ) { textbox_cursor_bkspc ( tb ); return 1; } else if ( abe_test_action ( ACCEPT_CUSTOM, ev->xkey.state, key ) ) { return -2; } else if ( abe_test_action ( ACCEPT_ENTRY_CONTINUE, ev->xkey.state, key ) ) { return -3; } else if ( abe_test_action ( ACCEPT_ENTRY, ev->xkey.state, key ) ) { return -1; } } if ( *pad != 0 && ( stat == XLookupBoth || stat == XLookupChars ) ) { // Filter When alt/ctrl is pressed do not accept the character. if ( !g_ascii_iscntrl ( *pad ) ) { textbox_insert ( tb, tb->cursor, pad ); textbox_cursor_inc ( tb ); return 1; } } return 0; }
int main ( int argc, char **argv ) { // Get DISPLAY const char *display_str = getenv ( "DISPLAY" ); if ( !( display = XOpenDisplay ( display_str ) ) ) { fprintf ( stderr, "cannot open display!\n" ); return EXIT_FAILURE; } TASSERT( display != NULL ); Screen *screen = DefaultScreenOfDisplay ( display ); Window root = RootWindow ( display, XScreenNumberOfScreen ( screen ) ); Window mw = XCreateSimpleWindow ( display, root, 0, 0, 200, 100, config.menu_bw, color_get ( display, config.menu_bc ), color_get ( display, config.menu_bg ) ); TASSERT( mw != None ); textbox_setup ( config.menu_bg, config.menu_fg, config.menu_hlbg, config.menu_hlfg ); textbox *box = textbox_create(mw , TB_EDITABLE|TB_AUTOWIDTH|TB_AUTOHEIGHT, 0,0, -1, -1, NORMAL, "test"); TASSERT( box != NULL ); textbox_cursor_end ( box ); TASSERT ( box->cursor == 4); textbox_cursor ( box, -1 ); TASSERT ( box->cursor == 0 ); textbox_cursor ( box, 8 ); TASSERT ( box->cursor == 4 ); textbox_cursor ( box, 2 ); TASSERT ( box->cursor == 2 ); textbox_insert ( box, 3, "bo"); TASSERT ( strcmp(box->text, "tesbot") == 0 ); textbox_cursor_end ( box ); TASSERT ( box->cursor == 6); TASSERT( textbox_get_width( box) > 0 ); TASSERT( textbox_get_height( box) > 0 ); TASSERT( textbox_get_width( box) >= textbox_get_font_width( box) ); TASSERT( textbox_get_height( box) >= textbox_get_font_height( box) ); TASSERT( textbox_get_estimated_char_width ( box) > 0 ); textbox_cursor_bkspc ( box ); TASSERT ( strcmp(box->text, "tesbo") == 0 ); TASSERT ( box->cursor == 5); textbox_cursor_dec ( box ); TASSERT ( box->cursor == 4); textbox_cursor_del ( box ); TASSERT ( strcmp(box->text, "tesb") == 0 ); textbox_cursor_dec ( box ); TASSERT ( box->cursor == 3); textbox_cursor_inc ( box ); TASSERT ( box->cursor == 4); textbox_cursor_inc ( box ); TASSERT ( box->cursor == 4); // Cursor after delete section. textbox_delete ( box, 0, 1 ); TASSERT ( strcmp(box->text, "esb") == 0 ); TASSERT ( box->cursor == 3); // Cursor before delete. textbox_text( box, "aap noot mies"); TASSERT ( strcmp(box->text, "aap noot mies") == 0 ); textbox_cursor( box, 3 ); TASSERT ( box->cursor == 3); textbox_delete ( box, 3, 6 ); TASSERT ( strcmp(box->text, "aapmies") == 0 ); TASSERT ( box->cursor == 3); // Cursor within delete textbox_text( box, "aap noot mies"); TASSERT ( strcmp(box->text, "aap noot mies") == 0 ); textbox_cursor( box, 5 ); TASSERT ( box->cursor == 5); textbox_delete ( box, 3, 6 ); TASSERT ( strcmp(box->text, "aapmies") == 0 ); TASSERT ( box->cursor == 3); // Cursor after delete. textbox_text( box, "aap noot mies"); TASSERT ( strcmp(box->text, "aap noot mies") == 0 ); textbox_cursor( box, 11 ); TASSERT ( box->cursor == 11); textbox_delete ( box, 3, 6 ); TASSERT ( strcmp(box->text, "aapmies") == 0 ); TASSERT ( box->cursor == 5); textbox_font ( box, HIGHLIGHT ); textbox_draw( box ); textbox_show( box ); textbox_move ( box, 12, 13); TASSERT ( box->x == 12 ); TASSERT ( box->y == 13 ); textbox_hide( box ); textbox_free(box); textbox_cleanup(); XDestroyWindow ( display, mw); XCloseDisplay ( display ); }