2121#include "settings.h"
2222#include "utils.h"
2323#include "icon-lookup.h"
24+ #include "menu.h"
2425
2526struct colored_layout {
2627 PangoLayout * l ;
@@ -36,6 +37,11 @@ window win;
3637
3738PangoFontDescription * pango_fdesc ;
3839
40+ static int calculate_menu_height (const struct colored_layout * cl );
41+ static int calculate_max_button_width (const struct colored_layout * cl );
42+ static int calculate_menu_per_row (const struct colored_layout * cl );
43+ static int calculate_menu_rows (const struct colored_layout * cl );
44+
3945// NOTE: Saves some characters
4046#define COLOR (cl , field ) (cl)->n->colors.field
4147
@@ -233,6 +239,13 @@ static bool have_progress_bar(const struct colored_layout *cl)
233239 !cl -> is_xmore );
234240}
235241
242+ static bool have_built_in_menu (const struct colored_layout * cl )
243+ {
244+ return (g_hash_table_size (cl -> n -> actions )> 0 &&
245+ settings .built_in_menu == true &&
246+ !cl -> is_xmore );
247+ }
248+
236249static void get_text_size (PangoLayout * l , int * w , int * h , double scale ) {
237250 pango_layout_get_pixel_size (l , w , h );
238251 // scale the size down, because it may be rendered at higher DPI
@@ -321,6 +334,8 @@ static struct dimensions calculate_notification_dimensions(struct colored_layout
321334 if (have_progress_bar (cl ))
322335 dim .w = MAX (settings .progress_bar_min_width , dim .w );
323336
337+ dim .h += calculate_menu_height (cl );
338+
324339 dim .h = MAX (settings .height .min , dim .h );
325340 dim .h = MIN (settings .height .max , dim .h );
326341
@@ -442,6 +457,10 @@ static struct colored_layout *layout_from_notification(cairo_t *c, struct notifi
442457 g_error_free (err );
443458 }
444459
460+ if (have_built_in_menu (cl )) {
461+ menu_init (n );
462+ }
463+
445464 n -> first_render = false;
446465 return cl ;
447466}
@@ -502,7 +521,7 @@ static int layout_get_height(struct colored_layout *cl, double scale)
502521
503522 return (cl -> n -> icon_position == ICON_TOP && cl -> n -> icon )
504523 ? h_icon + h_text + h_progress_bar + vertical_padding
505- : MAX (h_text , h_icon ) + h_progress_bar ;
524+ : MAX (h_text , h_icon ) + h_progress_bar + calculate_menu_height ( cl ) ;
506525}
507526
508527/* Attempt to make internal radius more organic.
@@ -699,6 +718,132 @@ void draw_rounded_rect(cairo_t *c, float x, float y, int width, int height, int
699718 cairo_close_path (c );
700719}
701720
721+
722+ static int calculate_max_button_width (const struct colored_layout * cl )
723+ {
724+ int buttons = menu_get_count (cl -> n );
725+ if (buttons == 0 ) {
726+ return 0 ;
727+ }
728+ const PangoFontDescription * desc = pango_layout_get_font_description (cl -> l );
729+ const int fontsize = pango_font_description_get_size (desc ) / PANGO_SCALE ;
730+ int max_text_width = 0 ;
731+ for (int i = 0 ; i < buttons ; i ++ ) {
732+ char * label = menu_get_label (cl -> n , i );
733+ if (!label ) {
734+ continue ;
735+ }
736+ int text_width = strlen (label ) * fontsize ;
737+ if (text_width > max_text_width ) {
738+ max_text_width = text_width ;
739+ }
740+ }
741+ max_text_width = MAX (settings .menu_min_width , max_text_width );
742+ max_text_width = MIN (settings .menu_max_width , max_text_width );
743+ return max_text_width ;
744+ }
745+
746+ static int calculate_menu_per_row (const struct colored_layout * cl )
747+ {
748+ int menu_width = calculate_max_button_width (cl );
749+ if (menu_width <= 0 ) {
750+ return 0 ;
751+ }
752+
753+ int menu_count = 300 / menu_width ;
754+ menu_count = MIN (settings .menu_max_per_row , menu_count );
755+ return menu_count ;
756+ }
757+
758+ static int calculate_menu_rows (const struct colored_layout * cl )
759+ {
760+ int buttons = menu_get_count (cl -> n );
761+ if (buttons <= 0 ) {
762+ return 0 ;
763+ }
764+
765+ int max_per_row = calculate_menu_per_row (cl );
766+ if (max_per_row < 1 ) {
767+ max_per_row = 1 ;
768+ }
769+
770+ int needed_rows = (buttons + max_per_row - 1 ) / max_per_row ;
771+ return MIN (needed_rows , settings .menu_max_rows );
772+ }
773+
774+ static int calculate_menu_height (const struct colored_layout * cl )
775+ {
776+ if (have_built_in_menu (cl )) {
777+ int rows = calculate_menu_rows (cl );
778+ return settings .menu_height * rows + settings .padding * rows ;
779+ } else {
780+ return 0 ;
781+ }
782+ }
783+
784+ static void draw_built_in_menu (cairo_t * c , struct colored_layout * cl , int area_x , int area_y , int area_width ,
785+ int area_height , double scale )
786+ {
787+ if (!have_built_in_menu (cl ))
788+ return ;
789+
790+ int buttons = menu_get_count (cl -> n );
791+ if (buttons == 0 ) {
792+ return ;
793+ }
794+
795+ int max_per_row = calculate_menu_per_row (cl );
796+ int rows = calculate_menu_rows (cl );
797+ int base_button_width = calculate_max_button_width (cl );
798+
799+ pango_layout_set_attributes (cl -> l , NULL );
800+ pango_layout_set_font_description (cl -> l , NULL );
801+ pango_layout_set_font_description (cl -> l , pango_fdesc );
802+ PangoAttrList * attr = pango_attr_list_new ();
803+ pango_layout_set_attributes (cl -> l , attr );
804+
805+ for (int row = 0 ; row < rows ; row ++ ) {
806+ int buttons_in_row = MIN (buttons - row * max_per_row , max_per_row );
807+ base_button_width = (area_width - settings .padding * (buttons_in_row + 1 )) / buttons_in_row ;
808+
809+ for (int col = 0 ; col < buttons_in_row ; col ++ ) {
810+ int button_index = row * max_per_row + col ;
811+ if (button_index >= buttons )
812+ break ;
813+
814+ char * label = menu_get_label (cl -> n , button_index );
815+ if (!label )
816+ continue ;
817+
818+ int x = area_x + settings .padding + col * (base_button_width + settings .padding );
819+ int y = area_y + row * (settings .menu_height + settings .padding );
820+ menu_set_position (cl -> n , button_index , x , y , base_button_width , settings .menu_height );
821+
822+ cairo_set_source_rgb (c , settings .menu_frame_color .r , settings .menu_frame_color .g ,
823+ settings .menu_frame_color .b );
824+ cairo_set_line_width (c , settings .menu_frame_width );
825+ draw_rect (c , x , y , base_button_width , settings .menu_height , scale );
826+
827+ if (settings .menu_frame_fill )
828+ cairo_fill (c );
829+ else
830+ cairo_stroke (c );
831+
832+ pango_layout_set_text (cl -> l , label , -1 );
833+
834+ int text_width , text_height ;
835+ pango_layout_get_pixel_size (cl -> l , & text_width , & text_height );
836+ double text_x = x + (base_button_width - text_width ) / 2 ;
837+ double text_y = y + (settings .menu_height - text_height ) / 2 ;
838+
839+ cairo_set_source_rgba (c , COLOR (cl , fg .r ), COLOR (cl , fg .g ), COLOR (cl , fg .b ), COLOR (cl , fg .a ));
840+ cairo_move_to (c , text_x , text_y );
841+ pango_cairo_show_layout (c , cl -> l );
842+ }
843+ }
844+ pango_attr_list_unref (attr );
845+ }
846+
702847static cairo_surface_t * render_background (cairo_surface_t * srf ,
703848 struct colored_layout * cl ,
704849 struct colored_layout * cl_next ,
@@ -784,10 +929,12 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
784929 layout_setup (cl , width , height , scale );
785930
786931 // NOTE: Includes paddings!
787- int h_without_progress_bar = height ;
788- if (have_progress_bar (cl )) {
789- h_without_progress_bar -= settings .progress_bar_height + settings .padding ;
790- }
932+ int h_text_and_icon = height ;
933+ if (have_progress_bar (cl ))
934+ h_text_and_icon -= settings .progress_bar_height + settings .padding ;
935+
936+ if (have_built_in_menu (cl ))
937+ h_text_and_icon -= calculate_menu_height (cl );
791938
792939 int text_h = 0 ;
793940 if (!cl -> n -> hide_text ) {
@@ -799,9 +946,9 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
799946 text_y = settings .padding ;
800947
801948 if (settings .vertical_alignment == VERTICAL_CENTER ) {
802- text_y = h_without_progress_bar / 2 - text_h / 2 ;
949+ text_y = h_text_and_icon / 2 - text_h / 2 ;
803950 } else if (settings .vertical_alignment == VERTICAL_BOTTOM ) {
804- text_y = h_without_progress_bar - settings .padding - text_h ;
951+ text_y = h_text_and_icon - settings .padding - text_h ;
805952 if (text_y < 0 ) text_y = settings .padding ;
806953 } // else VERTICAL_TOP
807954
@@ -867,7 +1014,7 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
8671014 unsigned int frame_width = settings .progress_bar_frame_width ,
8681015 progress_width = MIN (width - 2 * settings .h_padding , settings .progress_bar_max_width ),
8691016 progress_height = settings .progress_bar_height - frame_width ,
870- frame_y = h_without_progress_bar ,
1017+ frame_y = h_text_and_icon ,
8711018 progress_width_without_frame = progress_width - 2 * frame_width ,
8721019 progress_width_1 = progress_width_without_frame * progress / 100 ,
8731020 progress_width_2 = progress_width_without_frame - 1 ;
@@ -922,6 +1069,15 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
9221069 scale , settings .progress_bar_corners );
9231070 cairo_stroke (c );
9241071 }
1072+
1073+ if (have_built_in_menu (cl )) {
1074+ int y = h_text_and_icon ;
1075+ if (have_progress_bar (cl )) {
1076+ y += settings .progress_bar_height + settings .padding ;
1077+ }
1078+ draw_built_in_menu (c , cl , 0 , y , width , height , scale );
1079+ }
1080+
9251081}
9261082
9271083static struct dimensions layout_render (cairo_surface_t * srf ,
0 commit comments