From 43053cdfc34539dd73ba6a028090038f99d4093e Mon Sep 17 00:00:00 2001 From: Aaron Ash Date: Wed, 12 Apr 2017 17:58:32 +0200 Subject: [PATCH] view: Add icon (basic) support --- Makefile.am | 7 +++++++ configure.ac | 1 + include/mode-private.h | 12 ++++++++++++ include/mode.h | 12 ++++++++++++ include/widgets/textbox.h | 19 +++++++++++++++++++ include/x11-helper.h | 8 ++++++++ meson.build | 1 + source/dialogs/combi.c | 13 +++++++++++++ source/mode.c | 12 ++++++++++++ source/view.c | 8 ++++++++ source/widgets/textbox.c | 39 +++++++++++++++++++++++++++++++++++---- source/x11-helper.c | 24 ++++++++++++++++++++++++ 12 files changed, 152 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index 8aaad607..0fb26097 100644 --- a/Makefile.am +++ b/Makefile.am @@ -123,6 +123,7 @@ rofi_CFLAGS=\ $(pango_CFLAGS)\ $(libsn_CFLAGS)\ $(cairo_CFLAGS)\ + $(librsvg_CFLAGS)\ -DMANPAGE_PATH="\"$(mandir)/\""\ -I$(top_srcdir)/include/\ -I$(top_builddir)/lexer/\ @@ -142,6 +143,7 @@ rofi_LDADD=\ $(libsn_LIBS)\ $(pango_LIBS)\ $(cairo_LIBS)\ + $(librsvg_LIBS)\ $(LIBS) ## @@ -247,6 +249,7 @@ history_test_CFLAGS=\ $(AM_CFLAGS)\ $(glib_CFLAGS)\ $(libsn_CFLAGS)\ + $(cairo_CFLAGS)\ -DTHEME_DIR=\"$(themedir)\"\ -DPLUGIN_PATH=\"${libdir}/rofi\"\ -I$(top_srcdir)/include/\ @@ -274,6 +277,7 @@ textbox_test_CFLAGS=\ $(GW_XCB_CFLAGS)\ $(cairo_CFLAGS)\ $(libsn_CFLAGS)\ + $(librsvg_CFLAGS)\ -DPLUGIN_PATH=\"${libdir}/rofi\"\ -DTHEME_DIR=\"$(themedir)\"\ -I$(top_srcdir)/include/\ @@ -287,6 +291,7 @@ textbox_test_LDADD=\ $(pango_LIBS)\ $(GW_XCB_LIBS)\ $(cairo_LIBS)\ + $(librsvg_LIBS)\ $(libsn_LIBS) helper_pidfile_CFLAGS=$(textbox_test_CFLAGS) @@ -428,6 +433,7 @@ helper_test_CFLAGS=\ $(pango_CFLAGS)\ $(GW_XCB_CFLAGS)\ $(cairo_CFLAGS)\ + $(librsvg_CFLAGS)\ $(libsn_CFLAGS)\ -DPLUGIN_PATH=\"${libdir}/rofi\"\ -DTHEME_DIR=\"$(themedir)\"\ @@ -442,6 +448,7 @@ helper_test_LDADD=\ $(pango_LIBS)\ $(GW_XCB_LIBS)\ $(libsn_LIBS)\ + $(librsvg_LIBS)\ $(cairo_LIBS) diff --git a/configure.ac b/configure.ac index 8dc4fb8e..6a9d123b 100644 --- a/configure.ac +++ b/configure.ac @@ -122,6 +122,7 @@ GW_CHECK_XCB([xcb-aux xcb-xkb xkbcommon xkbcommon-x11 xcb-ewmh xcb-icccm xcb-xrm PKG_CHECK_MODULES([pango], [pango pangocairo]) PKG_CHECK_MODULES([cairo], [cairo cairo-xcb]) PKG_CHECK_MODULES([libsn], [libstartup-notification-1.0 ]) +PKG_CHECK_MODULES([librsvg], [librsvg-2.0 ]) dnl --------------------------------------------------------------------- dnl check - Unit testing. diff --git a/include/mode-private.h b/include/mode-private.h index b22eccf0..14021183 100644 --- a/include/mode-private.h +++ b/include/mode-private.h @@ -53,6 +53,16 @@ typedef void ( *_mode_free )( Mode *data ); */ typedef char * ( *_mode_get_display_value )( const Mode *sw, unsigned int selected_line, int *state, GList **attribute_list, int get_entry ); +/** + * @param sw The #Mode pointer + * @param selected_line The selected line + * + * Obtains the icon if available + * + * @return Get the icon + */ +typedef cairo_surface_t * ( *_mode_get_icon )( const Mode *sw, unsigned int selected_line, int height ); + /** * @param sw The #Mode pointer * @param selected_line The selected line @@ -162,6 +172,8 @@ struct rofi_mode _mode_token_match _token_match; /** Get the string to display for the entry. */ _mode_get_display_value _get_display_value; + /** Get the icon for the entry. */ + _mode_get_icon _get_icon; /** Get the 'completed' entry. */ _mode_get_completion _get_completion; diff --git a/include/mode.h b/include/mode.h index fe237351..d9424ec2 100644 --- a/include/mode.h +++ b/include/mode.h @@ -27,6 +27,8 @@ #ifndef ROFI_MODE_H #define ROFI_MODE_H + +#include /** * @defgroup MODE Mode * @@ -120,6 +122,16 @@ unsigned int mode_get_num_entries ( const Mode *mode ); */ char * mode_get_display_value ( const Mode *mode, unsigned int selected_line, int *state, GList **attribute_list, int get_entry ); +/** + * @param mode The mode to query + * @param selected_line The entry to query + * + * Returns the icon for the selected_line + * + * @returns allocated new cairo_surface_t if applicable + */ +cairo_surface_t * mode_get_icon ( const Mode *mode, unsigned int selected_line, int height ); + /** * @param mode The mode to query * @param selected_line The entry to query diff --git a/include/widgets/textbox.h b/include/widgets/textbox.h index d2d56095..3a8b1583 100644 --- a/include/widgets/textbox.h +++ b/include/widgets/textbox.h @@ -59,6 +59,9 @@ typedef struct int markup; int changed; + cairo_surface_t *icon; // AA TODO - pass in icons for a textbox line if needed + int icon_index; + int blink; guint blink_timeout; @@ -141,6 +144,14 @@ void textbox_font ( textbox *tb, TextBoxFontType tbft ); */ void textbox_text ( textbox *tb, const char *text ); +/** + * @param tb Handle to the textbox + * @param text The icon to show on the textbox + * + * Set the text to show. Cursor is moved to end (if visible) + */ +void textbox_icon ( textbox *tb, cairo_surface_t *icon ); + /** * @param tb Handle to the textbox * @param action the #KeyBindingAction to execute on textbox @@ -277,6 +288,14 @@ void textbox_set_pango_context ( const char *font, PangoContext *p ); */ void textbox_set_pango_attributes ( textbox *tb, PangoAttrList *list ); +/** + * @param tb Handle to the textbox + * @param index character index to draw the icon at. -1 for no icon + * + * Sets the character index where the icon should be drawn + */ +void textbox_set_icon_index ( textbox *tb, int index ); + /** * @param tb Handle to the textbox * diff --git a/include/x11-helper.h b/include/x11-helper.h index 01b061e6..d80af3e3 100644 --- a/include/x11-helper.h +++ b/include/x11-helper.h @@ -29,6 +29,7 @@ #define X11_ROFI_HELPER_H #include #include +#include #include #include @@ -212,6 +213,13 @@ cairo_surface_t * x11_helper_get_bg_surface ( void ); */ cairo_surface_t *x11_helper_get_screenshot_surface ( void ); +/** + * Gets a surface from an svg path + * + * @returns a cairo surface from an svg path + */ +cairo_surface_t *cairo_image_surface_create_from_svg ( const gchar* file, int height ); + /** * Creates an internal represenation of the available monitors. * Used for positioning rofi. diff --git a/meson.build b/meson.build index b832b05f..cb4c76bc 100644 --- a/meson.build +++ b/meson.build @@ -46,6 +46,7 @@ deps = [ dependency('pango'), dependency('pangocairo'), dependency('xkbcommon'), + dependency('librsvg-2.0'), c_compiler.find_library('m', required: false), ] diff --git a/source/dialogs/combi.c b/source/dialogs/combi.c index 8fa4a438..5631c519 100644 --- a/source/dialogs/combi.c +++ b/source/dialogs/combi.c @@ -247,6 +247,18 @@ static char * combi_get_completion ( const Mode *sw, unsigned int index ) return NULL; } +static cairo_surface_t * combi_get_icon ( const Mode *sw, unsigned int index, int height ) +{ + CombiModePrivateData *pd = mode_get_private_data ( sw ); + for ( unsigned i = 0; i < pd->num_switchers; i++ ) { + if ( index >= pd->starts[i] && index < ( pd->starts[i] + pd->lengths[i] ) ) { + cairo_surface_t *icon = mode_get_icon ( pd->switchers[i].mode, index - pd->starts[i], height ); + return icon; + } + } + return NULL; +} + static char * combi_preprocess_input ( Mode *sw, const char *input ) { CombiModePrivateData *pd = mode_get_private_data ( sw ); @@ -285,6 +297,7 @@ Mode combi_mode = ._token_match = combi_mode_match, ._get_completion = combi_get_completion, ._get_display_value = combi_mgrv, + ._get_icon = combi_get_icon, ._preprocess_input = combi_preprocess_input, .private_data = NULL, .free = NULL diff --git a/source/mode.c b/source/mode.c index a7e552d3..5e070e3c 100644 --- a/source/mode.c +++ b/source/mode.c @@ -70,6 +70,18 @@ char * mode_get_display_value ( const Mode *mode, unsigned int selected_line, in return mode->_get_display_value ( mode, selected_line, state, attribute_list, get_entry ); } +cairo_surface_t * mode_get_icon ( const Mode *mode, unsigned int selected_line, int height ) +{ + g_assert ( mode != NULL ); + + if ( mode->_get_icon != NULL ) { + return mode->_get_icon ( mode, selected_line, height ); + } + else { + return NULL; + } +} + char * mode_get_completion ( const Mode *mode, unsigned int selected_line ) { g_assert ( mode != NULL ); diff --git a/source/view.c b/source/view.c index e33e89ed..4745990c 100644 --- a/source/view.c +++ b/source/view.c @@ -906,6 +906,14 @@ static void update_callback ( textbox *t, unsigned int index, void *udata, TextB else{ list = pango_attr_list_new (); } + int icon_height = textbox_get_font_height ( t ); + + cairo_surface_t *icon = mode_get_icon ( state->sw, state->line_map[index], icon_height ); + textbox_icon ( t, icon ); + + //AA TODO: Find a better way to position icon than tab char + gchar *firstspace = g_utf8_strchr ( t->text, -1, '\t' ); + textbox_set_icon_index ( t, ( firstspace == NULL ) ? -1 : g_utf8_pointer_to_offset ( t->text, firstspace ) + 1 ); if ( state->tokens && config.show_match ) { ThemeHighlight th = { HL_BOLD | HL_UNDERLINE, { 0.0, 0.0, 0.0, 0.0 } }; diff --git a/source/widgets/textbox.c b/source/widgets/textbox.c index 9fd9c0f5..ee51367a 100644 --- a/source/widgets/textbox.c +++ b/source/widgets/textbox.c @@ -186,6 +186,8 @@ textbox* textbox_create_full ( WidgetType type, const char *name, TextboxFlags f tb->widget.trigger_action = textbox_editable_trigger_action; } + tb->icon_index = -1; //Don't draw the icon by default + // Enabled by default tb->widget.enabled = rofi_theme_get_boolean ( WIDGET ( tb ), "enabled", TRUE ); return tb; @@ -267,6 +269,12 @@ void textbox_set_pango_attributes ( textbox *tb, PangoAttrList *list ) pango_layout_set_attributes ( tb->layout, list ); } +void textbox_set_icon_index ( textbox *tb, int index ) +{ + tb->icon_index = index; + widget_queue_redraw ( WIDGET ( tb ) ); +} + // set the default text to display void textbox_text ( textbox *tb, const char *text ) { @@ -297,6 +305,13 @@ void textbox_text ( textbox *tb, const char *text ) widget_queue_redraw ( WIDGET ( tb ) ); } +void textbox_icon ( textbox *tb, cairo_surface_t *icon ) +{ + tb->icon = icon; + + widget_queue_redraw ( WIDGET ( tb ) ); +} + // within the parent handled auto width/height modes void textbox_moveresize ( textbox *tb, int x, int y, int w, int h ) { @@ -363,7 +378,26 @@ static void textbox_draw ( widget *wid, cairo_t *draw ) } // Skip the side MARGIN on the X axis. - int x = widget_padding_get_left ( WIDGET ( tb ) ) + offset; + int x = widget_padding_get_left ( WIDGET ( tb ) ) + offset; + int top = widget_padding_get_top ( WIDGET ( tb ) ); + int y = top + ( pango_font_metrics_get_ascent ( tb->metrics ) - pango_layout_get_baseline ( tb->layout ) ) / PANGO_SCALE; + + // draw Icon + int iconheight = textbox_get_font_height ( tb ); + int translatex = ( textbox_get_estimated_char_height () * tb->icon_index / 2 ); + if ( tb->icon != NULL && tb->icon_index != -1 ) { + cairo_save ( draw ); + + /*int iconw = cairo_image_surface_get_width (tb->icon);*/ + int iconh = cairo_image_surface_get_height ( tb->icon ); + double scale = (double) iconheight / iconh; + + cairo_translate ( draw, translatex, 0 ); + cairo_scale ( draw, scale, scale ); + cairo_set_source_surface ( draw, tb->icon, x, y ); + cairo_paint ( draw ); + cairo_restore ( draw ); + } if ( tb->flags & TB_RIGHT ) { int line_width = 0; @@ -376,14 +410,11 @@ static void textbox_draw ( widget *wid, cairo_t *draw ) x = ( ( tb->widget.w - tw - widget_padding_get_padding_width ( WIDGET ( tb ) ) - offset ) ) / 2; } - int top = widget_padding_get_top ( WIDGET ( tb ) ); - rofi_theme_get_color ( WIDGET ( tb ), "foreground", draw ); // Text rofi_theme_get_color ( WIDGET ( tb ), "text", draw ); // draw the cursor if ( tb->flags & TB_EDITABLE && tb->blink ) { - int y = top + ( pango_font_metrics_get_ascent ( tb->metrics ) - pango_layout_get_baseline ( tb->layout ) ) / PANGO_SCALE; // We want to place the cursor based on the text shown. const char *text = pango_layout_get_text ( tb->layout ); // Clamp the position, should not be needed, but we are paranoid. diff --git a/source/x11-helper.c b/source/x11-helper.c index 689d0519..94b5b53d 100644 --- a/source/x11-helper.c +++ b/source/x11-helper.c @@ -133,6 +133,30 @@ cairo_surface_t * x11_helper_get_bg_surface ( void ) xcb->screen->width_in_pixels, xcb->screen->height_in_pixels ); } +cairo_surface_t* cairo_image_surface_create_from_svg ( const gchar* file, int height ) +{ + cairo_surface_t *surface; + cairo_t *cr; + RsvgHandle * handle; + RsvgDimensionData dimensions; + + handle = rsvg_handle_new_from_file ( file, NULL ); + rsvg_handle_get_dimensions ( handle, &dimensions ); + double scale = (double) height / dimensions.height; + surface = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, + (double) dimensions.width * scale, + (double) dimensions.height * scale ); + cr = cairo_create ( surface ); + cairo_scale ( cr, scale, scale ); + rsvg_handle_render_cairo ( handle, cr ); + cairo_destroy ( cr ); + + rsvg_handle_close ( handle, NULL ); + g_object_unref ( handle ); + + return surface; +} + // retrieve a text property from a window // technically we could use window_get_prop(), but this is better for character set support char* window_get_text_prop ( xcb_window_t w, xcb_atom_t atom )