Skip to main content

pito_footer/
footer.rs

1use ratatui::{
2    buffer::Buffer,
3    layout::{Position, Rect},
4    style::Style,
5    widgets::Widget,
6};
7
8use crate::confirm::ConfirmBar;
9use crate::input::InputBar;
10use crate::key::Key;
11use crate::styles::{Styles, Tone};
12use crate::text::{self, Pen};
13
14const SEPARATOR: &str = " ยท ";
15const MOST: usize = 64;
16const BESIDE: u16 = 2;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[non_exhaustive]
20pub struct Hint<'a> {
21    pub key: &'a str,
22    pub label: &'a str,
23    pub lead: &'a str,
24    pub rank: u8,
25    pub pinned: bool,
26}
27
28impl<'a> Hint<'a> {
29    pub const fn new(key: &'a str, label: &'a str) -> Self {
30        Hint {
31            key,
32            label,
33            lead: "",
34            rank: 0,
35            pinned: false,
36        }
37    }
38
39    pub const fn rank(mut self, rank: u8) -> Self {
40        self.rank = rank;
41        self
42    }
43
44    pub const fn lead(mut self, lead: &'a str) -> Self {
45        self.lead = lead;
46        self
47    }
48
49    pub const fn pinned(mut self) -> Self {
50        self.pinned = true;
51        self
52    }
53
54    fn width(&self) -> u16 {
55        let mut total = 0u16;
56        for part in [self.lead, self.key, self.label] {
57            if part.is_empty() {
58                continue;
59            }
60            if total > 0 {
61                total = total.saturating_add(1);
62            }
63            total = total.saturating_add(text::width(part));
64        }
65        total
66    }
67
68    fn draw(&self, pen: &mut Pen, key: Style, muted: Style, room: u16) {
69        let stop = pen.x.saturating_add(room);
70        let mut first = true;
71        for (part, style) in [(self.lead, muted), (self.key, key), (self.label, muted)] {
72            if part.is_empty() {
73                continue;
74            }
75            if !first {
76                pen.clip(" ", muted, stop.saturating_sub(pen.x));
77            }
78            first = false;
79            pen.clip(part, style, stop.saturating_sub(pen.x));
80        }
81    }
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85#[non_exhaustive]
86pub struct Notice<'a> {
87    pub text: &'a str,
88    pub tone: Tone,
89    pub over: bool,
90}
91
92impl<'a> Notice<'a> {
93    pub const fn new(text: &'a str, tone: Tone) -> Self {
94        Notice {
95            text,
96            tone,
97            over: false,
98        }
99    }
100
101    pub const fn legend(text: &'a str) -> Self {
102        Notice::new(text, Tone::Muted)
103    }
104
105    pub const fn accent(text: &'a str) -> Self {
106        Notice::new(text, Tone::Accent)
107    }
108
109    pub const fn alert(text: &'a str) -> Self {
110        Notice::new(text, Tone::Alert)
111    }
112
113    pub const fn good(text: &'a str) -> Self {
114        Notice::new(text, Tone::Good)
115    }
116
117    pub const fn over(mut self) -> Self {
118        self.over = true;
119        self
120    }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub struct Help {
125    open: bool,
126    on: Key,
127}
128
129impl Default for Help {
130    fn default() -> Self {
131        Help::new(true)
132    }
133}
134
135impl Help {
136    pub const fn new(open: bool) -> Self {
137        Help {
138            open,
139            on: Key::Char('?'),
140        }
141    }
142
143    pub const fn toggle_on(mut self, key: Key) -> Self {
144        self.on = key;
145        self
146    }
147
148    pub fn open(&self) -> bool {
149        self.open
150    }
151
152    pub fn toggle(&mut self) {
153        self.open = !self.open;
154    }
155
156    pub fn wants(&self, key: Key) -> bool {
157        key == self.on
158    }
159
160    pub fn key(&mut self, key: Key) -> bool {
161        if self.wants(key) {
162            self.toggle();
163            return true;
164        }
165        false
166    }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170#[non_exhaustive]
171pub enum Part<'a> {
172    Text(&'a str, Style),
173    Spans(&'a [(&'a str, Style)]),
174    Hints(&'a [Hint<'a>]),
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
178#[non_exhaustive]
179pub struct Segment<'a> {
180    pub part: Part<'a>,
181    pub rank: u8,
182    pub right: bool,
183    pub shrink: bool,
184}
185
186impl<'a> Segment<'a> {
187    pub const fn text(text: &'a str, style: Style) -> Self {
188        Segment {
189            part: Part::Text(text, style),
190            rank: 0,
191            right: false,
192            shrink: false,
193        }
194    }
195
196    pub const fn spans(spans: &'a [(&'a str, Style)]) -> Self {
197        Segment {
198            part: Part::Spans(spans),
199            rank: 0,
200            right: false,
201            shrink: false,
202        }
203    }
204
205    pub const fn hints(hints: &'a [Hint<'a>]) -> Self {
206        Segment {
207            part: Part::Hints(hints),
208            rank: 0,
209            right: false,
210            shrink: true,
211        }
212    }
213
214    pub const fn rank(mut self, rank: u8) -> Self {
215        self.rank = rank;
216        self
217    }
218
219    pub const fn right(mut self) -> Self {
220        self.right = true;
221        self
222    }
223
224    pub const fn shrink(mut self) -> Self {
225        self.shrink = true;
226        self
227    }
228}
229
230fn mask(hints: &[Hint], keep: impl Fn(&Hint) -> bool) -> u64 {
231    hints
232        .iter()
233        .take(MOST)
234        .enumerate()
235        .filter(|(_, hint)| keep(hint))
236        .fold(0u64, |kept, (index, _)| kept | 1 << index)
237}
238
239#[derive(Debug, Clone, Copy)]
240struct Fit {
241    kept: u64,
242    widths: [u16; MOST],
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
246pub struct Footer<'a> {
247    hints: &'a [Hint<'a>],
248    segments: &'a [Segment<'a>],
249    notice: Option<Notice<'a>>,
250    confirm: Option<ConfirmBar<'a>>,
251    input: Option<InputBar<'a>>,
252    name: &'a str,
253    version: &'a str,
254    separator: &'a str,
255    gap: u16,
256    indent: u16,
257    open: bool,
258    rule: bool,
259    wrap: bool,
260    styles: Styles,
261}
262
263impl<'a> Footer<'a> {
264    pub fn new(hints: &'a [Hint<'a>]) -> Self {
265        Footer {
266            hints,
267            segments: &[],
268            notice: None,
269            confirm: None,
270            input: None,
271            name: "",
272            version: "",
273            separator: SEPARATOR,
274            gap: 1,
275            indent: 0,
276            open: true,
277            rule: true,
278            wrap: false,
279            styles: Styles::new(),
280        }
281    }
282
283    pub fn status(segments: &'a [Segment<'a>]) -> Self {
284        Footer {
285            segments,
286            rule: false,
287            ..Footer::new(&[])
288        }
289    }
290
291    pub fn notice(mut self, notice: Option<Notice<'a>>) -> Self {
292        self.notice = notice.filter(|notice| !notice.text.is_empty());
293        self
294    }
295
296    pub fn confirm(mut self, confirm: Option<ConfirmBar<'a>>) -> Self {
297        self.confirm = confirm;
298        self
299    }
300
301    pub fn input(mut self, input: Option<InputBar<'a>>) -> Self {
302        self.input = input;
303        self
304    }
305
306    pub fn version(mut self, name: &'a str, version: &'a str) -> Self {
307        self.name = name;
308        self.version = version;
309        self
310    }
311
312    pub fn separator(mut self, separator: &'a str) -> Self {
313        self.separator = separator;
314        self
315    }
316
317    pub fn gap(mut self, gap: u16) -> Self {
318        self.gap = gap;
319        self
320    }
321
322    pub fn indent(mut self, indent: u16) -> Self {
323        self.indent = indent;
324        self
325    }
326
327    pub fn open(mut self, open: bool) -> Self {
328        self.open = open;
329        self
330    }
331
332    pub fn help(self, help: &Help) -> Self {
333        self.open(help.open())
334    }
335
336    pub fn rule(mut self, rule: bool) -> Self {
337        self.rule = rule;
338        self
339    }
340
341    pub fn wrap(mut self, wrap: bool) -> Self {
342        self.wrap = wrap;
343        self
344    }
345
346    pub fn styles(mut self, styles: Styles) -> Self {
347        self.styles = styles;
348        self
349    }
350
351    pub fn height(&self, width: u16) -> u16 {
352        if let Some(confirm) = self.confirm {
353            return confirm.rule(false).height() + u16::from(self.rule);
354        }
355        if let Some(input) = self.input {
356            return input.rule(false).height() + u16::from(self.rule);
357        }
358        let inner = self.inner(width);
359        let lines =
360            self.line_count(inner) + u16::from(self.notice.is_some()) + self.pinned_lines(inner);
361        if lines == 0 {
362            0
363        } else {
364            lines + u16::from(self.rule)
365        }
366    }
367
368    pub fn shown(&self, key: &str, width: u16) -> bool {
369        let width = self.inner(width);
370        if self.confirm.is_some() || self.input.is_some() {
371            return false;
372        }
373        if self.takes_over(width) {
374            return self.pinned_lines(width) > 0 && self.pinned().shown(key, width);
375        }
376        if self.line_count(width) == 0 {
377            return false;
378        }
379        if self.wrapping() {
380            return self
381                .hints
382                .iter()
383                .any(|hint| hint.key == key && self.visible(hint));
384        }
385        let fit = self.fit(width);
386        (0..self.count()).any(|index| {
387            if fit.kept & (1 << index) == 0 {
388                return false;
389            }
390            let Part::Hints(hints) = self.segment(index).part else {
391                return false;
392            };
393            let kept = self.kept_hints(hints, fit.widths[index]);
394            hints
395                .iter()
396                .take(MOST)
397                .enumerate()
398                .any(|(at, hint)| kept & (1 << at) != 0 && hint.key == key)
399        })
400    }
401
402    pub fn cursor(&self, area: Rect) -> Option<Position> {
403        if self.confirm.is_some() {
404            return None;
405        }
406        let input = self.input?;
407        input.rule(false).cursor(self.body(area))
408    }
409
410    fn body(&self, area: Rect) -> Rect {
411        let rule = u16::from(self.rule).min(area.height);
412        Rect {
413            x: area.x.saturating_add(self.indent.min(area.width)),
414            y: area.y.saturating_add(rule),
415            width: self.inner(area.width),
416            height: area.height - rule,
417        }
418    }
419
420    fn inner(&self, width: u16) -> u16 {
421        width.saturating_sub(self.indent)
422    }
423
424    fn count(&self) -> usize {
425        if self.segments.is_empty() {
426            1
427        } else {
428            self.segments.len().min(MOST)
429        }
430    }
431
432    fn segment(&self, index: usize) -> Segment<'a> {
433        if self.segments.is_empty() {
434            Segment::hints(self.hints)
435        } else {
436            self.segments[index]
437        }
438    }
439
440    fn wrapping(&self) -> bool {
441        self.wrap && self.segments.is_empty()
442    }
443
444    fn visible(&self, hint: &Hint) -> bool {
445        self.open || hint.pinned
446    }
447
448    fn any_visible(&self) -> bool {
449        (0..self.count()).any(|index| match self.segment(index).part {
450            Part::Text(text, _) => !text.is_empty(),
451            Part::Spans(spans) => spans.iter().any(|(text, _)| !text.is_empty()),
452            Part::Hints(hints) => hints.iter().any(|hint| self.visible(hint)),
453        })
454    }
455
456    fn line_count(&self, width: u16) -> u16 {
457        if !self.any_visible() {
458            return u16::from(self.tag(width) > 0);
459        }
460        if !self.wrapping() {
461            return 1;
462        }
463        self.flow(width, |_, _, _| {}).0
464    }
465
466    fn tag(&self, width: u16) -> u16 {
467        if self.version.is_empty() {
468            return 0;
469        }
470        let mut total = text::width(self.version);
471        if !self.version.starts_with('v') {
472            total = total.saturating_add(1);
473        }
474        if !self.name.is_empty() {
475            total = total
476                .saturating_add(text::width(self.name))
477                .saturating_add(1);
478        }
479        if total > width { 0 } else { total }
480    }
481
482    fn beside(&self) -> u16 {
483        self.gap.max(BESIDE)
484    }
485
486    fn natural_line(&self) -> u16 {
487        let (total, shown) = (0..self.count())
488            .map(|index| self.natural(&self.segment(index)))
489            .filter(|width| *width > 0)
490            .fold((0u16, 0u16), |(total, shown), width| {
491                (total.saturating_add(width), shown + 1)
492            });
493        total.saturating_add(self.gap.saturating_mul(shown.saturating_sub(1)))
494    }
495
496    fn pinned(&self) -> Footer<'a> {
497        Footer {
498            open: false,
499            notice: None,
500            rule: false,
501            indent: 0,
502            ..*self
503        }
504    }
505
506    fn has_pinned(&self, width: u16) -> bool {
507        self.tag(width) > 0
508            || (0..self.count()).any(|index| match self.segment(index).part {
509                Part::Hints(hints) => hints.iter().any(|hint| hint.pinned),
510                Part::Text(..) | Part::Spans(..) => false,
511            })
512    }
513
514    fn pinned_lines(&self, width: u16) -> u16 {
515        if !self.takes_over(width) || !self.has_pinned(width) {
516            return 0;
517        }
518        self.pinned().line_count(width)
519    }
520
521    fn takes_over(&self, width: u16) -> bool {
522        self.notice
523            .is_some_and(|notice| notice.over && text::width(notice.text) > width)
524    }
525
526    fn flow(&self, width: u16, mut each: impl FnMut(usize, u16, bool)) -> (u16, Option<u16>) {
527        let separator = text::width(self.separator);
528        let tag = self.tag(width);
529        let reserve = tag.saturating_add(self.beside());
530        let last = self.hints.iter().rposition(|hint| self.visible(hint));
531        let mut shared = false;
532        let mut line = 0;
533        let mut used = 0u16;
534        for (index, hint) in self.hints.iter().enumerate() {
535            if !self.visible(hint) {
536                continue;
537            }
538            let mut wide = hint.width();
539            if tag > 0 && Some(index) == last && wide.saturating_add(reserve) <= width {
540                wide += reserve;
541                shared = true;
542            }
543            if used > 0 && used.saturating_add(separator).saturating_add(wide) > width {
544                line += 1;
545                used = 0;
546            }
547            let first = used == 0;
548            if !first {
549                used = used.saturating_add(separator);
550            }
551            used = used.saturating_add(wide);
552            each(index, line, first);
553        }
554        match (last, tag > 0) {
555            (None, false) => (0, None),
556            (None, true) => (1, Some(0)),
557            (Some(_), false) => (line + 1, None),
558            (Some(_), true) if shared => (line + 1, Some(line)),
559            (Some(_), true) => (line + 2, Some(line + 1)),
560        }
561    }
562
563    fn hints_width(&self, hints: &[Hint], kept: u64) -> u16 {
564        let separator = text::width(self.separator);
565        let mut total = 0u16;
566        let mut first = true;
567        for (index, hint) in hints.iter().take(MOST).enumerate() {
568            if kept & (1 << index) == 0 {
569                continue;
570            }
571            if !first {
572                total = total.saturating_add(separator);
573            }
574            first = false;
575            total = total.saturating_add(hint.width());
576        }
577        total
578    }
579
580    fn kept_hints(&self, hints: &[Hint], width: u16) -> u64 {
581        let mut kept = mask(hints, |hint| self.visible(hint));
582        while kept.count_ones() > 1 && self.hints_width(hints, kept) > width {
583            let drop = (0..hints.len().min(MOST))
584                .filter(|index| kept & (1 << index) != 0)
585                .max_by_key(|index| (!hints[*index].pinned, hints[*index].rank, *index));
586            match drop {
587                Some(index) => kept &= !(1 << index),
588                None => break,
589            }
590        }
591        kept
592    }
593
594    fn natural(&self, segment: &Segment) -> u16 {
595        match segment.part {
596            Part::Text(text, _) => text::width(text),
597            Part::Spans(spans) => text::spans_width(spans),
598            Part::Hints(hints) => self.hints_width(hints, self.kept_hints(hints, u16::MAX)),
599        }
600    }
601
602    fn least(&self, segment: &Segment) -> u16 {
603        if !segment.shrink {
604            return self.natural(segment);
605        }
606        match segment.part {
607            Part::Text(..) | Part::Spans(..) => 0,
608            Part::Hints(hints) => {
609                let pinned = mask(hints, |hint| hint.pinned && self.visible(hint));
610                self.hints_width(hints, pinned)
611            }
612        }
613    }
614
615    fn fit(&self, width: u16) -> Fit {
616        let count = self.count();
617        let mut kept = 0u64;
618        for index in 0..count {
619            if self.natural(&self.segment(index)) > 0 {
620                kept |= 1 << index;
621            }
622        }
623        loop {
624            let shown = kept.count_ones() as u16;
625            let gaps = self.gap.saturating_mul(shown.saturating_sub(1));
626            let fixed = (0..count)
627                .filter(|index| kept & (1 << index) != 0)
628                .fold(gaps, |total, index| {
629                    total.saturating_add(self.least(&self.segment(index)))
630                });
631            if fixed > width
632                && shown > 1
633                && let Some(index) = (0..count)
634                    .filter(|index| kept & (1 << index) != 0)
635                    .max_by_key(|index| (self.segment(*index).rank, *index))
636            {
637                kept &= !(1 << index);
638                continue;
639            }
640            let mut spare = width.saturating_sub(fixed);
641            let mut order = [0usize; MOST];
642            let mut len = 0;
643            for index in 0..count {
644                if kept & (1 << index) != 0 {
645                    order[len] = index;
646                    len += 1;
647                }
648            }
649            order[..len].sort_unstable_by_key(|index| (self.segment(*index).rank, *index));
650            let mut widths = [0u16; MOST];
651            for index in order[..len].iter().copied() {
652                let segment = self.segment(index);
653                let least = self.least(&segment);
654                let grow = self.natural(&segment).saturating_sub(least).min(spare);
655                spare -= grow;
656                widths[index] = least.min(width) + grow;
657            }
658            let starved = (0..count)
659                .find(|index| kept & (1 << index) != 0 && widths[*index] == 0 && shown > 1);
660            match starved {
661                Some(index) => kept &= !(1 << index),
662                None => return Fit { kept, widths },
663            }
664        }
665    }
666
667    fn draw_segment(&self, buf: &mut Buffer, line: Rect, x: u16, segment: &Segment, width: u16) {
668        let Some(mut pen) = Pen::new(buf, line, x, line.y) else {
669            return;
670        };
671        match segment.part {
672            Part::Text(text, style) => pen.clip(text, style, width),
673            Part::Spans(spans) => pen.spans(spans, width),
674            Part::Hints(hints) => {
675                let kept = self.kept_hints(hints, width);
676                let stop = x.saturating_add(width);
677                let mut first = true;
678                for (index, hint) in hints.iter().take(MOST).enumerate() {
679                    if kept & (1 << index) == 0 {
680                        continue;
681                    }
682                    if !first {
683                        let room = stop.saturating_sub(pen.x);
684                        pen.clip(self.separator, self.styles.muted, room);
685                    }
686                    first = false;
687                    let room = stop.saturating_sub(pen.x);
688                    hint.draw(&mut pen, self.styles.lit(), self.styles.muted, room);
689                }
690            }
691        }
692    }
693
694    fn draw_side(&self, buf: &mut Buffer, line: Rect, fit: &Fit, right: bool) {
695        let count = self.count();
696        let side =
697            |index: &usize| fit.kept & (1 << index) != 0 && self.segment(*index).right == right;
698        let total = (0..count)
699            .filter(side)
700            .enumerate()
701            .fold(0u16, |total, (at, index)| {
702                let gap = if at > 0 { self.gap } else { 0 };
703                total.saturating_add(gap).saturating_add(fit.widths[index])
704            });
705        let mut x = if right {
706            line.right().saturating_sub(total).max(line.x)
707        } else {
708            line.x
709        };
710        for (at, index) in (0..count).filter(side).enumerate() {
711            if at > 0 {
712                x = x.saturating_add(self.gap);
713            }
714            self.draw_segment(buf, line, x, &self.segment(index), fit.widths[index]);
715            x = x.saturating_add(fit.widths[index]);
716        }
717    }
718
719    fn draw_tag(&self, buf: &mut Buffer, line: Rect) {
720        let tag = self.tag(line.width);
721        if tag == 0 {
722            return;
723        }
724        let Some(mut pen) = Pen::new(buf, line, line.right() - tag, line.y) else {
725            return;
726        };
727        let style = self.styles.muted;
728        if !self.name.is_empty() {
729            pen.put(self.name, style);
730            pen.put(" ", style);
731        }
732        if !self.version.starts_with('v') {
733            pen.put("v", style);
734        }
735        pen.put(self.version, style);
736    }
737
738    fn draw_tag_after(&self, buf: &mut Buffer, line: Rect, used: u16) -> Rect {
739        let tag = self.tag(line.width);
740        let beside = if used == 0 { 0 } else { self.beside() };
741        if tag == 0 || used.saturating_add(beside).saturating_add(tag) > line.width {
742            return line;
743        }
744        self.draw_tag(buf, line);
745        Rect {
746            width: line.width - tag - beside,
747            ..line
748        }
749    }
750
751    fn draw_tag_at(&self, buf: &mut Buffer, body: Rect, end: Option<Position>) {
752        if let Some(end) = end {
753            let line = Rect::new(body.x, end.y, body.width, 1);
754            self.draw_tag_after(buf, line, end.x.saturating_sub(body.x));
755        }
756    }
757
758    fn draw_line(&self, buf: &mut Buffer, line: Rect) {
759        let line = self.draw_tag_after(buf, line, self.natural_line());
760        let fit = self.fit(line.width);
761        self.draw_side(buf, line, &fit, false);
762        self.draw_side(buf, line, &fit, true);
763    }
764
765    fn draw_wrapped(&self, buf: &mut Buffer, area: Rect) -> u16 {
766        let mut x = area.x;
767        let mut last = 0;
768        let (_, tag) = self.flow(area.width, |index, line, first| {
769            if line >= area.height {
770                return;
771            }
772            last = line + 1;
773            let y = area.y.saturating_add(line);
774            let start = if first { area.x } else { x };
775            let Some(mut pen) = Pen::new(buf, area, start, y) else {
776                return;
777            };
778            if !first {
779                pen.put(self.separator, self.styles.muted);
780            }
781            let room = pen.room();
782            self.hints[index].draw(&mut pen, self.styles.lit(), self.styles.muted, room);
783            x = pen.x;
784        });
785        if let Some(row) = tag
786            && row < area.height
787        {
788            self.draw_tag(
789                buf,
790                Rect::new(area.x, area.y.saturating_add(row), area.width, 1),
791            );
792            last = last.max(row + 1);
793        }
794        last
795    }
796}
797
798impl Widget for Footer<'_> {
799    fn render(self, area: Rect, buf: &mut Buffer) {
800        (&self).render(area, buf);
801    }
802}
803
804impl Widget for &Footer<'_> {
805    fn render(self, area: Rect, buf: &mut Buffer) {
806        if area.is_empty() || self.height(area.width) == 0 {
807            return;
808        }
809        if self.rule {
810            text::rule(buf, area, self.styles.rule);
811        }
812        let body = self.body(area);
813        let mut y = body.y;
814        let area = Rect {
815            y: area.y,
816            height: area.height,
817            ..body
818        };
819        let rest = |y: u16| Rect::new(area.x, y, area.width, area.bottom().saturating_sub(y));
820        if let Some(confirm) = self.confirm {
821            let end = confirm.rule(false).styled(self.styles).paint(body, buf);
822            self.draw_tag_at(buf, body, end);
823            return;
824        }
825        if let Some(input) = self.input {
826            let end = input.rule(false).styled(self.styles).paint(body, buf);
827            self.draw_tag_at(buf, body, end);
828            return;
829        }
830        if self.takes_over(area.width)
831            && let Some(notice) = self.notice
832        {
833            let all = rest(y);
834            let pinned = self.pinned_lines(area.width);
835            let kept = if pinned > 0 && all.height > pinned {
836                pinned
837            } else {
838                0
839            };
840            let flowing = Rect {
841                height: all.height - kept,
842                ..all
843            };
844            text::flow(buf, flowing, notice.text, notice.tone.style(&self.styles));
845            if kept > 0 {
846                let low = Rect {
847                    y: all.bottom() - kept,
848                    height: kept,
849                    ..all
850                };
851                self.pinned().styles(self.styles).render(low, buf);
852            }
853            return;
854        }
855        if self.line_count(area.width) > 0 && y < area.bottom() {
856            if self.wrapping() {
857                let room = rest(y);
858                let notice = u16::from(self.notice.is_some());
859                let room = Rect {
860                    height: room.height.saturating_sub(notice).max(1),
861                    ..room
862                };
863                y += self.draw_wrapped(buf, room);
864            } else {
865                self.draw_line(buf, Rect::new(area.x, y, area.width, 1));
866                y += 1;
867            }
868        }
869        if let Some(notice) = self.notice
870            && y < area.bottom()
871            && let Some(mut pen) = Pen::new(buf, area, area.x, y)
872        {
873            pen.rest(notice.text, notice.tone.style(&self.styles));
874        }
875    }
876}