Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions dunstrc
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,21 @@
mouse_middle_click = do_action, close_current
mouse_right_click = close_all

### action menu

built_in_menu = true
menu_frame_color = "#000000"

#set to true to fill the button with the frame color.
#set to false to draw the bounding box with the frame color and frame width.
menu_frame_fill = false
menu_frame_width = 1
menu_height = 30
menu_max_per_row = 4
menu_max_rows = 5
menu_max_width = 100
menu_min_width = 50

# Experimental features that may or may not work correctly. Do not expect them
# to have a consistent behaviour across releases.
[experimental]
Expand Down
173 changes: 165 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,132 @@ 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;
}
const PangoFontDescription *desc = pango_layout_get_font_description(cl->l);
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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This calculation assumes the font is monospace. Do we know this is the case for the button font? This assumption can be avoided by first rendering the font and asking pango about the width. That would also handle unicode characters better

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 = settings.width.max / 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.h_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.h_padding + col * (base_button_width + settings.h_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_rounded_rect(c, x, y, base_button_width, settings.menu_height, settings.corner_radius, scale, settings.corners);

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, round(text_x * scale), round(text_y * scale));
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 +929,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 +946,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 +1014,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 @@ -923,6 +1070,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 Expand Up @@ -1043,6 +1199,7 @@ void draw(void)
else if (!cl_next)
corners |= (settings.corners & C_BOT) | _C_LAST;

cl_this->n->displayed_top = dim.y;
dim = layout_render(image_surface, cl_this, cl_next, dim, corners);
corners &= ~(C_TOP | _C_FIRST);
}
Expand Down
23 changes: 23 additions & 0 deletions src/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,24 @@ struct notification *get_notification_at(const int y) {
return NULL;
}

bool handle_builtin_menu_click(int x, int y) {
if (!settings.built_in_menu) {
return false;
}

struct notification *n = get_notification_at(y);
if (n) {
if (menu_get_count(n) > 0) {
struct menu *m = menu_get_at(n, x, y);
if (m) {
signal_action_invoked(n, m->key);
return true;
}
}
}
return false;
}

void input_handle_click(unsigned int button, bool button_down, int mouse_x, int mouse_y){
LOG_I("Pointer handle button %i: %i", button, button_down);

Expand All @@ -63,6 +81,11 @@ void input_handle_click(unsigned int button, bool button_down, int mouse_x, int
return;
}

if (settings.built_in_menu){
if (handle_builtin_menu_click( mouse_x, mouse_y))
return;
}

enum mouse_action *acts;

switch (button) {
Expand Down
Loading