Skip to content

Commit 079185e

Browse files
committed
Implement built-in focus when button is selected
The design now utilizes an inverted display when the key is in focus, with the first key being in focus when the menu appears. Signed-off-by: gujie <[email protected]>
1 parent a3edfd6 commit 079185e

File tree

1 file changed

+192
-8
lines changed

1 file changed

+192
-8
lines changed

src/draw.c

Lines changed: 192 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "settings.h"
2222
#include "utils.h"
2323
#include "icon-lookup.h"
24+
#include "menu.h"
2425

2526
struct colored_layout {
2627
PangoLayout *l;
@@ -36,6 +37,11 @@ window win;
3637

3738
PangoFontDescription *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+
236255
static 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+
702874
static 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

9281111
static 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

Comments
 (0)