use glutin::VirtualKeyCode;
use super::{InterfaceItem, InterfaceItemBase};
use crate::text_processing::{OptTextStyle, ProcessedChar, TextProcessor};
use crate::{Events, TextBuffer, TextStyle};
use std::iter::repeat;
#[derive(Debug, Clone)]
pub struct Dialog {
pub unfocused_style: TextStyle,
pub focused_style: TextStyle,
pub up_buttons: Vec<VirtualKeyCode>,
pub down_buttons: Vec<VirtualKeyCode>,
base: InterfaceItemBase,
width: u32,
min_height: Option<u32>,
max_height: Option<u32>,
text: String,
rows: Vec<Vec<ProcessedChar>>,
processed_text: Vec<ProcessedChar>,
needs_processing: bool,
scroll_idx: u32,
}
impl Dialog {
pub fn new<T: Into<Option<u32>>, U: Into<Option<u32>>>(
width: u32,
min_height: T,
max_height: U,
) -> Dialog {
Dialog {
unfocused_style: TextStyle {
bg_color: [0.0, 0.0, 0.0, 0.0],
fg_color: [0.8, 0.8, 0.8, 1.0],
..Default::default()
},
focused_style: TextStyle {
bg_color: [0.8, 0.8, 0.8, 1.0],
fg_color: [0.2, 0.2, 0.2, 1.0],
..Default::default()
},
up_buttons: vec![VirtualKeyCode::Up],
down_buttons: vec![VirtualKeyCode::Down],
base: InterfaceItemBase::new(true),
width: width,
min_height: min_height.into(),
max_height: max_height.into(),
text: String::new(),
rows: Vec::new(),
processed_text: Vec::new(),
needs_processing: true,
scroll_idx: 0,
}
}
with_base!(Dialog);
with_style!(Dialog);
pub fn with_width(mut self, width: u32) -> Dialog {
self.width = width;
self.needs_processing = true;
self
}
pub fn with_min_height(mut self, min_height: Option<u32>) -> Dialog {
self.min_height = min_height;
self
}
pub fn with_max_height(mut self, max_height: Option<u32>) -> Dialog {
self.max_height = max_height;
self
}
pub fn with_text<T: Into<String>>(mut self, text: T) -> Dialog {
self.text = text.into();
self.needs_processing = true;
self
}
pub fn with_up_buttons(mut self, buttons: Vec<VirtualKeyCode>) -> Dialog {
self.up_buttons = buttons;
self
}
pub fn with_down_buttons(mut self, buttons: Vec<VirtualKeyCode>) -> Dialog {
self.down_buttons = buttons;
self
}
pub fn set_width(&mut self, width: u32) {
self.width = width;
self.needs_processing = true;
}
pub fn set_min_height(&mut self, min_height: Option<u32>) {
self.min_height = min_height;
}
pub fn set_max_height(&mut self, max_height: Option<u32>) {
self.max_height = max_height;
}
pub fn set_text<T: Into<String>>(&mut self, text: T) {
self.text = text.into();
self.needs_processing = true;
}
pub fn scroll_up(&mut self) -> bool {
if self.scroll_idx > 0 {
self.scroll_idx -= 1;
self.base.dirty = true;
true
} else {
false
}
}
pub fn scroll_down(&mut self) -> bool {
if self.scroll_idx + 1 < self.rows.len() as u32 {
self.scroll_idx += 1;
self.base.dirty = true;
true
} else {
false
}
}
pub fn get_scroll(&self) -> u32 {
self.scroll_idx
}
pub fn set_scroll(&mut self, scroll: u32) {
if scroll < self.rows.len() as u32 {
self.scroll_idx = scroll;
}
}
fn update_rows(&mut self) {
let width = self.width;
let mut words = Vec::new();
let mut curr_word = Vec::new();
for c in self.processed_text.clone() {
if ((c.character == ' ' || c.character == '\n') && !curr_word.is_empty())
|| curr_word.len() as u32 >= width
{
words.push(curr_word);
curr_word = Vec::new();
} else {
curr_word.push(c);
}
}
if !curr_word.is_empty() {
words.push(curr_word);
}
let mut last_style = OptTextStyle {
fg_color: None,
bg_color: None,
shakiness: None,
};
self.rows = Vec::new();
let mut curr_row = Vec::new();
for word in words.iter_mut() {
if ((curr_row.len() + word.len() + 1) as u32) <= width {
if !curr_row.is_empty() {
curr_row.push(ProcessedChar {
character: ' ',
style: last_style.clone(),
});
}
curr_row.append(&mut word.clone());
} else {
self.rows.push(curr_row);
curr_row = word.clone();
}
if let Some(last) = word.last() {
last_style = last.style.clone();
}
}
self.rows.push(curr_row);
}
}
impl InterfaceItem for Dialog {
fn get_base(&self) -> &InterfaceItemBase {
&self.base
}
fn get_mut_base(&mut self) -> &mut InterfaceItemBase {
&mut self.base
}
fn get_total_width(&self) -> u32 {
self.width
}
fn get_total_height(&self) -> u32 {
if let Some(max_height) = self.max_height {
max_height
} else if let Some(min_height) = self.min_height {
min_height.max(self.rows.len() as u32)
} else {
self.rows.len() as u32
}
}
fn draw(&mut self, text_buffer: &mut TextBuffer) {
self.base.dirty = false;
text_buffer.cursor.style = if self.base.is_focused() {
self.focused_style
} else {
self.unfocused_style
};
let none_style = OptTextStyle {
fg_color: None,
bg_color: None,
shakiness: None,
};
for idx in 0..self.get_total_height() {
let text: Vec<ProcessedChar>;
if let Some(row) = self.rows.get((self.scroll_idx + idx) as usize) {
text = row
.iter()
.cloned()
.chain(
repeat(ProcessedChar {
character: ' ',
style: none_style.clone(),
})
.take(self.width as usize - row.len()),
)
.collect();
} else {
text = repeat(ProcessedChar {
character: ' ',
style: none_style.clone(),
})
.take(self.width as usize)
.collect();
}
text_buffer.cursor.move_to(self.base.x, self.base.y + idx);
text_buffer.write_processed(&text);
}
}
fn handle_events(&mut self, events: &Events) -> bool {
let mut handled = false;
for button in events.keyboard.get_just_pressed_list() {
if self.down_buttons.contains(&button) {
handled = handled || self.scroll_down();
} else if self.up_buttons.contains(&button) {
handled = handled || self.scroll_up();
}
}
handled
}
fn update(&mut self, _: f32, processor: &TextProcessor) {
if self.needs_processing {
self.processed_text = processor.process(vec![self.text.clone().into()]);
self.update_rows();
self.needs_processing = false;
}
}
}