diff --git a/Makefile.am b/Makefile.am index ca688bd1..537a24e4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -7,13 +7,15 @@ LIBS=\ @xft_LIBS@\ @x11_LIBS@\ @xdgbasedir_LIBS@\ - @xinerama_LIBS@ + @xinerama_LIBS@\ + @pango_LIBS@ AM_CFLAGS=\ @xft_CFLAGS@\ @x11_CFLAGS@\ @xdgbasedir_CFLAGS@\ @xinerama_CFLAGS@\ + @pango_CFLAGS@\ -DMANPAGE_PATH="\"$(mandir)/man1/rofi.1\""\ -I$(top_srcdir)/include/\ -I$(top_srcdir)/config/\ diff --git a/configure.ac b/configure.ac index a67e0e8b..fa5fca1a 100644 --- a/configure.ac +++ b/configure.ac @@ -31,6 +31,7 @@ PKG_CHECK_MODULES([xft], [xft]) PKG_CHECK_MODULES([x11], [x11]) PKG_CHECK_MODULES([xinerama], [xinerama]) PKG_CHECK_MODULES([xdgbasedir], [libxdg-basedir]) +PKG_CHECK_MODULES([pango], [pango pangoxft]) AC_CONFIG_FILES([Makefile]) diff --git a/include/textbox.h b/include/textbox.h index 18681acd..735f3dcb 100644 --- a/include/textbox.h +++ b/include/textbox.h @@ -2,18 +2,21 @@ #define __TEXTBOX_H__ #include +#include +#include +#include + typedef struct { unsigned long flags; Window window, parent; short x, y, w, h; short cursor; - XftFont *font; XftColor color_fg, color_bg; char *text; XIM xim; XIC xic; - XGlyphInfo extents; + PangoLayout *layout; } textbox; @@ -76,7 +79,6 @@ void textbox_hide ( textbox *tb ); * Clean with textbox_cleanup() */ void textbox_setup ( - const char *font_str, const char *font_active_str, const char *bg, const char *fg, const char *hlbg, const char *hlfg ); @@ -86,4 +88,9 @@ void textbox_setup ( */ void textbox_cleanup (); +int textbox_get_height ( textbox *tb ); +int textbox_get_width ( textbox *tb ); +int textbox_get_font_height ( textbox *tb ); +int textbox_get_font_width ( textbox *tb ); + #endif //__TEXTBOX_H__ diff --git a/source/rofi.c b/source/rofi.c index 86c42258..cf45a478 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -1123,14 +1123,15 @@ MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prom 0, 0, NORMAL, prompt ); textbox *text = textbox_create ( main_window, TB_AUTOHEIGHT | TB_EDITABLE, - ( config.padding ) + prompt_tb->w, + ( config.padding ) + textbox_get_width ( prompt_tb ), ( config.padding ), ( ( config.hmode == TRUE ) ? - element_width : ( w - ( 2 * ( config.padding ) ) ) ) - prompt_tb->w, 1, + element_width : ( w - ( 2 * ( config.padding ) ) ) ) + - textbox_get_width ( prompt_tb ), 1, NORMAL, ( input != NULL ) ? *input : "" ); - int line_height = text->font->ascent + text->font->descent; + int line_height = textbox_get_height ( text ); //text->font->ascent + text->font->descent; textbox_show ( text ); textbox_show ( prompt_tb ); @@ -1722,10 +1723,10 @@ static void run_switcher ( int do_fork, SwitcherMode mode ) } // Because of the above fork, we want to do this here. // Make sure this is isolated to its own thread. - textbox_setup ( config.menu_font, active_font, - config.menu_bg, config.menu_fg, - config.menu_hlbg, - config.menu_hlfg ); + textbox_setup ( + config.menu_bg, config.menu_fg, + config.menu_hlbg, + config.menu_hlfg ); char *input = NULL; // Dmenu is a special mode. You can cycle away from it. if ( mode == DMENU_DIALOG ) { diff --git a/source/textbox.c b/source/textbox.c index ad8db382..0ab4f580 100644 --- a/source/textbox.c +++ b/source/textbox.c @@ -37,22 +37,25 @@ #include #include + #include "rofi.h" #include "textbox.h" #define SIDE_MARGIN 2 + extern Display *display; /** * Font + font color cache. * Avoid re-loading font on every change on every textbox. */ -XftFont *font = NULL; -XftFont *font_active = NULL; -XftColor color_fg; -XftColor color_bg; -XftColor color_hlfg; -XftColor color_hlbg; +XftColor color_fg; +XftColor color_bg; +XftColor color_hlfg; +XftColor color_hlbg; + +PangoContext *p_context = NULL; + void textbox_moveresize ( textbox *tb, int x, int y, int w, int h ); @@ -73,6 +76,12 @@ textbox* textbox_create ( Window parent, tb->w = MAX ( 1, w ); tb->h = MAX ( 1, h ); + tb->layout = pango_layout_new ( p_context ); + + PangoFontDescription *pfd = pango_font_description_from_string ( config.menu_font ); + pango_layout_set_font_description ( tb->layout, pfd ); + pango_font_description_free ( pfd ); + unsigned int cp = ( tbft == NORMAL ) ? color_bg.pixel : color_hlbg.pixel; tb->window = XCreateSimpleWindow ( display, tb->parent, tb->x, tb->y, tb->w, tb->h, 0, None, cp ); @@ -98,36 +107,36 @@ textbox* textbox_create ( Window parent, // set an Xft font by name void textbox_font ( textbox *tb, TextBoxFontType tbft ) { + PangoFontDescription *pfd = pango_font_description_from_string ( config.menu_font ); switch ( tbft ) { case HIGHLIGHT: - tb->font = font; tb->color_bg = color_hlbg; tb->color_fg = color_hlfg; break; case ACTIVE: - tb->font = font_active; + pango_font_description_set_style ( pfd, PANGO_STYLE_ITALIC ); tb->color_bg = color_bg; tb->color_fg = color_fg; break; case ACTIVE_HIGHLIGHT: - tb->font = font_active; + pango_font_description_set_style ( pfd, PANGO_STYLE_ITALIC ); tb->color_bg = color_hlbg; tb->color_fg = color_hlfg; break; case NORMAL: default: - tb->font = font; tb->color_bg = color_bg; tb->color_fg = color_fg; break; } + pango_layout_set_font_description ( tb->layout, pfd ); + pango_font_description_free ( pfd ); } // outer code may need line height, width, etc void textbox_extents ( textbox *tb ) { - XftTextExtentsUtf8 ( display, tb->font, ( unsigned char * ) tb->text, strlen ( tb->text ), &tb->extents ); } // set the default text to display @@ -137,7 +146,9 @@ void textbox_text ( textbox *tb, char *text ) free ( tb->text ); } - tb->text = strdup ( text ); + tb->text = strdup ( text ); + pango_layout_set_text ( tb->layout, tb->text, strlen ( tb->text ) ); + tb->cursor = MAX ( 0, MIN ( ( int ) strlen ( text ), tb->cursor ) ); textbox_extents ( tb ); } @@ -154,17 +165,21 @@ void textbox_move ( textbox *tb, int x, int y ) void textbox_moveresize ( textbox *tb, int x, int y, int w, int h ) { if ( tb->flags & TB_AUTOHEIGHT ) { - h = tb->font->ascent + tb->font->descent; + h = textbox_get_font_height ( tb ); } if ( tb->flags & TB_AUTOWIDTH ) { if ( w > 1 ) { - w = MIN ( w, tb->extents.width + 2 * SIDE_MARGIN ); + w = MIN ( w, textbox_get_font_width ( tb ) + 2 * SIDE_MARGIN ); } else{ - w = tb->extents.width + 2 * SIDE_MARGIN; + w = textbox_get_font_width ( tb ) + 2 * SIDE_MARGIN; } } + else { + // set ellipsize + pango_layout_set_ellipsize ( tb->layout, PANGO_ELLIPSIZE_END ); + } if ( x != tb->x || y != tb->y || w != tb->w || h != tb->h ) { tb->x = x; @@ -199,6 +214,9 @@ void textbox_free ( textbox *tb ) if ( tb->text ) { free ( tb->text ); } + if ( tb->layout == NULL ) { + g_object_unref ( tb->layout ); + } XDestroyWindow ( display, tb->window ); free ( tb ); @@ -206,11 +224,9 @@ void textbox_free ( textbox *tb ) void textbox_draw ( textbox *tb ) { - XGlyphInfo extents; - - GC context = XCreateGC ( display, tb->window, 0, 0 ); - Pixmap canvas = XCreatePixmap ( display, tb->window, tb->w, tb->h, DefaultDepth ( display, DefaultScreen ( display ) ) ); - XftDraw *draw = XftDrawCreate ( display, canvas, DefaultVisual ( display, DefaultScreen ( display ) ), DefaultColormap ( display, DefaultScreen ( display ) ) ); + GC context = XCreateGC ( display, tb->window, 0, 0 ); + Pixmap canvas = XCreatePixmap ( display, tb->window, tb->w, tb->h, DefaultDepth ( display, DefaultScreen ( display ) ) ); + XftDraw *draw = XftDrawCreate ( display, canvas, DefaultVisual ( display, DefaultScreen ( display ) ), DefaultColormap ( display, DefaultScreen ( display ) ) ); // clear canvas XftDrawRect ( draw, &tb->color_bg, 0, 0, tb->w, tb->h ); @@ -218,46 +234,26 @@ void textbox_draw ( textbox *tb ) char *text = tb->text ? tb->text : ""; int text_len = strlen ( text ); int length = text_len; - int line_height = tb->font->ascent + tb->font->descent; + int line_height = textbox_get_font_height ( tb ); int line_width = 0; int cursor_x = 0; int cursor_width = MAX ( 2, line_height / 10 ); - if ( tb->flags & TB_EDITABLE ) { - int cursor_offset = 0; - cursor_offset = MIN ( tb->cursor, text_len ); + pango_layout_set_text ( tb->layout, tb->text, strlen ( tb->text ) ); - // Trailing spaces still go wrong.... - // The replace by _ is needed, one way or the other. - // Make a copy, and replace all trailing spaces. - char *test = strdup ( text ); - for ( int iter = strlen ( text ) - 1; iter >= 0 && test[iter] == ' '; iter-- ) { - test[iter] = '_'; - } - // calc cursor position - XftTextExtentsUtf8 ( display, tb->font, ( unsigned char * ) test, cursor_offset, &extents ); + if ( tb->flags & TB_EDITABLE ) { + PangoRectangle pos; + int cursor_offset = 0; + cursor_offset = MIN ( tb->cursor, text_len ); + pango_layout_get_cursor_pos ( tb->layout, cursor_offset, &pos, NULL ); // Add a small 4px offset between cursor and last glyph. - cursor_x = extents.width + ( ( extents.width > 0 ) ? 2 : 0 ); - free ( test ); + cursor_x = pos.x / PANGO_SCALE; } - // calc full input text width - // Calculate the right size, so no characters are cut off. - // TODO: Check performance of this. - do { - XftTextExtentsUtf8 ( display, tb->font, ( unsigned char * ) text, length, &extents ); - line_width = extents.width; - if ( line_width <= ( tb->w - 2 * SIDE_MARGIN ) ) { - break; - } + pango_layout_set_width ( tb->layout, PANGO_SCALE * ( tb->w - 2 * SIDE_MARGIN ) ); - for ( length -= 1; length > 0 && ( text[length] & 0xc0 ) == 0x80; length -= 1 ) { - ; - } - } while ( line_width > 0 ); - - int x = SIDE_MARGIN, y = tb->font->ascent; + int x = SIDE_MARGIN, y = 0; if ( tb->flags & TB_RIGHT ) { x = tb->w - line_width; @@ -268,7 +264,9 @@ void textbox_draw ( textbox *tb ) } // draw the text. - XftDrawStringUtf8 ( draw, &tb->color_fg, tb->font, x, y, ( unsigned char * ) text, length ); +// XftDrawStringUtf8 ( draw, &tb->color_fg, tb->font, x, y, ( unsigned char * ) text, length ); + + pango_xft_render_layout ( draw, &( tb->color_fg ), tb->layout, x, y ); // draw the cursor if ( tb->flags & TB_EDITABLE ) { @@ -422,38 +420,59 @@ int textbox_keypress ( textbox *tb, XEvent *ev ) */ void textbox_setup ( - const char *font_str, const char *font_active_str, const char *bg, const char *fg, const char *hlbg, const char *hlfg ) { Visual *visual = DefaultVisual ( display, DefaultScreen ( display ) ); Colormap colormap = DefaultColormap ( display, DefaultScreen ( display ) ); - font = XftFontOpenName ( display, DefaultScreen ( display ), font_str ); - font_active = XftFontOpenName ( display, DefaultScreen ( display ), font_active_str ); XftColorAllocName ( display, visual, colormap, fg, &color_fg ); XftColorAllocName ( display, visual, colormap, bg, &color_bg ); XftColorAllocName ( display, visual, colormap, hlfg, &color_hlfg ); XftColorAllocName ( display, visual, colormap, hlbg, &color_hlbg ); + + PangoFontMap *font_map = pango_xft_get_font_map ( display, DefaultScreen ( display ) ); + p_context = pango_font_map_create_context ( font_map ); } void textbox_cleanup () { - if ( font != NULL ) { + if ( p_context ) { Visual *visual = DefaultVisual ( display, DefaultScreen ( display ) ); Colormap colormap = DefaultColormap ( display, DefaultScreen ( display ) ); - XftFontClose ( display, font ); - font = NULL; - - XftFontClose ( display, font_active ); - font_active = NULL; XftColorFree ( display, visual, colormap, &color_fg ); XftColorFree ( display, visual, colormap, &color_bg ); XftColorFree ( display, visual, colormap, &color_hlfg ); XftColorFree ( display, visual, colormap, &color_hlbg ); + g_object_unref ( p_context ); + p_context = NULL; } } + +int textbox_get_width ( textbox *tb ) +{ + return textbox_get_font_width ( tb ) + 2 * SIDE_MARGIN; +} + +int textbox_get_height ( textbox *tb ) +{ + return textbox_get_font_height ( tb ); +} + +int textbox_get_font_height ( textbox *tb ) +{ + int height; + pango_layout_get_pixel_size ( tb->layout, NULL, &height ); + return height; +} + +int textbox_get_font_width ( textbox *tb ) +{ + int width; + pango_layout_get_pixel_size ( tb->layout, &width, NULL ); + return width; +}