21
21
#include "settings.h"
22
22
#include "utils.h"
23
23
#include "icon-lookup.h"
24
+ #include "menu.h"
24
25
25
26
struct colored_layout {
26
27
PangoLayout * l ;
@@ -36,6 +37,11 @@ window win;
36
37
37
38
PangoFontDescription * pango_fdesc ;
38
39
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
+
39
45
// NOTE: Saves some characters
40
46
#define COLOR (cl , field ) (cl)->n->colors.field
41
47
@@ -233,6 +239,19 @@ static bool have_progress_bar(const struct colored_layout *cl)
233
239
!cl -> is_xmore );
234
240
}
235
241
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
+
236
255
static void get_text_size (PangoLayout * l , int * w , int * h , double scale ) {
237
256
pango_layout_get_pixel_size (l , w , h );
238
257
// 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
321
340
if (have_progress_bar (cl ))
322
341
dim .w = MAX (settings .progress_bar_min_width , dim .w );
323
342
343
+ dim .h += calculate_menu_height (cl );
344
+
324
345
dim .h = MAX (settings .height .min , dim .h );
325
346
dim .h = MIN (settings .height .max , dim .h );
326
347
@@ -442,6 +463,10 @@ static struct colored_layout *layout_from_notification(cairo_t *c, struct notifi
442
463
g_error_free (err );
443
464
}
444
465
466
+ if (have_built_in_menu (cl )) {
467
+ menu_init (n );
468
+ }
469
+
445
470
n -> first_render = false;
446
471
return cl ;
447
472
}
@@ -502,7 +527,7 @@ static int layout_get_height(struct colored_layout *cl, double scale)
502
527
503
528
return (cl -> n -> icon_position == ICON_TOP && cl -> n -> icon )
504
529
? 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 ) ;
506
531
}
507
532
508
533
/* 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
699
724
cairo_close_path (c );
700
725
}
701
726
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
+
702
874
static cairo_surface_t * render_background (cairo_surface_t * srf ,
703
875
struct colored_layout * cl ,
704
876
struct colored_layout * cl_next ,
@@ -784,10 +956,12 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
784
956
layout_setup (cl , width , height , scale );
785
957
786
958
// 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 );
791
965
792
966
int text_h = 0 ;
793
967
if (!cl -> n -> hide_text ) {
@@ -799,9 +973,9 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
799
973
text_y = settings .padding ;
800
974
801
975
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 ;
803
977
} 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 ;
805
979
if (text_y < 0 ) text_y = settings .padding ;
806
980
} // else VERTICAL_TOP
807
981
@@ -867,7 +1041,7 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
867
1041
unsigned int frame_width = settings .progress_bar_frame_width ,
868
1042
progress_width = MIN (width - 2 * settings .h_padding , settings .progress_bar_max_width ),
869
1043
progress_height = settings .progress_bar_height - frame_width ,
870
- frame_y = h_without_progress_bar ,
1044
+ frame_y = h_text_and_icon ,
871
1045
progress_width_without_frame = progress_width - 2 * frame_width ,
872
1046
progress_width_1 = progress_width_without_frame * progress / 100 ,
873
1047
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
923
1097
scale , settings .progress_bar_corners );
924
1098
cairo_stroke (c );
925
1099
}
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
+
926
1109
}
927
1110
928
1111
static struct dimensions layout_render (cairo_surface_t * srf ,
@@ -1043,6 +1226,7 @@ void draw(void)
1043
1226
else if (!cl_next )
1044
1227
corners |= (settings .corners & C_BOT ) | _C_LAST ;
1045
1228
1229
+ cl_this -> n -> displayed_top = dim .y ;
1046
1230
dim = layout_render (image_surface , cl_this , cl_next , dim , corners );
1047
1231
corners &= ~(C_TOP | _C_FIRST );
1048
1232
}
0 commit comments