1use std::borrow::Cow;
9use std::iter::Iterator;
10use std::marker::PhantomData;
11
12use serde::{Deserialize, Serialize};
13
14use ratatui::{
15 buffer::Buffer,
16 layout::Rect,
17 style::{Color, Modifier as StyleModifier, Style},
18 text::{Line, Span},
19 widgets::{BorderType, StatefulWidget, Tabs, Widget},
20};
21
22use super::{
23 cmdbar::{CommandBar, CommandBarState},
24 util::{rect_down, rect_zero_height},
25 windows::{WindowActions, WindowLayout, WindowLayoutRoot, WindowLayoutState},
26 TerminalCursor,
27 Window,
28 WindowOps,
29};
30
31use modalkit::actions::*;
32use modalkit::errors::{EditResult, UIError, UIResult};
33use modalkit::prelude::*;
34use modalkit::ui::FocusList;
35
36use modalkit::editing::{
37 application::{ApplicationInfo, EmptyInfo},
38 completion::CompletionList,
39 context::EditContext,
40 store::Store,
41};
42
43const MAX_COMPL_BARH: usize = 10;
44const GAP_COMPL_COL: usize = 2;
45
46#[derive(Default)]
48struct CompletionMenu {
49 cursor: (u16, u16),
50}
51
52impl CompletionMenu {
53 fn new(cursor: (u16, u16)) -> Self {
54 CompletionMenu { cursor }
55 }
56}
57
58impl StatefulWidget for CompletionMenu {
59 type State = CompletionList;
60
61 fn render(self, area: Rect, buffer: &mut Buffer, state: &mut CompletionList) {
62 if area.height <= 1 {
63 return;
65 }
66
67 let len = state.candidates.len();
68
69 let top = area.top();
70 let bot = area.bottom();
71 let (cx, cy) = self.cursor;
72
73 let above = cy.saturating_sub(top) as usize;
74 let below = bot.saturating_sub(cy).saturating_sub(1) as usize;
75
76 let right = area.right();
77 let space = right.saturating_sub(cx).saturating_sub(1) as usize;
78 let maxw = state.candidates.iter().map(|s| s.len()).max().unwrap_or(0).min(space);
79 let style = Style::reset().add_modifier(StyleModifier::REVERSED);
80 let style_sel = style.bg(Color::Yellow).fg(Color::Black);
81
82 let x = if state.start.y == state.cursor.y {
83 let diff = state.cursor.x.saturating_sub(state.start.x);
84 cx.saturating_sub(diff as u16)
85 } else {
86 cx
87 };
88
89 let mut draw = |y: u16, idx: usize, s: &str| {
90 let sel = matches!(state.selected, Some(i) if i == idx);
91 let style = if sel { style_sel } else { style };
92 let slen = s.len();
93
94 let (x, _) = buffer.set_stringn(x, y, s, space, style);
95 let start = (maxw - slen) as u16;
96
97 for off in 0..start {
98 buffer.set_stringn(x + off, y, " ", 1, style);
99 }
100 };
101
102 let candidates = state.candidates.iter().enumerate();
103
104 if len <= below || below >= above {
105 let height = len.min(below);
107 let page = if let Some(selected) = state.selected {
108 selected / height
109 } else {
110 0
111 };
112
113 for (y, (idx, s)) in candidates.skip(page * height).take(height).enumerate() {
114 let y = cy + y as u16 + 1;
115 draw(y, idx, s);
116 }
117 } else {
118 let height = len.min(above);
120 let page = if let Some(selected) = state.selected {
121 selected / height
122 } else {
123 0
124 };
125
126 let n = (len - page * height).min(height);
127
128 for (y, (idx, s)) in candidates.skip(page * height).take(height).enumerate() {
129 let y = cy.saturating_sub((n - y) as u16);
130 draw(y, idx, s);
131 }
132 }
133 }
134}
135
136#[derive(Default)]
138struct CompletionBar {
139 colw: u16,
140 cols: u16,
141 rows: u16,
142}
143
144impl CompletionBar {
145 fn new(list: &CompletionList, width: u16) -> Self {
146 match list.display {
147 CompletionDisplay::None => CompletionBar::default(),
148 CompletionDisplay::List => CompletionBar::default(),
149 CompletionDisplay::Bar => {
150 let len = list.candidates.len();
151 let width = width as usize;
152
153 if len == 0 {
154 return CompletionBar::default();
155 }
156
157 let cmax = list.candidates.iter().map(|s| s.len()).max().unwrap_or(0);
158 let cmax = cmax.clamp(1, width);
159 let colw = (cmax + GAP_COMPL_COL).min(width);
160 let cols = (width / colw).max(1);
161 let rows = (len / cols).clamp(1, MAX_COMPL_BARH);
162
163 CompletionBar {
164 colw: colw as u16,
165 cols: cols as u16,
166 rows: rows as u16,
167 }
168 },
169 }
170 }
171}
172
173impl StatefulWidget for CompletionBar {
174 type State = CompletionList;
175
176 fn render(self, area: Rect, buffer: &mut Buffer, state: &mut CompletionList) {
177 if area.height == 0 {
178 return;
179 }
180
181 let mut iter = state.candidates.iter();
182 let maxw = (self.colw as usize).saturating_sub(GAP_COMPL_COL);
183 let style = Style::default();
184
185 for x in 0..self.cols {
186 for y in 0..self.rows {
187 let item = match iter.next() {
188 Some(item) => item.as_str(),
189 None => return,
190 };
191
192 let x = area.x + x * self.colw;
193 let y = area.y + y;
194 buffer.set_stringn(x, y, item, maxw, style);
195 }
196 }
197 }
198}
199
200trait TabActions<C, S, I>
201where
202 I: ApplicationInfo,
203{
204 fn tab_close(
206 &mut self,
207 target: &TabTarget,
208 flags: CloseFlags,
209 ctx: &C,
210 store: &mut S,
211 ) -> UIResult<EditInfo, I>;
212
213 fn tab_extract(
216 &mut self,
217 change: &FocusChange,
218 side: &MoveDir1D,
219 ctx: &C,
220 store: &mut S,
221 ) -> UIResult<EditInfo, I>;
222
223 fn tab_focus(&mut self, change: &FocusChange, ctx: &C, store: &mut S) -> UIResult<EditInfo, I>;
225
226 fn tab_move(&mut self, change: &FocusChange, ctx: &C, store: &mut S) -> UIResult<EditInfo, I>;
228
229 fn tab_open(
231 &mut self,
232 target: &OpenTarget<I::WindowId>,
233 change: &FocusChange,
234 ctx: &C,
235 store: &mut S,
236 ) -> UIResult<EditInfo, I>;
237}
238
239fn bold<'a>(s: String) -> Span<'a> {
240 Span::styled(s, Style::default().add_modifier(StyleModifier::BOLD))
241}
242
243#[derive(Clone, Debug, Deserialize, Serialize)]
245#[serde(bound(deserialize = "I::WindowId: Deserialize<'de>"))]
246#[serde(bound(serialize = "I::WindowId: Serialize"))]
247pub struct TabbedLayoutDescription<I: ApplicationInfo> {
248 pub tabs: Vec<WindowLayoutRoot<I>>,
250 pub focused: usize,
252}
253
254impl<I: ApplicationInfo> TabbedLayoutDescription<I> {
255 pub fn to_layout<W: Window<I>>(
257 self,
258 area: Option<Rect>,
259 store: &mut Store<I>,
260 ) -> UIResult<FocusList<WindowLayoutState<W, I>>, I> {
261 let mut tabs = self
262 .tabs
263 .into_iter()
264 .map(|desc| desc.to_layout(area, store))
265 .collect::<UIResult<Vec<_>, I>>()
266 .map(FocusList::new)?;
267
268 let change = FocusChange::Offset(Count::Exact(self.focused + 1), true);
270 let ctx = EditContext::default();
271 tabs.focus(&change, &ctx);
272
273 Ok(tabs)
274 }
275}
276
277#[derive(Clone, Copy, Debug, Eq, PartialEq)]
279pub enum CurrentFocus {
280 Command,
282
283 Window,
285}
286
287pub struct ScreenState<W, I = EmptyInfo>
289where
290 W: Window<I>,
291 I: ApplicationInfo,
292{
293 focused: CurrentFocus,
294 cmdbar: CommandBarState<I>,
295 tabs: FocusList<WindowLayoutState<W, I>>,
296
297 messages: Vec<(String, Style)>,
298 last_message: bool,
299}
300
301impl<W, I> ScreenState<W, I>
302where
303 W: Window<I>,
304 I: ApplicationInfo,
305{
306 pub fn new(win: W, cmdbar: CommandBarState<I>) -> Self {
308 let tab = WindowLayoutState::new(win);
309 let tabs = FocusList::from(tab);
310
311 Self::from_list(tabs, cmdbar)
312 }
313
314 pub fn from_list(tabs: FocusList<WindowLayoutState<W, I>>, cmdbar: CommandBarState<I>) -> Self {
316 ScreenState {
317 focused: CurrentFocus::Window,
318 cmdbar,
319 tabs,
320
321 messages: vec![],
322 last_message: false,
323 }
324 }
325
326 pub fn as_description(&self) -> TabbedLayoutDescription<I> {
328 TabbedLayoutDescription {
329 tabs: self.tabs.iter().map(WindowLayoutState::as_description).collect(),
330 focused: self.tabs.pos(),
331 }
332 }
333
334 pub fn push_message<T: ToString>(&mut self, msg: T, style: Style) {
336 self.messages.push((msg.to_string(), style));
337 self.last_message = true;
338 }
339
340 pub fn push_error<T: ToString>(&mut self, msg: T) {
342 let style = Style::default().fg(Color::Red);
343
344 self.push_message(msg, style);
345 }
346
347 pub fn push_info<T: Into<String>>(&mut self, msg: T) {
349 let style = Style::default();
350
351 self.push_message(msg.into(), style);
352 }
353
354 pub fn clear_message(&mut self) {
356 self.last_message = false;
357 }
358
359 fn focus_command(
360 &mut self,
361 prompt: &str,
362 ct: CommandType,
363 act: &Action<I>,
364 ctx: &EditContext,
365 ) -> EditResult<EditInfo, I> {
366 self.focused = CurrentFocus::Command;
367 self.cmdbar.reset();
368 self.cmdbar.set_type(prompt, ct, act, ctx);
369 self.clear_message();
370
371 Ok(None)
372 }
373
374 fn focus_window(&mut self) -> EditResult<EditInfo, I> {
375 self.focused = CurrentFocus::Window;
376 self.cmdbar.reset();
377
378 Ok(None)
379 }
380
381 pub fn command_bar(
383 &mut self,
384 act: &CommandBarAction<I>,
385 ctx: &EditContext,
386 ) -> EditResult<EditInfo, I> {
387 match act {
388 CommandBarAction::Focus(s, ct, act) => self.focus_command(s, *ct, act, ctx),
389 CommandBarAction::Unfocus => self.focus_window(),
390 }
391 }
392
393 pub fn current_tab(&self) -> UIResult<&WindowLayoutState<W, I>, I> {
395 self.tabs.get().ok_or(UIError::NoTab)
396 }
397
398 pub fn current_tab_mut(&mut self) -> UIResult<&mut WindowLayoutState<W, I>, I> {
400 self.tabs.get_mut().ok_or(UIError::NoTab)
401 }
402
403 pub fn current_window(&self) -> Option<&W> {
405 self.tabs.get().and_then(WindowLayoutState::get)
406 }
407
408 pub fn current_window_mut(&mut self) -> UIResult<&mut W, I> {
410 self.current_tab_mut()?.get_mut().ok_or(UIError::NoWindow)
411 }
412
413 fn _max_idx(&self) -> usize {
415 self.tabs.len().saturating_sub(1)
416 }
417}
418
419impl<W, I> TabActions<EditContext, Store<I>, I> for ScreenState<W, I>
420where
421 W: Window<I>,
422 I: ApplicationInfo,
423{
424 fn tab_close(
425 &mut self,
426 target: &TabTarget,
427 flags: CloseFlags,
428 ctx: &EditContext,
429 store: &mut Store<I>,
430 ) -> UIResult<EditInfo, I> {
431 let mut filter = |tab: &mut WindowLayoutState<W, I>| -> UIResult<(), I> {
432 let _ = tab.window_close(&WindowTarget::All, flags, ctx, store);
433
434 if tab.windows() == 0 {
435 return Ok(());
436 }
437
438 let msg = "Could not close all windows in tab";
439 let err = UIError::Failure(msg.into());
440
441 return Err(err);
442 };
443
444 self.tabs.try_close(target, &mut filter, ctx)?;
445
446 Ok(None)
447 }
448
449 fn tab_extract(
450 &mut self,
451 change: &FocusChange,
452 side: &MoveDir1D,
453 ctx: &EditContext,
454 _: &mut Store<I>,
455 ) -> UIResult<EditInfo, I> {
456 if self.windows() <= 1 {
457 return Ok(Some(InfoMessage::from("Already one window")));
458 }
459
460 let tab = self.current_tab_mut()?.extract();
461
462 let (idx, side) = if let Some((idx, _)) = self.tabs.target(change, ctx) {
463 (idx, *side)
464 } else {
465 (self._max_idx(), MoveDir1D::Next)
466 };
467
468 self.tabs.insert(idx, side, tab);
469
470 return Ok(None);
471 }
472
473 fn tab_focus(
474 &mut self,
475 change: &FocusChange,
476 ctx: &EditContext,
477 _: &mut Store<I>,
478 ) -> UIResult<EditInfo, I> {
479 self.tabs.focus(change, ctx);
480
481 Ok(None)
482 }
483
484 fn tab_move(
485 &mut self,
486 change: &FocusChange,
487 ctx: &EditContext,
488 _: &mut Store<I>,
489 ) -> UIResult<EditInfo, I> {
490 self.tabs.transfer(change, ctx);
491
492 return Ok(None);
493 }
494
495 fn tab_open(
496 &mut self,
497 target: &OpenTarget<I::WindowId>,
498 change: &FocusChange,
499 ctx: &EditContext,
500 store: &mut Store<I>,
501 ) -> UIResult<EditInfo, I> {
502 let (idx, side) =
503 self.tabs.target(change, ctx).unwrap_or((self.tabs.pos(), MoveDir1D::Next));
504 let tab = self.current_tab_mut()?.from_target(target, ctx, store)?;
505
506 self.tabs.insert(idx, side, tab);
507
508 return Ok(None);
509 }
510}
511
512impl<W, I> TabCount for ScreenState<W, I>
513where
514 W: Window<I>,
515 I: ApplicationInfo,
516{
517 fn tabs(&self) -> usize {
518 self.tabs.len()
519 }
520}
521
522impl<W, I> TabContainer<EditContext, Store<I>, I> for ScreenState<W, I>
523where
524 W: Window<I>,
525 I: ApplicationInfo,
526{
527 fn tab_command(
528 &mut self,
529 act: &TabAction<I>,
530 ctx: &EditContext,
531 store: &mut Store<I>,
532 ) -> UIResult<EditInfo, I> {
533 match act {
534 TabAction::Close(target, flags) => self.tab_close(target, *flags, ctx, store),
535 TabAction::Extract(target, side) => self.tab_extract(target, side, ctx, store),
536 TabAction::Focus(change) => self.tab_focus(change, ctx, store),
537 TabAction::Move(change) => self.tab_move(change, ctx, store),
538 TabAction::Open(target, change) => self.tab_open(target, change, ctx, store),
539 act => {
540 let msg = format!("unknown tab action: {act:?}");
541 return Err(UIError::Unimplemented(msg));
542 },
543 }
544 }
545}
546
547impl<W, I> WindowCount for ScreenState<W, I>
548where
549 W: Window<I>,
550 I: ApplicationInfo,
551{
552 fn windows(&self) -> usize {
553 self.tabs.get().map(WindowCount::windows).unwrap_or(0)
554 }
555}
556
557impl<W, I> WindowContainer<EditContext, Store<I>, I> for ScreenState<W, I>
558where
559 W: Window<I>,
560 I: ApplicationInfo,
561{
562 fn window_command(
563 &mut self,
564 act: &WindowAction<I>,
565 ctx: &EditContext,
566 store: &mut Store<I>,
567 ) -> UIResult<EditInfo, I> {
568 let tab = self.current_tab_mut()?;
569 let ret = tab.window_command(act, ctx, store);
570
571 if tab.windows() == 0 {
572 self.tabs.remove_current();
573 }
574
575 ret
576 }
577}
578
579macro_rules! delegate_focus {
580 ($s: expr, $id: ident => $invoke: expr) => {
581 match $s.focused {
582 CurrentFocus::Command => {
583 let $id = &mut $s.cmdbar;
584 $invoke
585 },
586 CurrentFocus::Window => {
587 if let Ok($id) = $s.current_window_mut() {
588 $invoke
589 } else {
590 Ok(Default::default())
591 }
592 },
593 }
594 };
595}
596
597impl<W, I> Editable<EditContext, Store<I>, I> for ScreenState<W, I>
598where
599 W: Window<I> + Editable<EditContext, Store<I>, I>,
600 I: ApplicationInfo,
601{
602 fn editor_command(
603 &mut self,
604 act: &EditorAction,
605 ctx: &EditContext,
606 store: &mut Store<I>,
607 ) -> EditResult<EditInfo, I> {
608 delegate_focus!(self, f => f.editor_command(act, ctx, store))
609 }
610}
611
612impl<W, I> TerminalCursor for ScreenState<W, I>
613where
614 W: Window<I> + TerminalCursor,
615 I: ApplicationInfo,
616{
617 fn get_term_cursor(&self) -> Option<(u16, u16)> {
618 match self.focused {
619 CurrentFocus::Command => self.cmdbar.get_term_cursor(),
620 CurrentFocus::Window => {
621 if let Some(w) = self.current_window() {
622 w.get_term_cursor()
623 } else {
624 None
625 }
626 },
627 }
628 }
629}
630
631impl<W, C, I> Jumpable<C, I> for ScreenState<W, I>
632where
633 W: Window<I> + Jumpable<C, I>,
634 I: ApplicationInfo,
635{
636 fn jump(
637 &mut self,
638 list: PositionList,
639 dir: MoveDir1D,
640 count: usize,
641 ctx: &C,
642 ) -> UIResult<usize, I> {
643 self.current_tab_mut()?.jump(list, dir, count, ctx)
644 }
645}
646
647impl<W, I> Promptable<EditContext, Store<I>, I> for ScreenState<W, I>
648where
649 W: Window<I> + Promptable<EditContext, Store<I>, I>,
650 I: ApplicationInfo,
651{
652 fn prompt(
653 &mut self,
654 act: &PromptAction,
655 ctx: &EditContext,
656 store: &mut Store<I>,
657 ) -> EditResult<Vec<(Action<I>, EditContext)>, I> {
658 delegate_focus!(self, f => f.prompt(act, ctx, store))
659 }
660}
661
662impl<W, I> Scrollable<EditContext, Store<I>, I> for ScreenState<W, I>
663where
664 W: Window<I> + Scrollable<EditContext, Store<I>, I>,
665 I: ApplicationInfo,
666{
667 fn scroll(
668 &mut self,
669 style: &ScrollStyle,
670 ctx: &EditContext,
671 store: &mut Store<I>,
672 ) -> EditResult<EditInfo, I> {
673 delegate_focus!(self, f => f.scroll(style, ctx, store))
674 }
675}
676
677impl<W, C, I> Searchable<C, Store<I>, I> for ScreenState<W, I>
678where
679 W: Window<I> + Searchable<C, Store<I>, I>,
680 I: ApplicationInfo,
681{
682 fn search(
683 &mut self,
684 dir: MoveDirMod,
685 count: Count,
686 ctx: &C,
687 store: &mut Store<I>,
688 ) -> UIResult<EditInfo, I> {
689 self.current_window_mut()?.search(dir, count, ctx, store)
690 }
691}
692
693pub struct Screen<'a, W, I = EmptyInfo>
695where
696 W: Window<I>,
697 I: ApplicationInfo,
698{
699 store: &'a mut Store<I>,
700 showdialog: Vec<Span<'a>>,
701 showmode: Option<Span<'a>>,
702
703 borders: bool,
704 border_style: Style,
705 border_style_focused: Style,
706 border_type: BorderType,
707 cmdbar_style: Style,
708 cmdbar_prompt_style: Option<Style>,
709 tab_style: Style,
710 tab_style_focused: Style,
711 divider: Span<'a>,
712 focused: bool,
713
714 _p: PhantomData<(W, I)>,
715}
716
717impl<'a, W, I> Screen<'a, W, I>
718where
719 W: Window<I>,
720 I: ApplicationInfo,
721{
722 pub fn new(store: &'a mut Store<I>) -> Self {
724 Screen {
725 store,
726 showdialog: Vec::new(),
727 showmode: None,
728 borders: false,
729 border_style: Style::default(),
730 border_style_focused: Style::default(),
731 border_type: BorderType::Plain,
732 cmdbar_style: Style::default(),
733 cmdbar_prompt_style: None,
734 tab_style: Style::default(),
735 tab_style_focused: Style::default(),
736 divider: Span::raw("|"),
737 focused: true,
738 _p: PhantomData,
739 }
740 }
741
742 pub fn border_style(mut self, style: Style) -> Self {
744 self.border_style = style;
745 self
746 }
747
748 pub fn border_style_focused(mut self, style: Style) -> Self {
750 self.border_style_focused = style;
751 self
752 }
753
754 pub fn border_type(mut self, border_type: BorderType) -> Self {
756 self.border_type = border_type;
757 self
758 }
759
760 pub fn borders(mut self, borders: bool) -> Self {
762 self.borders = borders;
763 self
764 }
765
766 pub fn cmdbar_style(mut self, style: Style) -> Self {
768 self.cmdbar_style = style;
769 self
770 }
771
772 pub fn cmdbar_prompt_style(mut self, style: Style) -> Self {
774 self.cmdbar_prompt_style = Some(style);
775 self
776 }
777
778 pub fn tab_style(mut self, style: Style) -> Self {
780 self.tab_style = style;
781 self
782 }
783
784 pub fn tab_style_focused(mut self, style: Style) -> Self {
786 self.tab_style_focused = style;
787 self
788 }
789
790 pub fn divider(mut self, divider: impl Into<Span<'a>>) -> Self {
794 self.divider = divider.into();
795 self
796 }
797
798 pub fn focus(mut self, focused: bool) -> Self {
800 self.focused = focused;
801 self
802 }
803
804 pub fn show_dialog(mut self, dialog: Vec<Cow<'a, str>>) -> Self {
806 self.showdialog = dialog.into_iter().map(Span::raw).collect();
807 self
808 }
809
810 pub fn show_mode(mut self, mode: Option<String>) -> Self {
812 self.showmode = mode.map(bold);
813 self
814 }
815}
816
817impl<W, I> StatefulWidget for Screen<'_, W, I>
818where
819 W: Window<I>,
820 I: ApplicationInfo,
821{
822 type State = ScreenState<W, I>;
823
824 fn render(mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
825 if area.height == 0 {
826 return;
827 }
828
829 let focused = state.focused;
830
831 let mut compls = match focused {
832 CurrentFocus::Command => state.cmdbar.get_completions(),
833 CurrentFocus::Window => state.current_window().and_then(WindowOps::get_completions),
834 };
835
836 let ntabs = state.tabs.len();
838 let tabh = if ntabs > 1 && area.height > 2 { 1 } else { 0 };
839
840 let cbar = compls
842 .as_ref()
843 .map(|l| CompletionBar::new(l, area.width))
844 .unwrap_or_default();
845 let mut barh = cbar.rows;
846
847 let dialog = std::mem::take(&mut self.showdialog);
849 let cmdh = match dialog.len() {
850 0 => 1,
851 n => {
852 barh = 0;
854 (n as u16).clamp(1, area.height)
855 },
856 };
857
858 let winh = area.height.saturating_sub(tabh).saturating_sub(barh).saturating_sub(cmdh);
860
861 let init = rect_zero_height(area);
862 let tabarea = rect_down(init, tabh);
863 let winarea = rect_down(tabarea, winh);
864 let bararea = rect_down(winarea, barh);
865 let cmdarea = rect_down(bararea, cmdh);
866
867 let titles: Vec<Line> = state
868 .tabs
869 .iter()
870 .map(|tab| {
871 let mut spans = vec![];
872 let n = tab.windows();
873
874 if n > 1 {
875 spans.push(Span::from(format!("{n} ")));
876 }
877
878 if let Some(w) = tab.get() {
879 let mut title = w.get_tab_title(self.store).spans;
880
881 spans.append(&mut title);
882 } else {
883 spans.push(Span::from("[No Name]"));
884 }
885
886 Line::from(spans)
887 })
888 .collect();
889
890 Tabs::new(titles)
891 .style(self.tab_style)
892 .highlight_style(self.tab_style_focused)
893 .divider(self.divider)
894 .select(state.tabs.pos())
895 .render(tabarea, buf);
896
897 if let Ok(tab) = state.current_tab_mut() {
898 WindowLayout::new(self.store)
899 .focus(self.focused && focused == CurrentFocus::Window)
900 .border_style(self.border_style)
901 .border_style_focused(self.border_style_focused)
902 .border_type(self.border_type)
903 .borders(self.borders)
904 .render(winarea, buf, tab);
905 }
906
907 if !dialog.is_empty() {
908 let iter = dialog.into_iter().take(cmdarea.height as usize);
909
910 for (i, line) in iter.enumerate() {
911 let y = cmdarea.y + i as u16;
912 buf.set_span(0, y, &line, cmdarea.width);
913 }
914
915 return;
916 }
917
918 let status = if self.showmode.is_some() || !state.last_message {
919 state.last_message = false;
920 self.showmode
921 } else if let Some((s, style)) = state.messages.last() {
922 Some(Span::styled(s, *style))
923 } else {
924 None
925 };
926
927 CommandBar::new()
928 .focus(focused == CurrentFocus::Command)
929 .status(status)
930 .style(self.cmdbar_style)
931 .prompt_style(self.cmdbar_prompt_style.unwrap_or(self.cmdbar_style))
932 .render(cmdarea, buf, &mut state.cmdbar);
933
934 if let Some(ref mut completions) = compls {
936 match completions.display {
937 CompletionDisplay::None => {},
938 CompletionDisplay::Bar => {
939 cbar.render(bararea, buf, completions);
940 },
941 CompletionDisplay::List => {
942 if let Some(cursor) = state.get_term_cursor() {
943 CompletionMenu::new(cursor).render(winarea, buf, completions);
944 }
945 },
946 }
947 }
948 }
949}