Implement support for round corners

This commit is contained in:
dj95 2018-05-26 22:45:08 +03:00 committed by Nikos Tsipinakis
parent de89328359
commit a3034d5f62
9 changed files with 220 additions and 6 deletions

View File

@ -41,6 +41,7 @@ settings_t defaults = {
.ignore_newline = false,
.line_height = 0, /* if line height < font height, it will be raised to font height */
.notification_height = 0, /* if notification height < font height and padding, it will be raised */
.corner_radius = 0,
.separator_height = 2, /* height of the separator line between two notifications */
.padding = 0,

View File

@ -28,6 +28,7 @@ pkg_config_packs := dbus-1 \
pangocairo \
x11 \
xinerama \
xext \
"xrandr >= 1.5" \
xscrnsaver

View File

@ -460,6 +460,18 @@ By enabling this setting dunst will not be able to detect when a monitor is
connected or disconnected which might break follow mode if the screen layout
changes.
=item B<corner_radius> (default: 0)
Define the corner radius in pixels. A corner radius of 0 will result in
rectangular shaped notifications.
By enabling this setting the outer border and the frame will be shaped.
If you have multiple notifications, the whole window is shaped, not every
single notification.
To avoid the corners clipping the icon or text the corner radius will be
automatically lowered to half of the notification height if it exceeds it.
=back
=head2 Shortcut section

View File

@ -207,6 +207,13 @@
# debug: all less than unimportant stuff
verbosity = mesg
# Define the corner radius of the notification window
# in pixel size. If the radius is 0, you have no rounded
# corners.
# The radius will be automatically lowered if it exceeds half of the
# notification height to avoid clipping text and/or icons.
corner_radius = 0
### Legacy
# Use the Xinerama extension instead of RandR for multi-monitor support.

View File

@ -172,6 +172,8 @@ static struct dimensions calculate_dimensions(GSList *layouts)
dim.h += 2 * settings.frame_width;
dim.h += (g_slist_length(layouts) - 1) * settings.separator_height;
dim.corner_radius = settings.corner_radius;
int text_width = 0, total_width = 0;
for (GSList *iter = layouts; iter; iter = iter->next) {
colored_layout *cl = iter->data;
@ -217,6 +219,8 @@ static struct dimensions calculate_dimensions(GSList *layouts)
dim.h += h;
text_width = MAX(w, text_width);
}
dim.corner_radius = MIN(dim.corner_radius, h/2);
}
if (dim.w <= 0) {
@ -389,12 +393,103 @@ static int layout_get_height(colored_layout *cl)
return MAX(h, h_icon);
}
static void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, bool first, bool last)
{
const float degrees = M_PI / 180.0;
if (first && last) {
cairo_new_sub_path(c);
cairo_arc(c,
x + width - corner_radius,
y + corner_radius,
corner_radius,
-90 * degrees,
0 * degrees);
cairo_arc(c,
x + width - corner_radius,
y + height - corner_radius,
corner_radius,
0 * degrees,
90 * degrees);
cairo_arc(c,
x + corner_radius,
y + height - corner_radius,
corner_radius,
90 * degrees,
180 * degrees);
cairo_arc(c,
x + corner_radius,
y + corner_radius,
corner_radius,
180 * degrees,
270 * degrees);
cairo_close_path(c);
} else if (first) {
cairo_new_sub_path(c);
cairo_arc(c,
x + width - corner_radius,
y + corner_radius,
corner_radius,
-90 * degrees,
0 * degrees);
cairo_arc(c,
x + width,
y + height,
0,
0,
0);
cairo_arc(c,
x,
y + height,
0,
0,
0);
cairo_arc(c,
x + corner_radius,
y + corner_radius,
corner_radius,
180 * degrees,
270 * degrees);
cairo_close_path(c);
} else if (last) {
cairo_new_sub_path(c);
cairo_arc(c,
x + width,
y,
0,
0,
0);
cairo_arc(c,
x + width - corner_radius,
y + height - corner_radius,
corner_radius,
0 * degrees,
90 * degrees);
cairo_arc(c,
x + corner_radius,
y + height - corner_radius,
corner_radius,
90 * degrees,
180 * degrees);
cairo_arc(c,
x,
y,
0,
180 * degrees,
270 * degrees);
cairo_close_path(c);
} else {
cairo_rectangle(c, x, y, width, height);
}
}
static cairo_surface_t *render_background(cairo_surface_t *srf,
colored_layout *cl,
colored_layout *cl_next,
int y,
int width,
int height,
int corner_radius,
bool first,
bool last,
int *ret_width)
@ -403,12 +498,15 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
cairo_t *c = cairo_create(srf);
if (first) height += settings.frame_width;
if (last) height += settings.frame_width;
else height += settings.separator_height;
if (first)
height += settings.frame_width;
if (last)
height += settings.frame_width;
else
height += settings.separator_height;
cairo_set_source_rgb(c, cl->frame.r, cl->frame.g, cl->frame.b);
cairo_rectangle(c, x, y, width, height);
draw_rounded_rect(c, x, y, width, height, corner_radius, first, last);
cairo_fill(c);
/* adding frame */
@ -426,7 +524,7 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
height -= settings.separator_height;
cairo_set_source_rgb(c, cl->bg.r, cl->bg.g, cl->bg.b);
cairo_rectangle(c, x, y, width, height);
draw_rounded_rect(c, x, y, width, height, corner_radius, first, last);
cairo_fill(c);
if ( settings.sep_color != SEP_FRAME
@ -504,7 +602,7 @@ static struct dimensions layout_render(cairo_surface_t *srf,
int bg_width = 0;
int bg_height = MAX(settings.notification_height, (2 * settings.padding) + cl_h);
cairo_surface_t *content = render_background(srf, cl, cl_next, dim.y, dim.w, bg_height, first, last, &bg_width);
cairo_surface_t *content = render_background(srf, cl, cl_next, dim.y, dim.w, bg_height, dim.corner_radius, first, last, &bg_width);
cairo_t *c = cairo_create(content);
render_content(c, cl, bg_width);

View File

@ -368,6 +368,12 @@ void load_settings(char *cmdline_config_path)
"Transparency. range 0-100"
);
settings.corner_radius = option_get_int(
"global",
"corner_radius", "-corner_radius", defaults.corner_radius,
"Window corner radius"
);
{
char *c = option_get_string(
"global",

View File

@ -84,6 +84,7 @@ typedef struct _settings {
keyboard_shortcut history_ks;
keyboard_shortcut context_ks;
bool force_xinerama;
int corner_radius;
} settings_t;
extern settings_t settings;

View File

@ -6,6 +6,7 @@
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/shape.h>
#include <assert.h>
#include <cairo-xlib.h>
#include <cairo.h>
@ -80,6 +81,89 @@ static void x_win_move(window_x11 *win, int x, int y, int width, int height)
}
}
void x_win_round_corners(window_x11 *win, const int rad)
{
const int width = win->dim.w;
const int height = win->dim.h;
Pixmap mask = XCreatePixmap(xctx.dpy, win->xwin, width, height, 1);
XGCValues xgcv;
GC shape_gc = XCreateGC(xctx.dpy, mask, 0, &xgcv);
const int dia = 2 * rad;
XSetForeground(xctx.dpy, shape_gc, 0);
XFillRectangle(xctx.dpy,
mask,
shape_gc,
0,
0,
width,
height);
XSetForeground(xctx.dpy, shape_gc, 1);
XFillArc(xctx.dpy,
mask,
shape_gc,
0,
0,
dia,
dia,
0,
360 * 64);
XFillArc(xctx.dpy,
mask,
shape_gc,
width - dia - 1,
0,
dia,
dia,
0,
360 * 64);
XFillArc(xctx.dpy,
mask,
shape_gc,
0,
height - dia - 1,
dia,
dia,
0,
360 * 64);
XFillArc(xctx.dpy,
mask,
shape_gc,
width - dia - 1,
height - dia - 1,
dia,
dia,
0,
360 * 64);
XFillRectangle(xctx.dpy,
mask,
shape_gc,
rad,
0,
width-dia,
height);
XFillRectangle(xctx.dpy,
mask,
shape_gc,
0,
rad,
width,
height-dia);
XShapeCombineMask(xctx.dpy, win->xwin, ShapeBounding, 0, 0, mask, ShapeSet);
XFreePixmap(xctx.dpy, mask);
XShapeSelectInput(xctx.dpy,
win->xwin, ShapeNotifyMask);
}
void x_display_surface(cairo_surface_t *srf, window_x11 *win, const struct dimensions *dim)
{
x_win_move(win, dim->x, dim->y, dim->w, dim->h);
@ -89,6 +173,8 @@ void x_display_surface(cairo_surface_t *srf, window_x11 *win, const struct dimen
cairo_paint(win->c_ctx);
cairo_show_page(win->c_ctx);
x_win_round_corners(win, dim->corner_radius);
XFlush(xctx.dpy);
}

View File

@ -31,6 +31,8 @@ struct dimensions {
int y;
int w;
int h;
int corner_radius;
};
typedef struct _xctx {