mod state;
use duat_core::{context::DynFile, prelude::*, text::Builder, ui::Side};
pub use self::{macros::status, state::State};
use crate::state::{name_txt, main_txt, mode_txt, sels_txt};
pub struct StatusLine<U: Ui> {
file_handle: FileHandle<U>,
text_fn: TextFn<U>,
text: Text,
checker: Box<dyn Fn(&Pass) -> bool + Send>,
}
impl<U: Ui> Widget<U> for StatusLine<U> {
type Cfg = StatusLineCfg<U>;
fn update(pa: &mut Pass, handle: &Handle<Self, U>) {
if let FileHandle::Dynamic(dyn_file) = &mut handle.write(pa).file_handle {
dyn_file.swap_to_current();
}
let sl = handle.read(pa);
handle.write(pa).text = match &sl.file_handle {
FileHandle::Fixed(file) => (sl.text_fn)(pa, file),
FileHandle::Dynamic(dyn_file) => (sl.text_fn)(pa, dyn_file.handle()),
};
}
fn needs_update(&self, pa: &Pass) -> bool {
let file_changed = match &self.file_handle {
FileHandle::Fixed(handle) => handle.has_changed(),
FileHandle::Dynamic(dyn_file) => dyn_file.has_changed(pa),
};
file_changed || (self.checker)(pa)
}
fn cfg() -> Self::Cfg {
StatusLineCfg {
fns: None,
specs: PushSpecs::below().ver_len(1.0),
}
}
fn text(&self) -> &Text {
&self.text
}
fn text_mut(&mut self) -> &mut Text {
&mut self.text
}
fn once() -> Result<(), Text> {
form::set_weak("file", Form::yellow().italic());
form::set_weak("selections", Form::dark_blue());
form::set_weak("coord", Form::dark_yellow());
form::set_weak("separator", Form::cyan());
form::set_weak("mode", Form::green());
Ok(())
}
}
pub struct StatusLineCfg<U: Ui> {
fns: Option<(BuilderFn<U>, CheckerFn)>,
specs: PushSpecs,
}
impl<U: Ui> StatusLineCfg<U> {
#[doc(hidden)]
pub fn new_with(fns: (BuilderFn<U>, CheckerFn), specs: PushSpecs) -> Self {
Self { fns: Some(fns), specs }
}
pub fn fmt(self, new: Self) -> Self {
Self { specs: self.specs, ..new }
}
pub fn above(self) -> Self {
Self {
specs: PushSpecs::above().ver_len(1.0),
..self
}
}
pub fn below(self) -> Self {
Self {
specs: PushSpecs::below().ver_len(1.0),
..self
}
}
pub fn right_ratioed(self, den: u16, div: u16) -> Self {
Self {
specs: self.specs.to_right().hor_ratio(den, div),
..self
}
}
pub fn specs(&self) -> PushSpecs {
self.specs
}
}
impl<U: Ui> WidgetCfg<U> for StatusLineCfg<U> {
type Widget = StatusLine<U>;
fn build(self, pa: &mut Pass, info: BuildInfo<U>) -> (Self::Widget, PushSpecs) {
let (builder_fn, checker_fn) = if let Some((builder, checker)) = self.fns {
(builder, checker)
} else {
let mode_txt = mode_txt(pa);
let cfg = match self.specs.side() {
Side::Above | Side::Below => {
macros::status!("{mode_txt}{Spacer}{name_txt} {sels_txt} {main_txt}")
}
Side::Right => {
macros::status!("{AlignRight}{name_txt} {mode_txt} {sels_txt} {main_txt}",)
}
Side::Left => unreachable!(),
};
cfg.fns.unwrap()
};
let widget = StatusLine {
file_handle: match info.file() {
Some(handle) => FileHandle::Fixed(handle),
None => FileHandle::Dynamic(context::dyn_file(pa).unwrap()),
},
text_fn: Box::new(move |pa, fh| {
let builder = Text::builder();
builder_fn(pa, builder, fh)
}),
text: Text::default(),
checker: Box::new(checker_fn),
};
(widget, self.specs)
}
}
impl<U: Ui> Default for StatusLineCfg<U> {
fn default() -> Self {
StatusLine::cfg()
}
}
mod macros {
pub macro status($($parts:tt)*) {{
#[allow(unused_imports)]
use $crate::{
private_exports::{
duat_core::{context::Handle, data::Pass, file::File, ui::PushSpecs, text::Builder},
format_like, parse_form, parse_status_part, parse_str
},
widgets::StatusLineCfg,
};
let text_fn = |_: &Pass, _: &mut Builder, _: &Handle<File<_>, _>| {};
let checker = |_: &Pass| false;
let (text_fn, checker) = format_like!(
parse_str,
[('{', parse_status_part, false), ('[', parse_form, true)],
(text_fn, checker),
$($parts)*
);
StatusLineCfg::new_with(
(
Box::new(move |pa: &Pass, mut builder: Builder, handle: &Handle<File<_>, _>| {
text_fn(pa, &mut builder, &handle);
builder.build()
}),
Box::new(checker)
),
PushSpecs::below().ver_len(1.0),
)
}}
}
type TextFn<U> = Box<dyn Fn(&Pass, &Handle<File<U>, U>) -> Text + Send>;
type BuilderFn<U> = Box<dyn Fn(&Pass, Builder, &Handle<File<U>, U>) -> Text + Send>;
type CheckerFn = Box<dyn Fn(&Pass) -> bool + Send>;
enum FileHandle<U: Ui> {
Fixed(Handle<File<U>, U>),
Dynamic(DynFile<U>),
}