use rusty_bubbles::cursor;
use rusty_bubbles::textinput;
use rusty_bubbletea::cursor::Cursor;
use rusty_bubbletea::model::Model as ModelTrait;
use rusty_bubbletea::{batch, quit, Cmd, KeyPressMsg, Msg, Program, View};
use rusty_lipgloss::{Color, Style};
fn focused_style() -> Style {
Style::new().foreground("205")
}
fn blurred_style() -> Style {
Style::new().foreground("240")
}
fn help_style() -> Style {
blurred_style()
}
fn cursor_mode_help_style() -> Style {
Style::new().foreground("244")
}
fn focused_button() -> String {
focused_style().render("[ Submit ]")
}
fn blurred_button() -> String {
format!("[ {} ]", blurred_style().render("Submit"))
}
struct Model {
focus_index: usize,
inputs: Vec<textinput::Model>,
cursor_mode: cursor::Mode,
quitting: bool,
}
impl Model {
fn initial_model() -> Self {
let mut m = Model {
focus_index: 0,
inputs: vec![textinput::new(), textinput::new(), textinput::new()],
cursor_mode: cursor::Mode::Blink,
quitting: false,
};
for i in 0..m.inputs.len() {
m.inputs[i].char_limit = 32;
let mut s = m.inputs[i].styles().clone();
s.cursor.color = Color::parse("205");
s.focused.prompt = focused_style();
s.focused.text = focused_style();
s.blurred.prompt = blurred_style();
m.inputs[i].set_styles(s);
match i {
0 => {
m.inputs[i].placeholder = "Nickname".to_string();
m.inputs[i].focus();
}
1 => {
m.inputs[i].placeholder = "Email".to_string();
m.inputs[i].char_limit = 64;
}
2 => {
m.inputs[i].placeholder = "Password".to_string();
m.inputs[i].echo_mode = textinput::EchoMode::EchoPassword;
m.inputs[i].echo_character = '•';
}
_ => {}
}
}
m
}
fn update_inputs(&mut self, msg: &dyn Msg) -> Cmd {
let mut cmds: Vec<Cmd> = Vec::new();
for i in 0..self.inputs.len() {
cmds.push(self.inputs[i].update(msg));
}
batch(cmds)
}
}
impl ModelTrait for Model {
fn init(&self) -> Cmd {
Some(Box::new(|| Some(textinput::blink())))
}
fn update(&mut self, msg: &dyn Msg) -> Cmd {
if let Some(k) = msg.as_any().downcast_ref::<KeyPressMsg>() {
match k.0.to_string().as_str() {
"ctrl+c" | "esc" => {
self.quitting = true;
return quit();
}
"ctrl+r" => {
self.cursor_mode = match self.cursor_mode {
cursor::Mode::Blink => cursor::Mode::Static,
cursor::Mode::Static => cursor::Mode::Hide,
cursor::Mode::Hide => cursor::Mode::Blink,
};
for i in 0..self.inputs.len() {
let mut s = self.inputs[i].styles().clone();
s.cursor.blink = self.cursor_mode == cursor::Mode::Blink;
self.inputs[i].set_styles(s);
}
return None;
}
"tab" | "shift+tab" | "enter" | "up" | "down" => {
let s = k.0.to_string();
if s == "enter" && self.focus_index == self.inputs.len() {
return quit();
}
if s == "up" || s == "shift+tab" {
if self.focus_index == 0 {
self.focus_index = self.inputs.len();
} else {
self.focus_index -= 1;
}
} else {
self.focus_index += 1;
}
if self.focus_index > self.inputs.len() {
self.focus_index = 0;
}
let mut cmds: Vec<Cmd> = Vec::new();
for i in 0..self.inputs.len() {
if i == self.focus_index {
cmds.push(self.inputs[i].focus());
continue;
}
self.inputs[i].blur();
}
return batch(cmds);
}
_ => {}
}
}
self.update_inputs(msg)
}
fn view(&self) -> View {
let mut b = String::new();
let mut c: Option<Cursor> = None;
for (i, input) in self.inputs.iter().enumerate() {
b += &self.inputs[i].view();
if i < self.inputs.len() - 1 {
b += "\n";
}
if self.cursor_mode != cursor::Mode::Hide && input.focused() {
if let Some(mut cur) = input.cursor() {
cur.position.y += i;
c = Some(cur);
}
}
}
let button = if self.focus_index == self.inputs.len() {
focused_button()
} else {
blurred_button()
};
b += &format!("\n\n{}\n\n", button);
b += &help_style().render("cursor mode is ");
b += &cursor_mode_help_style().render(self.cursor_mode.to_string());
b += &help_style().render(" (ctrl+r to change style)");
if self.quitting {
b += "\n";
}
let mut v = View::new(&b);
v.cursor = c;
v
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let p = Program::new(Model::initial_model());
p.run()?;
Ok(())
}