use crate::common::Size;
use egui::{vec2, FontId, Response, Sense, Stroke, Ui, Widget};
use egui_components_theme::{mix, Theme};
pub struct Input<'a> {
value: &'a mut String,
placeholder: Option<String>,
width: Option<f32>,
password: bool,
disabled: bool,
size: Size,
}
impl<'a> Input<'a> {
pub fn new(value: &'a mut String) -> Self {
Self {
value,
placeholder: None,
width: None,
password: false,
disabled: false,
size: Size::Medium,
}
}
pub fn placeholder(mut self, p: impl Into<String>) -> Self {
self.placeholder = Some(p.into());
self
}
pub fn width(mut self, w: f32) -> Self {
self.width = Some(w);
self
}
pub fn password(mut self, p: bool) -> Self {
self.password = p;
self
}
pub fn disabled(mut self, d: bool) -> Self {
self.disabled = d;
self
}
pub fn size(mut self, s: Size) -> Self {
self.size = s;
self
}
pub fn small(self) -> Self {
self.size(Size::Small)
}
pub fn large(self) -> Self {
self.size(Size::Large)
}
}
impl<'a> Widget for Input<'a> {
fn ui(self, ui: &mut Ui) -> Response {
let theme = Theme::get(ui.ctx());
let m = theme.metrics;
let c = theme.colors;
let height = self.size.input_height(&m);
let width = self.width.unwrap_or_else(|| ui.available_width().min(240.0));
let desired = vec2(width, height);
let (rect, response) = ui.allocate_exact_size(desired, Sense::hover());
let painter = ui.painter();
let radius = theme.corner();
let bg = if self.disabled {
mix(c.background, c.muted_background, 0.6)
} else {
c.background
};
painter.rect_filled(rect, radius, bg);
let mut border_color = c.input_border;
let mut has_focus = false;
let inner_rect = rect.shrink2(vec2(m.input_padding_x, 4.0));
let inner_response = {
let mut child = ui.new_child(
egui::UiBuilder::new()
.max_rect(inner_rect)
.layout(egui::Layout::left_to_right(egui::Align::Center)),
);
if self.disabled {
child.disable();
}
let edit = egui::TextEdit::singleline(self.value)
.frame(egui::Frame::NONE)
.desired_width(inner_rect.width())
.password(self.password)
.font(FontId::proportional(m.font_size_md))
.text_color(if self.disabled {
mix(c.foreground, c.muted_foreground, 0.5)
} else {
c.foreground
});
let r = child.add(edit);
if r.has_focus() {
has_focus = true;
}
r
};
if has_focus {
border_color = c.ring;
} else if response.hovered() || inner_response.hovered() {
border_color = mix(c.input_border, c.foreground, 0.25);
}
ui.painter().rect_stroke(
rect,
radius,
Stroke::new(m.border_width, border_color),
egui::StrokeKind::Inside,
);
if has_focus {
ui.painter().rect_stroke(
rect.expand(2.0),
theme.corner(),
theme.focus_ring(),
egui::StrokeKind::Outside,
);
}
if self.value.is_empty() && !has_focus {
if let Some(ph) = &self.placeholder {
let font = FontId::proportional(m.font_size_md);
ui.painter().text(
egui::pos2(inner_rect.left(), inner_rect.center().y),
egui::Align2::LEFT_CENTER,
ph,
font,
c.muted_foreground,
);
}
}
if !self.disabled && (response.hovered() || inner_response.hovered()) {
ui.ctx().set_cursor_icon(egui::CursorIcon::Text);
}
inner_response.union(response)
}
}