fission-command-ui 0.14.1

Terminal UI application for the fission command
Documentation
use crate::density::UiDensity;
use crate::state::UiState;
use crate::theme::UiPalette;
use fission::op::Fill;
use fission::prelude::*;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ButtonTone {
    Primary,
    Neutral,
    Success,
    Warning,
}

#[derive(Clone)]
pub struct ActionButton {
    pub label: String,
    pub action: ActionEnvelope,
    pub tone: ButtonTone,
    pub width: Option<f32>,
}

impl ActionButton {
    pub fn new(label: impl Into<String>, action: ActionEnvelope) -> Self {
        Self {
            label: label.into(),
            action,
            tone: ButtonTone::Neutral,
            width: None,
        }
    }

    pub fn tone(mut self, tone: ButtonTone) -> Self {
        self.tone = tone;
        self
    }

    pub fn width(mut self, width: f32) -> Self {
        self.width = Some(width);
        self
    }
}

impl From<ActionButton> for Widget {
    fn from(component: ActionButton) -> Self {
        let (_ctx, view) = fission::build::current::<UiState>();
        let palette = UiPalette::for_mode(view.state().theme_mode);
        let (background, text) = match component.tone {
            ButtonTone::Primary => (palette.accent, palette.accent_text),
            ButtonTone::Neutral => (palette.subtle, palette.text),
            ButtonTone::Success => (palette.success, palette.accent_text),
            ButtonTone::Warning => (palette.warning, palette.accent_text),
        };
        let marker = match component.tone {
            ButtonTone::Primary => ">",
            ButtonTone::Neutral => "-",
            ButtonTone::Success => "+",
            ButtonTone::Warning => "!",
        };
        let label = format!("[{marker} {}]", component.label);
        let density = UiDensity::new(view.state().compact_mode);
        Button {
            on_press: Some(component.action.clone()),
            width: component.width,
            height: Some(density.control_height()),
            padding: Some(density.control_padding()),
            background_fill: Some(Fill::Solid(background)),
            text_color: Some(text),
            child: Some(Text::new(label).color(text).into()),
            ..Default::default()
        }
        .into()
    }
}
#[derive(Clone)]
pub struct TogglePill {
    pub label: String,
    pub enabled: bool,
    pub action: ActionEnvelope,
}

impl TogglePill {
    pub fn new(label: impl Into<String>, enabled: bool, action: ActionEnvelope) -> Self {
        Self {
            label: label.into(),
            enabled,
            action,
        }
    }
}

impl From<TogglePill> for Widget {
    fn from(component: TogglePill) -> Self {
        let (_ctx, _view) = fission::build::current::<UiState>();
        let label = if component.enabled {
            format!("[x] {}", component.label)
        } else {
            format!("[ ] {}", component.label)
        };
        let tone = if component.enabled {
            ButtonTone::Primary
        } else {
            ButtonTone::Neutral
        };
        ActionButton::new(label, component.action.clone())
            .tone(tone)
            .into()
    }
}
#[derive(Clone)]
pub struct FormTextField {
    pub id: &'static str,
    pub label: String,
    pub value: String,
    pub placeholder: String,
    pub action: ActionEnvelope,
    pub width: f32,
}

impl FormTextField {
    pub fn new(
        id: &'static str,
        label: impl Into<String>,
        value: impl Into<String>,
        placeholder: impl Into<String>,
        action: ActionEnvelope,
    ) -> Self {
        Self {
            id,
            label: label.into(),
            value: value.into(),
            placeholder: placeholder.into(),
            action,
            width: 32.0,
        }
    }

    pub fn width(mut self, width: f32) -> Self {
        self.width = width;
        self
    }
}

impl From<FormTextField> for Widget {
    fn from(component: FormTextField) -> Self {
        let (_ctx, view) = fission::build::current::<UiState>();
        let palette = UiPalette::for_mode(view.state().theme_mode);
        let density = UiDensity::new(view.state().compact_mode);
        TextInput {
            id: Some(WidgetId::explicit(component.id)),
            value: component.value.clone(),
            label: Some(component.label.clone().into()),
            placeholder: Some(component.placeholder.clone().into()),
            on_input: Some(component.action.clone()),
            width: Some(component.width),
            height: Some(density.text_input_height()),
            padding: Some(density.text_input_padding()),
            background_fill: Some(Fill::Solid(palette.surface)),
            border_color: Some(palette.border),
            focus_border_color: Some(palette.accent),
            text_color: Some(palette.text),
            placeholder_color: Some(palette.muted),
            label_color: Some(palette.muted),
            helper_color: Some(palette.muted),
            ..Default::default()
        }
        .into()
    }
}