Skip to content

Commit

Permalink
Implement built-in clickable menu rendering
Browse files Browse the repository at this point in the history
Add menu button drawing function.
Update layout calculations for menu height.
Integrate menu rendering into notification display.

Signed-off-by: LI Qingwu <[email protected]>
  • Loading branch information
Qingwu-Li committed Feb 8, 2025
1 parent d7719c1 commit 1c3052b
Showing 1 changed file with 173 additions and 8 deletions.
181 changes: 173 additions & 8 deletions src/draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "settings.h"
#include "utils.h"
#include "icon-lookup.h"
#include "menu.h"

struct colored_layout {
PangoLayout *l;
Expand All @@ -36,6 +37,11 @@ window win;

PangoFontDescription *pango_fdesc;

static int calculate_menu_height(const struct colored_layout *cl);
static int calculate_max_button_width(const struct colored_layout *cl);
static int calculate_menu_per_row(const struct colored_layout *cl);
static int calculate_menu_rows(const struct colored_layout *cl);

// NOTE: Saves some characters
#define COLOR(cl, field) (cl)->n->colors.field

Expand Down Expand Up @@ -233,6 +239,13 @@ static bool have_progress_bar(const struct colored_layout *cl)
!cl->is_xmore);
}

static bool have_built_in_menu(const struct colored_layout *cl)
{
return (g_hash_table_size(cl->n->actions)>0 &&
settings.built_in_menu == true &&
!cl->is_xmore);
}

static void get_text_size(PangoLayout *l, int *w, int *h, double scale) {
pango_layout_get_pixel_size(l, w, h);
// scale the size down, because it may be rendered at higher DPI
Expand Down Expand Up @@ -321,6 +334,8 @@ static struct dimensions calculate_notification_dimensions(struct colored_layout
if (have_progress_bar(cl))
dim.w = MAX(settings.progress_bar_min_width, dim.w);

dim.h += calculate_menu_height(cl);

dim.h = MAX(settings.height.min, dim.h);
dim.h = MIN(settings.height.max, dim.h);

Expand Down Expand Up @@ -442,6 +457,10 @@ static struct colored_layout *layout_from_notification(cairo_t *c, struct notifi
g_error_free(err);
}

if (have_built_in_menu(cl)) {
menu_init(n);
}

n->first_render = false;
return cl;
}
Expand Down Expand Up @@ -502,7 +521,7 @@ static int layout_get_height(struct colored_layout *cl, double scale)

return (cl->n->icon_position == ICON_TOP && cl->n->icon)
? h_icon + h_text + h_progress_bar + vertical_padding
: MAX(h_text, h_icon) + h_progress_bar;
: MAX(h_text, h_icon) + h_progress_bar + calculate_menu_height(cl);
}

/* Attempt to make internal radius more organic.
Expand Down Expand Up @@ -699,6 +718,141 @@ void draw_rounded_rect(cairo_t *c, float x, float y, int width, int height, int
cairo_close_path(c);
}


static int calculate_max_button_width(const struct colored_layout *cl)
{
int buttons = menu_get_count(cl->n);
if (buttons == 0) {
return 0;
}
if (!cl->l) {
fprintf(stderr, "cl->l is NULL\n");
return 0;
}

const PangoFontDescription *desc = pango_layout_get_font_description(cl->l);

if (!desc) {
return 0;
}
const int fontsize = pango_font_description_get_size(desc) / PANGO_SCALE;
int max_text_width = 0;
for (int i = 0; i < buttons; i++) {
char *label = menu_get_label(cl->n, i);
if (!label) {
continue;
}
int text_width = strlen(label) * fontsize;
if (text_width > max_text_width) {
max_text_width = text_width;
}
}
max_text_width = MAX(settings.menu_min_width, max_text_width);
max_text_width = MIN(settings.menu_max_width, max_text_width);
return max_text_width;
}

static int calculate_menu_per_row(const struct colored_layout *cl)
{
int menu_width = calculate_max_button_width(cl);
if (menu_width <= 0) {
return 0;
}

int menu_count = 300 / menu_width;
menu_count = MIN(settings.menu_max_per_row, menu_count);
return menu_count;
}

static int calculate_menu_rows(const struct colored_layout *cl)
{
int buttons = menu_get_count(cl->n);
if (buttons <= 0) {
return 0;
}

int max_per_row = calculate_menu_per_row(cl);
if (max_per_row < 1) {
max_per_row = 1;
}

int needed_rows = (buttons + max_per_row - 1) / max_per_row;
return MIN(needed_rows, settings.menu_max_rows);
}

static int calculate_menu_height(const struct colored_layout *cl)
{
if (have_built_in_menu(cl)) {
int rows = calculate_menu_rows(cl);
return settings.menu_height * rows + settings.padding * rows;
} else {
return 0;
}
}

static void draw_built_in_menu(cairo_t *c, struct colored_layout *cl, int area_x, int area_y, int area_width,
int area_height, double scale)
{
if (!have_built_in_menu(cl))
return;

int buttons = menu_get_count(cl->n);
if (buttons == 0) {
return;
}

int max_per_row = calculate_menu_per_row(cl);
int rows = calculate_menu_rows(cl);
int base_button_width = calculate_max_button_width(cl);

pango_layout_set_attributes(cl->l, NULL);
pango_layout_set_font_description(cl->l, NULL);
pango_layout_set_font_description(cl->l, pango_fdesc);
PangoAttrList *attr = pango_attr_list_new();
pango_layout_set_attributes(cl->l, attr);

for (int row = 0; row < rows; row++) {
int buttons_in_row = MIN(buttons - row * max_per_row, max_per_row);
base_button_width = (area_width - settings.padding * (buttons_in_row + 1)) / buttons_in_row;

for (int col = 0; col < buttons_in_row; col++) {
int button_index = row * max_per_row + col;
if (button_index >= buttons)
break;

char *label = menu_get_label(cl->n, button_index);
if (!label)
continue;

int x = area_x + settings.padding + col * (base_button_width + settings.padding);
int y = area_y + row * (settings.menu_height + settings.padding);
menu_set_position(cl->n, button_index, x, y, base_button_width, settings.menu_height);

cairo_set_source_rgb(c, settings.menu_frame_color.r, settings.menu_frame_color.g,
settings.menu_frame_color.b);
cairo_set_line_width(c, settings.menu_frame_width);
draw_rect(c, x, y, base_button_width, settings.menu_height, scale);

if (settings.menu_frame_fill)
cairo_fill(c);
else
cairo_stroke(c);

pango_layout_set_text(cl->l, label, -1);

int text_width, text_height;
pango_layout_get_pixel_size(cl->l, &text_width, &text_height);
double text_x = x + (base_button_width - text_width) / 2;
double text_y = y + (settings.menu_height - text_height) / 2;

cairo_set_source_rgba(c, COLOR(cl, fg.r), COLOR(cl, fg.g), COLOR(cl, fg.b), COLOR(cl, fg.a));
cairo_move_to(c, text_x, text_y);
pango_cairo_show_layout(c, cl->l);
}
}
pango_attr_list_unref(attr);
}

static cairo_surface_t *render_background(cairo_surface_t *srf,
struct colored_layout *cl,
struct colored_layout *cl_next,
Expand Down Expand Up @@ -784,10 +938,12 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
layout_setup(cl, width, height, scale);

// NOTE: Includes paddings!
int h_without_progress_bar = height;
if (have_progress_bar(cl)) {
h_without_progress_bar -= settings.progress_bar_height + settings.padding;
}
int h_text_and_icon = height;
if (have_progress_bar(cl))
h_text_and_icon -= settings.progress_bar_height + settings.padding;

if (have_built_in_menu(cl))
h_text_and_icon -= calculate_menu_height(cl);

int text_h = 0;
if (!cl->n->hide_text) {
Expand All @@ -799,9 +955,9 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
text_y = settings.padding;

if (settings.vertical_alignment == VERTICAL_CENTER) {
text_y = h_without_progress_bar / 2 - text_h / 2;
text_y = h_text_and_icon / 2 - text_h / 2;
} else if (settings.vertical_alignment == VERTICAL_BOTTOM) {
text_y = h_without_progress_bar - settings.padding - text_h;
text_y = h_text_and_icon - settings.padding - text_h;
if (text_y < 0) text_y = settings.padding;
} // else VERTICAL_TOP

Expand Down Expand Up @@ -867,7 +1023,7 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
unsigned int frame_width = settings.progress_bar_frame_width,
progress_width = MIN(width - 2 * settings.h_padding, settings.progress_bar_max_width),
progress_height = settings.progress_bar_height - frame_width,
frame_y = h_without_progress_bar,
frame_y = h_text_and_icon,
progress_width_without_frame = progress_width - 2 * frame_width,
progress_width_1 = progress_width_without_frame * progress / 100,
progress_width_2 = progress_width_without_frame - 1;
Expand Down Expand Up @@ -922,6 +1078,15 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
scale, settings.progress_bar_corners);
cairo_stroke(c);
}

if (have_built_in_menu(cl)) {
int y = h_text_and_icon;
if (have_progress_bar(cl)) {
y += settings.progress_bar_height + settings.padding;
}
draw_built_in_menu(c, cl, 0, y, width, height, scale);
}

}

static struct dimensions layout_render(cairo_surface_t *srf,
Expand Down

0 comments on commit 1c3052b

Please sign in to comment.