use std::{any::TypeId, collections::HashMap};
use duat_core::{
Ns,
context::Handle,
data::Pass,
hook::{self, FocusedOn, KeySent, UnfocusedFrom},
opts::PrintOpts,
text::{Text, TextMut},
ui::{PushSpecs, PushTarget, Side, Widget},
};
use crate::modes::PromptMode;
pub fn add_promptline_hooks() {
let ns = Ns::new();
hook::add::<FocusedOn<PromptLine>>(move |_, (_, promptline)| {
let promptline = promptline.clone();
hook::add::<KeySent>(move |pa, _| {
let (pl, area) = promptline.write_with_area(pa);
if pl.request_width {
let width = area.size_of_text(pl.print_opts(), &pl.text).unwrap().x;
area.set_width(width + pl.print_opts().scrolloff.x as f32)
.unwrap();
}
if let Some(main) = pl.text.selections().get_main() {
area.scroll_around_points(
&pl.text,
main.caret().to_two_points_after(),
pl.print_opts(),
);
}
})
.grouped(ns);
});
hook::add::<UnfocusedFrom<PromptLine>>(move |_, _| hook::remove(ns));
}
pub struct PromptLine {
pub(crate) text: Text,
prompts: HashMap<TypeId, Text>,
request_width: bool,
}
impl PromptLine {
pub fn builder() -> PromptLineBuilder {
PromptLineBuilder::default()
}
pub fn prompt_of<M: PromptMode>(&self) -> Option<Text> {
self.prompts.get(&TypeId::of::<M>()).cloned()
}
pub fn set_prompt<M: PromptMode>(&mut self, text: Text) {
self.prompts.entry(TypeId::of::<M>()).or_insert(text);
}
pub fn prompt_of_id(&self, id: TypeId) -> Option<Text> {
self.prompts.get(&id).cloned()
}
}
impl Widget for PromptLine {
fn text(&self) -> &Text {
&self.text
}
fn text_mut(&mut self) -> TextMut<'_> {
self.text.as_mut()
}
fn print_opts(&self) -> PrintOpts {
let mut opts = PrintOpts::default_for_input();
opts.force_scrolloff = true;
opts
}
}
pub struct PromptLineBuilder {
prompts: Option<HashMap<TypeId, Text>>,
specs: PushSpecs,
request_width: bool,
}
impl Default for PromptLineBuilder {
fn default() -> Self {
Self {
prompts: None,
specs: PushSpecs {
side: Side::Below,
height: Some(1.0),
..Default::default()
},
request_width: false,
}
}
}
impl PromptLineBuilder {
pub fn push_on(self, pa: &mut Pass, push_target: &impl PushTarget) -> Handle<PromptLine> {
let promptline = PromptLine {
text: Text::default(),
prompts: self.prompts.unwrap_or_default(),
request_width: self.request_width,
};
push_target.push_outer(pa, promptline, self.specs)
}
pub fn set_prompt<M: PromptMode>(mut self, prompt: Text) -> Self {
self.prompts
.get_or_insert_default()
.insert(TypeId::of::<M>(), prompt);
self
}
pub fn above(self) -> Self {
Self {
specs: PushSpecs { side: Side::Above, ..self.specs },
..self
}
}
pub fn below(self) -> Self {
Self {
specs: PushSpecs { side: Side::Below, ..self.specs },
..self
}
}
pub fn hidden(self) -> Self {
Self {
specs: PushSpecs { hidden: true, ..self.specs },
..self
}
}
pub(crate) fn request_width(self) -> Self {
Self { request_width: true, ..self }
}
}