Skip to main content

blizz_ui/
wizard_composer.rs

1//! Composites question text, selector or text-entry chrome, hint, timer bar,
2//! and optional cursor positioning for the blizz setup wizard prompt layer.
3//!
4//! The wizard crate owns UI state (`UiController`); this module only lays out
5//! and draws those pieces in a fixed order.
6
7use std::io::{self, Write};
8
9use rand::Rng;
10
11use crate::components::hint::HintComponent;
12use crate::components::selector::SelectorComponent;
13use crate::components::text::TextComponent;
14use crate::components::text_entry::TextEntryComponent;
15use crate::components::timer_bar::TimerBarComponent;
16use crate::input_buffer::InputBuffer;
17use crate::layout::Size;
18use crate::prompt::{self, text_entry as text_entry_dims, text_entry_layout};
19
20/// References to wizard prompt widgets for [`render_wizard_prompt_layer`].
21#[derive(Clone, Copy)]
22pub struct WizardPromptRefs<'a> {
23  pub question: &'a TextComponent,
24  pub entry: &'a TextEntryComponent,
25  pub selector: &'a SelectorComponent,
26  pub hint: &'a HintComponent,
27  pub timer_bar: &'a TimerBarComponent,
28}
29
30#[cfg(not(tarpaulin_include))]
31pub fn render_wizard_prompt_layer<W: Write, R: Rng>(
32  writer: &mut W,
33  terminal_size: Size,
34  refs: WizardPromptRefs<'_>,
35  input_buf: &InputBuffer,
36  rng: &mut R,
37) -> io::Result<()> {
38  let tw = terminal_size.width;
39  let dims = text_entry_dims(&refs.entry.label, &refs.entry.content, tw);
40  let layout = text_entry_layout(&dims, terminal_size);
41
42  refs.question.render(writer, tw, layout.question_row, rng)?;
43
44  if refs.selector.visible {
45    let selector_start = layout.box_top_row;
46    let visible_count = refs.selector.render(writer, tw, selector_start, rng)?;
47    let hint_row = selector_start
48      .saturating_add(visible_count as u16)
49      .saturating_add(1);
50    refs.hint.render(writer, tw, hint_row)?;
51    refs
52      .timer_bar
53      .render(writer, tw, hint_row.saturating_add(1))?;
54    return Ok(());
55  }
56
57  let cursor_col = if refs.entry.visible && refs.entry.open > 0.0 {
58    Some(
59      refs
60        .entry
61        .render_with_cursor(writer, &layout, tw, input_buf, rng)?,
62    )
63  } else {
64    None
65  };
66
67  refs.hint.render(writer, tw, layout.hint_row)?;
68  refs
69    .timer_bar
70    .render(writer, tw, layout.hint_row.saturating_add(1))?;
71
72  let box_fully_ready = refs.entry.visible
73    && refs.entry.open >= 1.0
74    && refs.entry.label_reveal >= 1.0
75    && refs.entry.content_reveal >= 1.0;
76
77  if box_fully_ready && let Some(col) = cursor_col {
78    prompt::queue_cursor(writer, &layout, col)?;
79  }
80
81  Ok(())
82}