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,19 @@ 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+
249+ static bool have_build_in_menu_keyboard (const struct colored_layout * cl )
250+ {
251+ return (have_built_in_menu (cl ) &&
252+ settings .built_in_menu_key_navigation == true );
253+ }
254+
236255static void get_text_size (PangoLayout * l , int * w , int * h , double scale ) {
237256 pango_layout_get_pixel_size (l , w , h );
238257 // scale the size down, because it may be rendered at higher DPI
@@ -321,6 +340,8 @@ static struct dimensions calculate_notification_dimensions(struct colored_layout
321340 if (have_progress_bar (cl ))
322341 dim .w = MAX (settings .progress_bar_min_width , dim .w );
323342
343+ dim .h += calculate_menu_height (cl );
344+
324345 dim .h = MAX (settings .height .min , dim .h );
325346 dim .h = MIN (settings .height .max , dim .h );
326347
@@ -442,6 +463,10 @@ static struct colored_layout *layout_from_notification(cairo_t *c, struct notifi
442463 g_error_free (err );
443464 }
444465
466+ if (have_built_in_menu (cl )) {
467+ menu_init (n );
468+ }
469+
445470 n -> first_render = false;
446471 return cl ;
447472}
@@ -502,7 +527,7 @@ static int layout_get_height(struct colored_layout *cl, double scale)
502527
503528 return (cl -> n -> icon_position == ICON_TOP && cl -> n -> icon )
504529 ? h_icon + h_text + h_progress_bar + vertical_padding
505- : MAX (h_text , h_icon ) + h_progress_bar ;
530+ : MAX (h_text , h_icon ) + h_progress_bar + calculate_menu_height ( cl ) ;
506531}
507532
508533/* Attempt to make internal radius more organic.
@@ -699,6 +724,153 @@ void draw_rounded_rect(cairo_t *c, float x, float y, int width, int height, int
699724 cairo_close_path (c );
700725}
701726
727+
728+ static int calculate_max_button_width (const struct colored_layout * cl )
729+ {
730+ int buttons = menu_get_count (cl -> n );
731+ if (buttons == 0 ) {
732+ return 0 ;
733+ }
734+ const PangoFontDescription * desc = pango_layout_get_font_description (cl -> l );
735+ const int fontsize = pango_font_description_get_size (desc ) / PANGO_SCALE ;
736+ int max_text_width = 0 ;
737+ for (int i = 0 ; i < buttons ; i ++ ) {
738+ char * label = menu_get_label (cl -> n , i );
739+ if (!label ) {
740+ continue ;
741+ }
742+ int text_width = strlen (label ) * fontsize ;
743+ if (text_width > max_text_width ) {
744+ max_text_width = text_width ;
745+ }
746+ }
747+ max_text_width = MAX (settings .menu_min_width , max_text_width );
748+ max_text_width = MIN (settings .menu_max_width , max_text_width );
749+ return max_text_width ;
750+ }
751+
752+ static int calculate_menu_per_row (const struct colored_layout * cl )
753+ {
754+ int menu_width = calculate_max_button_width (cl );
755+ if (menu_width <= 0 ) {
756+ return 0 ;
757+ }
758+
759+ int menu_count = settings .width .max / menu_width ;
760+ menu_count = MIN (settings .menu_max_per_row , menu_count );
761+ return menu_count ;
762+ }
763+
764+ static int calculate_menu_rows (const struct colored_layout * cl )
765+ {
766+ int buttons = menu_get_count (cl -> n );
767+ if (buttons <= 0 ) {
768+ return 0 ;
769+ }
770+
771+ int max_per_row = calculate_menu_per_row (cl );
772+ if (max_per_row < 1 ) {
773+ max_per_row = 1 ;
774+ }
775+
776+ int needed_rows = (buttons + max_per_row - 1 ) / max_per_row ;
777+ return MIN (needed_rows , settings .menu_max_rows );
778+ }
779+
780+ static int calculate_menu_height (const struct colored_layout * cl )
781+ {
782+ if (have_built_in_menu (cl )) {
783+ int rows = calculate_menu_rows (cl );
784+ return settings .menu_height * rows + settings .padding * rows ;
785+ } else {
786+ return 0 ;
787+ }
788+ }
789+
790+ static void draw_built_in_menu (cairo_t * c , struct colored_layout * cl , int area_x , int area_y , int area_width ,
791+ int area_height , double scale )
792+ {
793+ if (!have_built_in_menu (cl ))
794+ return ;
795+
796+ int buttons = menu_get_count (cl -> n );
797+ if (buttons == 0 ) {
798+ return ;
799+ }
800+
801+ int max_per_row = calculate_menu_per_row (cl );
802+ cl -> n -> actual_menu_per_row = max_per_row ;
803+ int rows = calculate_menu_rows (cl );
804+ int base_button_width = calculate_max_button_width (cl );
805+
806+ pango_layout_set_attributes (cl -> l , NULL );
807+ pango_layout_set_font_description (cl -> l , NULL );
808+ pango_layout_set_font_description (cl -> l , pango_fdesc );
809+ PangoAttrList * attr = pango_attr_list_new ();
810+ pango_layout_set_attributes (cl -> l , attr );
811+
812+ for (int row = 0 ; row < rows ; row ++ ) {
813+ int buttons_in_row = MIN (buttons - row * max_per_row , max_per_row );
814+ base_button_width = (area_width - settings .h_padding * (buttons_in_row + 1 )) / buttons_in_row ;
815+
816+ for (int col = 0 ; col < buttons_in_row ; col ++ ) {
817+ int button_index = row * max_per_row + col ;
818+ if (button_index >= buttons )
819+ break ;
820+
821+ char * label = menu_get_label (cl -> n , button_index );
822+ if (!label )
823+ continue ;
824+
825+ int x = area_x + settings .h_padding + col * (base_button_width + settings .h_padding );
826+ int y = area_y + row * (settings .menu_height + settings .padding );
827+ menu_set_position (cl -> n , button_index , x , y , base_button_width , settings .menu_height );
828+
829+ bool is_selected = have_build_in_menu_keyboard (cl ) &&
830+ (button_index == cl -> n -> selected_menu );
831+
832+ // set the color of the button according to the selected state
833+ if (is_selected ) {
834+ // backeffect: use the foreground color as the background color
835+ cairo_set_source_rgb (c , COLOR (cl , fg .r ), COLOR (cl , fg .g ), COLOR (cl , fg .b ));
836+ } else {
837+ // normal state: use the background color as the background color
838+ cairo_set_source_rgb (c , settings .menu_frame_color .r , settings .menu_frame_color .g ,
839+ settings .menu_frame_color .b );
840+ }
841+
842+ cairo_set_line_width (c , settings .menu_frame_width );
843+ draw_rounded_rect (c , x , y , base_button_width , settings .menu_height , settings .corner_radius , scale , settings .corners );
844+
845+ // selected items are filled, unselected items are only stroked
846+ if (is_selected || settings .menu_frame_fill )
847+ cairo_fill (c );
848+ else
849+ cairo_stroke (c );
850+
851+ pango_layout_set_text (cl -> l , label , -1 );
852+
853+ int text_width , text_height ;
854+ pango_layout_get_pixel_size (cl -> l , & text_width , & text_height );
855+ double text_x = x + (base_button_width - text_width ) / 2 ;
856+ double text_y = y + (settings .menu_height - text_height ) / 2 ;
857+
858+ // reverse the colors for the text
859+ if (is_selected ) {
860+ // For selected items: use the background color as the text color
861+ cairo_set_source_rgba (c , COLOR (cl , bg .r ), COLOR (cl , bg .g ), COLOR (cl , bg .b ), COLOR (cl , bg .a ));
862+ } else {
863+ // For unselected items: use the foreground color as the text color
864+ cairo_set_source_rgba (c , COLOR (cl , fg .r ), COLOR (cl , fg .g ), COLOR (cl , fg .b ), COLOR (cl , fg .a ));
865+ }
866+
867+ cairo_move_to (c , text_x , text_y );
868+ pango_cairo_show_layout (c , cl -> l );
869+ }
870+ }
871+ pango_attr_list_unref (attr );
872+ }
873+
702874static cairo_surface_t * render_background (cairo_surface_t * srf ,
703875 struct colored_layout * cl ,
704876 struct colored_layout * cl_next ,
@@ -784,10 +956,12 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
784956 layout_setup (cl , width , height , scale );
785957
786958 // 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- }
959+ int h_text_and_icon = height ;
960+ if (have_progress_bar (cl ))
961+ h_text_and_icon -= settings .progress_bar_height + settings .padding ;
962+
963+ if (have_built_in_menu (cl ))
964+ h_text_and_icon -= calculate_menu_height (cl );
791965
792966 int text_h = 0 ;
793967 if (!cl -> n -> hide_text ) {
@@ -799,9 +973,9 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
799973 text_y = settings .padding ;
800974
801975 if (settings .vertical_alignment == VERTICAL_CENTER ) {
802- text_y = h_without_progress_bar / 2 - text_h / 2 ;
976+ text_y = h_text_and_icon / 2 - text_h / 2 ;
803977 } else if (settings .vertical_alignment == VERTICAL_BOTTOM ) {
804- text_y = h_without_progress_bar - settings .padding - text_h ;
978+ text_y = h_text_and_icon - settings .padding - text_h ;
805979 if (text_y < 0 ) text_y = settings .padding ;
806980 } // else VERTICAL_TOP
807981
@@ -867,7 +1041,7 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
8671041 unsigned int frame_width = settings .progress_bar_frame_width ,
8681042 progress_width = MIN (width - 2 * settings .h_padding , settings .progress_bar_max_width ),
8691043 progress_height = settings .progress_bar_height - frame_width ,
870- frame_y = h_without_progress_bar ,
1044+ frame_y = h_text_and_icon ,
8711045 progress_width_without_frame = progress_width - 2 * frame_width ,
8721046 progress_width_1 = progress_width_without_frame * progress / 100 ,
8731047 progress_width_2 = progress_width_without_frame - 1 ,
@@ -923,6 +1097,15 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
9231097 scale , settings .progress_bar_corners );
9241098 cairo_stroke (c );
9251099 }
1100+
1101+ if (have_built_in_menu (cl )) {
1102+ int y = h_text_and_icon ;
1103+ if (have_progress_bar (cl )) {
1104+ y += settings .progress_bar_height + settings .padding ;
1105+ }
1106+ draw_built_in_menu (c , cl , 0 , y , width , height , scale );
1107+ }
1108+
9261109}
9271110
9281111static struct dimensions layout_render (cairo_surface_t * srf ,
@@ -1043,6 +1226,7 @@ void draw(void)
10431226 else if (!cl_next )
10441227 corners |= (settings .corners & C_BOT ) | _C_LAST ;
10451228
1229+ cl_this -> n -> displayed_top = dim .y ;
10461230 dim = layout_render (image_surface , cl_this , cl_next , dim , corners );
10471231 corners &= ~(C_TOP | _C_FIRST );
10481232 }
0 commit comments