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}