Skip to content

Commit 20033b8

Browse files
authored
Merge pull request #1342 from bynect/dynamic-height
Implement dynamic height and change vertical padding
2 parents b00dca6 + 2b407f2 commit 20033b8

File tree

15 files changed

+413
-205
lines changed

15 files changed

+413
-205
lines changed

Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ dunstify: dunstify.o
9090
${CC} -o ${@} dunstify.o ${DUNSTIFY_CFLAGS} ${DUNSTIFY_LDFLAGS}
9191
endif
9292

93-
.PHONY: test test-valgrind test-coverage
93+
.PHONY: test test-valgrind test-coverage functional-tests
9494
test: test/test clean-coverage-run
9595
# Make sure an error code is returned when the test fails
9696
/usr/bin/env bash -c 'set -euo pipefail;\
@@ -124,6 +124,9 @@ test/%.o: test/%.c src/%.c
124124
test/test: ${OBJ} ${TEST_OBJ}
125125
${CC} -o ${@} ${TEST_OBJ} $(filter-out ${TEST_OBJ:test/%=src/%},${OBJ}) ${CFLAGS} ${LDFLAGS}
126126

127+
functional-tests: dunst dunstify
128+
PREFIX=. ./test/functional-tests/test.sh
129+
127130
.PHONY: doc doc-doxygen
128131
doc: docs/dunst.1 docs/dunst.5 docs/dunstctl.1
129132

@@ -213,7 +216,7 @@ clean-wayland-protocols:
213216
install-service install-service-dbus install-service-systemd \
214217
uninstall uninstall-dunstctl uninstall-dunstrc \
215218
uninstall-service uninstall-service-dbus uninstall-service-systemd \
216-
uninstall-keepconf uninstall-purge
219+
uninstall-keepconf uninstall-purge
217220
install: install-dunst install-dunstctl install-dunstrc install-service
218221

219222
install-dunst: dunst doc

docs/dunst.5.pod

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,11 @@ old geometry config as follows:
8484

8585
In the new config you can then set the following variables (make sure to remove
8686
any negative signs)
87-
width = <width>
88-
height = <height>
89-
offset = <offset>
90-
origin = top-right # or top-left, or any other direction you prefer
87+
88+
width = <width>
89+
height = <height>
90+
offset = <offset>
91+
origin = top-right # or top-left, or any other direction you prefer
9192

9293
=item B<width>
9394

@@ -96,8 +97,9 @@ specify a constant width or two numbers for the minimum and maximum width. The
9697
notification will expand from the minimum width as neccesary.
9798

9899
Examples:
99-
width = 300 # constant width of 300
100-
width = (0, 300) # width between 0 and 300
100+
101+
width = 300 # constant width of 300
102+
width = (0, 300) # width between 0 and 300
101103

102104
When setting a width bigger than the screen, dunst will clamp the width to the
103105
screen width. So if you want the notifcation to stretch the entire screen
@@ -106,7 +108,16 @@ screens exceed (e.g. 10000).
106108

107109
=item B<height>
108110

109-
The maximum height of a single notification.
111+
The height of each notification in pixels. This can be a single number to
112+
specify a constant height or two numbers for the minimum and maximum width. The
113+
notification will expand from the minimum height as neccesary.
114+
115+
Examples:
116+
117+
height = 300 # constant height of 300
118+
height = (0, 300) # height between 0 and 300
119+
120+
Note that unlike width, different notifications can have diffrent height values.
110121

111122
=item B<notification_limit> (default: 20)
112123

@@ -123,15 +134,16 @@ more information.
123134
The origin of the notification window on the screen. It can then be moved with
124135
offset.
125136
Origin can be one of:
126-
top-left
127-
top-center
128-
top-right
129-
bottom-left
130-
bottom-center
131-
bottom-right
132-
left-center
133-
center
134-
right-center
137+
138+
top-left
139+
top-center
140+
top-right
141+
bottom-left
142+
bottom-center
143+
bottom-right
144+
left-center
145+
center
146+
right-center
135147

136148
=item B<offset> format: (horizontal, vertical)
137149

@@ -140,8 +152,9 @@ of the screen specified by B<origin>. A negative offset will lead to the
140152
notification being off screen.
141153

142154
Examples:
143-
origin = top-right
144-
offset = (10, 300) # a margin of 10 pixels from the right and 300 pixels from the top
155+
156+
origin = top-right
157+
offset = (10, 300) # a margin of 10 pixels from the right and 300 pixels from the top
145158

146159
For backwards compatibility the syntax NxN is also accepted.
147160

@@ -416,7 +429,7 @@ removed from the format.
416429
=item B<vertical_alignment> (values: [top/center/bottom], default: center)
417430

418431
Defines how the text and icon should be aligned vertically within the
419-
notification. If icons are disabled, this option has no effect.
432+
notification.
420433

421434
=item B<show_age_threshold> (default: 60)
422435

@@ -563,13 +576,13 @@ actions to be executed in sequence.
563576

564577
B<Defaults:>
565578

566-
=over 12
579+
=over 8
567580

568-
=item * C<mouse_left_click="close_current">
581+
=item * C<mouse_left_click=close_current>
569582

570-
=item * C<mouse_middle_click="do_action, close_current">
583+
=item * C<mouse_middle_click=do_action, close_current>
571584

572-
=item * C<mouse_right_click="close_all">
585+
=item * C<mouse_right_click=close_all>
573586

574587
=back
575588

dunstrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
# constant width of 300
2727
width = 300
2828

29-
# The maximum height of a single notification, excluding the frame.
30-
height = 300
29+
# The height of a single notification, excluding the frame.
30+
height = (0, 300)
3131

3232
# Position the notification in the top right corner
3333
origin = top-right

src/draw.c

Lines changed: 70 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ static void layout_setup(struct colored_layout *cl, int width, int height, doubl
202202
int icon_width = cl->icon ? get_icon_width(cl->icon, scale) + horizontal_padding : 0;
203203
int text_width = width - 2 * settings.h_padding - (cl->n->icon_position == ICON_TOP ? 0 : icon_width);
204204
int progress_bar_height = have_progress_bar(cl) ? settings.progress_bar_height + settings.padding : 0;
205-
int max_text_height = MAX(0, settings.height.max - progress_bar_height - 2 * settings.padding);
205+
int max_text_height = MAX(0, height - progress_bar_height - 2 * settings.padding);
206206
layout_setup_pango(cl->l, text_width, max_text_height, cl->n->word_wrap, cl->n->ellipsize, cl->n->alignment);
207207
}
208208

@@ -245,11 +245,13 @@ static struct dimensions calculate_notification_dimensions(struct colored_layout
245245
dim.h += progress_bar_height;
246246
dim.w = dim.text_width + icon_width + 2 * settings.h_padding;
247247

248-
dim.h = MIN(settings.height.max, dim.h + settings.padding * 2);
249-
dim.w = MAX(settings.width.min, dim.w);
250248
if (have_progress_bar(cl))
251249
dim.w = MAX(settings.progress_bar_min_width, dim.w);
252250

251+
dim.h = MIN(settings.height.max, dim.h + settings.padding * 2);
252+
dim.h = MAX(settings.height.min, dim.h);
253+
254+
dim.w = MAX(settings.width.min, dim.w);
253255
dim.w = MIN(settings.width.max, dim.w);
254256

255257
cl->n->displayed_height = dim.h;
@@ -426,11 +428,9 @@ static int layout_get_height(struct colored_layout *cl, double scale)
426428
}
427429

428430

429-
if (cl->n->icon_position == ICON_TOP && cl->n->icon) {
430-
return h_icon + h_text + h_progress_bar + vertical_padding;
431-
} else {
432-
return MAX(h_text, h_icon) + h_progress_bar;
433-
}
431+
return (cl->n->icon_position == ICON_TOP && cl->n->icon)
432+
? h_icon + h_text + h_progress_bar + vertical_padding
433+
: MAX(h_text, h_icon) + h_progress_bar;
434434
}
435435

436436
/* Attempt to make internal radius more organic.
@@ -705,88 +705,96 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
705705
round(width * scale), round(height * scale));
706706
}
707707

708-
static void render_content(cairo_t *c, struct colored_layout *cl, int width, double scale)
708+
static void render_content(cairo_t *c, struct colored_layout *cl, int width, int height, double scale)
709709
{
710710
// Redo layout setup, while knowing the width. This is to make
711711
// alignment work correctly
712-
layout_setup(cl, width, settings.height.max, scale);
712+
layout_setup(cl, width, height, scale);
713713

714-
const int h = layout_get_height(cl, scale);
715-
LOG_D("Layout height %i", h);
716-
int h_without_progress_bar = h;
714+
// NOTE: Includes paddings!
715+
int h_without_progress_bar = height;
717716
if (have_progress_bar(cl)) {
718-
h_without_progress_bar -= settings.progress_bar_height + settings.padding;
717+
h_without_progress_bar -= settings.progress_bar_height + settings.padding;
719718
}
720719

720+
int text_h = 0;
721721
if (!cl->n->hide_text) {
722-
int h_text = 0;
723-
get_text_size(cl->l, NULL, &h_text, scale);
722+
get_text_size(cl->l, NULL, &text_h, scale);
723+
}
724724

725-
int text_x = settings.h_padding,
726-
text_y = settings.padding + h_without_progress_bar / 2 - h_text / 2;
727-
728-
// text positioning
729-
if (cl->icon) {
730-
// vertical alignment
731-
if (settings.vertical_alignment == VERTICAL_TOP) {
732-
text_y = settings.padding;
733-
} else if (settings.vertical_alignment == VERTICAL_BOTTOM) {
734-
text_y = h_without_progress_bar + settings.padding - h_text;
735-
if (text_y < 0)
736-
text_y = settings.padding;
737-
} // else VERTICAL_CENTER
738-
739-
// icon position
740-
if (cl->n->icon_position == ICON_LEFT) {
741-
text_x = get_icon_width(cl->icon, scale) + settings.h_padding + get_horizontal_text_icon_padding(cl->n);
742-
} else if (cl->n->icon_position == ICON_TOP) {
743-
text_y = get_icon_height(cl->icon, scale) + settings.padding + get_vertical_text_icon_padding(cl->n);
744-
} // else ICON_RIGHT
745-
}
746-
cairo_move_to(c, round(text_x * scale), round(text_y * scale));
725+
// text vertical alignment
726+
int text_x = settings.h_padding,
727+
text_y = settings.padding;
747728

748-
cairo_set_source_rgba(c, COLOR(cl, fg.r), COLOR(cl, fg.g), COLOR(cl, fg.b), COLOR(cl, fg.a));
749-
pango_cairo_update_layout(c, cl->l);
750-
pango_cairo_show_layout(c, cl->l);
751-
}
729+
if (settings.vertical_alignment == VERTICAL_CENTER) {
730+
text_y = h_without_progress_bar / 2 - text_h / 2;
731+
} else if (settings.vertical_alignment == VERTICAL_BOTTOM) {
732+
text_y = h_without_progress_bar - settings.padding - text_h;
733+
} // else VERTICAL_TOP
752734

753735
// icon positioning
754-
if (cl->icon) {
755-
unsigned int image_width = get_icon_width(cl->icon, scale),
756-
image_height = get_icon_height(cl->icon, scale),
757-
image_x = width - settings.h_padding - image_width,
758-
image_y = settings.padding + h_without_progress_bar/2 - image_height/2;
736+
if (cl->icon && cl->n->icon_position != ICON_OFF) {
737+
int image_width = get_icon_width(cl->icon, scale),
738+
image_height = get_icon_height(cl->icon, scale),
739+
image_x = width - settings.h_padding - image_width,
740+
image_y = text_y,
741+
v_padding = get_vertical_text_icon_padding(cl->n);
759742

760743
// vertical alignment
761-
if (settings.vertical_alignment == VERTICAL_TOP) {
762-
image_y = settings.padding;
763-
} else if (settings.vertical_alignment == VERTICAL_BOTTOM) {
764-
image_y = h_without_progress_bar + settings.padding - image_height;
765-
if (image_y < settings.padding || image_y > h_without_progress_bar)
766-
image_y = settings.padding;
767-
} // else VERTICAL_CENTER
744+
switch (settings.vertical_alignment) {
745+
case VERTICAL_TOP:
746+
if (cl->n->icon_position == ICON_TOP) {
747+
// Shift text downward
748+
text_y += image_height + v_padding;
749+
}
750+
break;
751+
case VERTICAL_CENTER:
752+
if (cl->n->icon_position == ICON_TOP) {
753+
// Adjust text and image by half
754+
image_y -= (image_height + v_padding) / 2;
755+
text_y += (image_height + v_padding) / 2;
756+
} else {
757+
image_y += text_h / 2 - image_height / 2;
758+
}
759+
break;
760+
case VERTICAL_BOTTOM:
761+
if (cl->n->icon_position == ICON_TOP) {
762+
image_y -= image_height + v_padding;
763+
} else {
764+
image_y -= image_height - text_h;
765+
}
766+
break;
767+
}
768768

769769
// icon position
770-
if (cl->n->icon_position == ICON_LEFT) {
770+
if (cl->n->icon_position == ICON_TOP) {
771+
image_x = (width - image_width) / 2;
772+
} else if (cl->n->icon_position == ICON_LEFT) {
771773
image_x = settings.h_padding;
772-
} else if (cl->n->icon_position == ICON_TOP) {
773-
image_y = settings.padding;
774-
image_x = width/2 - image_width/2;
774+
text_x += image_width + get_horizontal_text_icon_padding(cl->n);
775775
} // else ICON_RIGHT
776776

777777
cairo_set_source_surface(c, cl->icon, round(image_x * scale), round(image_y * scale));
778778
draw_rounded_rect(c, image_x, image_y, image_width, image_height, settings.icon_corner_radius, scale, settings.icon_corners);
779779
cairo_fill(c);
780780
}
781781

782+
// text positioning
783+
if (!cl->n->hide_text) {
784+
cairo_move_to(c, round(text_x * scale), round(text_y * scale));
785+
cairo_set_source_rgba(c, COLOR(cl, fg.r), COLOR(cl, fg.g), COLOR(cl, fg.b), COLOR(cl, fg.a));
786+
pango_cairo_update_layout(c, cl->l);
787+
pango_cairo_show_layout(c, cl->l);
788+
}
789+
782790
// progress bar positioning
783791
if (have_progress_bar(cl)) {
784792
int progress = MIN(cl->n->progress, 100);
785793
unsigned int frame_x = 0;
786794
unsigned int frame_width = settings.progress_bar_frame_width,
787795
progress_width = MIN(width - 2 * settings.h_padding, settings.progress_bar_max_width),
788796
progress_height = settings.progress_bar_height - frame_width,
789-
frame_y = settings.padding + h - settings.progress_bar_height,
797+
frame_y = h_without_progress_bar,
790798
progress_width_without_frame = progress_width - 2 * frame_width,
791799
progress_width_1 = progress_width_without_frame * progress / 100,
792800
progress_width_2 = progress_width_without_frame - 1;
@@ -853,11 +861,12 @@ static struct dimensions layout_render(cairo_surface_t *srf,
853861

854862
int bg_width = 0;
855863
int bg_height = MIN(settings.height.max, (2 * settings.padding) + cl_h);
864+
bg_height = MAX(settings.height.min, bg_height);
856865

857866
cairo_surface_t *content = render_background(srf, cl, cl_next, dim.y, dim.w, bg_height, dim.corner_radius, corners, &bg_width, scale);
858867
cairo_t *c = cairo_create(content);
859868

860-
render_content(c, cl, bg_width, scale);
869+
render_content(c, cl, bg_width, bg_height, scale);
861870

862871
/* adding frame */
863872
if (corners & (C_TOP | _C_FIRST))
@@ -866,10 +875,7 @@ static struct dimensions layout_render(cairo_surface_t *srf,
866875
if (corners & (C_BOT | _C_LAST))
867876
dim.y += settings.frame_width;
868877

869-
if ((2 * settings.padding + cl_h) < settings.height.max)
870-
dim.y += cl_h + 2 * settings.padding;
871-
else
872-
dim.y += settings.height.max;
878+
dim.y += bg_height;
873879

874880
if (settings.gap_size)
875881
dim.y += settings.gap_size;

src/settings.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,14 @@ void check_and_correct_settings(struct settings *s) {
223223
DIE("setting width min (%i) is always greather than max (%i)", s->width.min, s->width.max);
224224
}
225225

226+
if (s->height.min == INT_MIN) {
227+
s->height.min = 0;
228+
}
226229
if (s->height.min < 0 || s->height.max < 0) {
227230
DIE("setting height does not support negative values");
228231
}
229-
if (s->height.min != s->height.max) {
230-
LOG_W("Dynamic height is not yet supported");
232+
if (s->height.min > s->height.max) {
233+
DIE("setting height min (%i) is always greather than max (%i)", s->height.min, s->height.max);
231234
}
232235

233236
if (s->offset.x == INT_MIN || s->offset.y == INT_MAX) {

src/settings_data.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,9 +1528,9 @@ static const struct setting allowed_settings[] = {
15281528
{
15291529
.name = "height",
15301530
.section = "global",
1531-
.description = "The maximum height of a single notification, excluding the frame.",
1531+
.description = "The height of a notification, excluding the frame.",
15321532
.type = TYPE_LENGTH,
1533-
.default_value = "300",
1533+
.default_value = "(0, 300)",
15341534
.value = &settings.height,
15351535
.parser = NULL,
15361536
.parser_data = NULL,

0 commit comments

Comments
 (0)