duat_base/widgets/
prompt_line.rs1use std::{any::TypeId, collections::HashMap};
19
20use duat_core::{
21 context::Handle,
22 data::Pass,
23 opts::PrintOpts,
24 text::Text,
25 ui::{PushSpecs, PushTarget, Side, Widget},
26};
27
28use crate::modes::PromptMode;
29
30pub struct PromptLine {
52 text: Text,
53 prompts: HashMap<TypeId, Text>,
54 request_width: bool,
55}
56
57impl PromptLine {
58 pub fn builder() -> PromptLineBuilder {
61 PromptLineBuilder::default()
62 }
63
64 pub fn prompt_of<M: PromptMode>(&self) -> Option<Text> {
66 self.prompts.get(&TypeId::of::<M>()).cloned()
67 }
68
69 pub fn set_prompt<M: PromptMode>(&mut self, text: Text) {
71 self.prompts.entry(TypeId::of::<M>()).or_insert(text);
72 }
73
74 pub fn prompt_of_id(&self, id: TypeId) -> Option<Text> {
76 self.prompts.get(&id).cloned()
77 }
78}
79
80impl Widget for PromptLine {
81 fn update(pa: &mut Pass, handle: &Handle<Self>) {
82 let (pl, area) = handle.write_with_area(pa);
83
84 if pl.request_width {
85 let width = area.width_of_text(pl.get_print_opts(), &pl.text).unwrap();
86 area.set_width(width + pl.get_print_opts().scrolloff.x as f32)
87 .unwrap();
88 }
89
90 if let Some(main) = pl.text.selections().get_main() {
91 area.scroll_around_points(
92 &pl.text,
93 main.caret().to_two_points_after(),
94 pl.get_print_opts(),
95 );
96 }
97 }
98
99 fn needs_update(&self, _: &Pass) -> bool {
100 false
101 }
102
103 fn text(&self) -> &Text {
104 &self.text
105 }
106
107 fn text_mut(&mut self) -> &mut Text {
108 &mut self.text
109 }
110
111 fn get_print_opts(&self) -> PrintOpts {
112 let mut opts = PrintOpts::default_for_input();
113 opts.force_scrolloff = true;
114 opts
115 }
116}
117
118#[doc(hidden)]
119pub struct PromptLineBuilder {
120 prompts: Option<HashMap<TypeId, Text>>,
121 specs: PushSpecs,
122 request_width: bool,
123}
124
125impl Default for PromptLineBuilder {
126 fn default() -> Self {
127 Self {
128 prompts: None,
129 specs: PushSpecs {
130 side: Side::Below,
131 height: Some(1.0),
132 ..Default::default()
133 },
134 request_width: false,
135 }
136 }
137}
138
139impl PromptLineBuilder {
140 pub fn push_on(self, pa: &mut Pass, push_target: &impl PushTarget) -> Handle<PromptLine> {
141 let prompt_line = PromptLine {
142 text: Text::default(),
143 prompts: self.prompts.unwrap_or_default(),
144 request_width: self.request_width,
145 };
146
147 push_target.push_outer(pa, prompt_line, self.specs)
148 }
149
150 pub fn set_prompt<M: PromptMode>(mut self, prompt: Text) -> Self {
155 self.prompts
156 .get_or_insert_default()
157 .insert(TypeId::of::<M>(), prompt);
158 self
159 }
160
161 pub fn above(self) -> Self {
163 Self {
164 specs: PushSpecs { side: Side::Above, ..self.specs },
165 ..self
166 }
167 }
168
169 pub fn below(self) -> Self {
171 Self {
172 specs: PushSpecs { side: Side::Below, ..self.specs },
173 ..self
174 }
175 }
176
177 pub fn hidden(self) -> Self {
179 Self {
180 specs: PushSpecs { hidden: true, ..self.specs },
181 ..self
182 }
183 }
184
185 pub(crate) fn request_width(self) -> Self {
187 Self { request_width: true, ..self }
188 }
189}