use crate::frame::context::UpdateType;
use crate::frame::App;
use crate::layout::popup::Popup;
use crate::render::rectangle::param::RectParam;
use crate::render::{RenderParam, WrcRender};
use crate::response::{Callback, Response};
use crate::size::border::Border;
use crate::size::padding::Padding;
use crate::size::radius::Radius;
use crate::size::rect::Rect;
use crate::size::SizeMode;
use crate::style::color::Color;
use crate::style::ClickStyle;
use crate::text::buffer::TextBuffer;
use crate::ui::Ui;
use crate::widgets::select::SelectItem;
use crate::widgets::{Widget, WidgetChange, WidgetSize};
use std::fmt::Display;
use std::sync::{Arc, RwLock};
use crate::{Offset, Pos};
use crate::align::Align;
pub struct ComboBox<T> {
pub(crate) id: String,
popup_id: String,
size_mode: SizeMode,
text_buffer: TextBuffer,
data: Vec<T>,
popup_rect: Rect,
callback: Option<Box<dyn FnMut(&mut Box<dyn App>, &mut Ui, &T)>>,
fill_render: RenderParam<RectParam>,
selected: Arc<RwLock<Option<String>>>,
previous_select: Option<String>,
changed: bool,
}
impl<T: Display + 'static> ComboBox<T> {
pub fn new(data: Vec<T>) -> Self {
let mut fill_style = ClickStyle::new();
fill_style.fill.inactive = Color::rgb(230, 230, 230);
fill_style.border.inactive = Border::new(1.0).radius(Radius::same(3)).color(Color::rgba(144, 209, 255, 255));
ComboBox {
id: crate::gen_unique_id(),
popup_id: "".to_string(),
size_mode: SizeMode::Fix(100.0, 20.0),
text_buffer: TextBuffer::new("123456".to_string()).with_align(Align::LeftCenter),
data,
popup_rect: Rect::new().with_size(100.0, 150.0),
callback: None,
fill_render: RenderParam::new(RectParam::new(Rect::new().with_size(100.0, 20.0), fill_style)),
previous_select: None,
selected: Arc::new(RwLock::new(None)),
changed: false,
}
}
fn reset_size(&mut self, ui: &mut Ui) {
self.text_buffer.size_mode = self.size_mode.clone();
self.text_buffer.init(ui);
let (w, h) = self.size_mode.size(self.fill_render.param.rect.width(), self.fill_render.param.rect.height());
self.fill_render.param.rect.set_size(w, h);
}
pub fn with_size(mut self, width: f32, height: f32) -> Self {
self.size_mode = SizeMode::Fix(width, height);
self
}
pub fn with_popup_height(mut self, height: f32) -> Self {
self.popup_rect.set_height(height);
self
}
fn add_item(&self, ui: &mut Ui, item: &T) {
let mut select = SelectItem::new(item.to_string()).padding(Padding::same(3.0))
.contact(self.selected.clone()).align(Align::LeftCenter);
select.set_size(self.popup_rect.width() - 10.0, 25.0);
ui.add(select);
}
fn add_items(&self, ui: &mut Ui) {
for datum in self.data.iter() {
self.add_item(ui, datum);
}
}
pub fn connect<A: 'static>(mut self, f: fn(&mut A, &mut Ui, &T)) -> Self {
self.callback = Some(Callback::create_combobox(f));
self
}
fn init(&mut self, ui: &mut Ui) {
self.reset_size(ui);
let popup = Popup::new(ui, self.popup_rect.width(), self.popup_rect.height());
self.popup_id = popup.id.clone();
popup.show(ui, |ui| self.add_items(ui));
self.re_init(ui);
}
fn re_init(&mut self, ui: &mut Ui) {
let mut fill_style = ClickStyle::new();
fill_style.fill.inactive = Color::rgb(230, 230, 230);
fill_style.border.inactive = Border::new(1.0).radius(Radius::same(3)).color(Color::rgba(144, 209, 255, 255));
self.fill_render.init_rectangle(ui, false, false);
self.text_buffer.init(ui);
}
pub fn parent(&self) -> Arc<RwLock<Option<String>>> {
self.selected.clone()
}
fn update_buffer(&mut self, ui: &mut Ui) {
let select = self.selected.read().unwrap();
if *select != self.previous_select {
self.previous_select = select.clone();
if let Some(ref select) = self.previous_select {
self.text_buffer.update_buffer_text(ui, select);
if let Some(ref mut callback) = self.callback {
let app = ui.app.take().unwrap();
let t = self.data.iter().find(|x| &x.to_string() == select).unwrap();
callback(app, ui, t);
ui.app.replace(app);
ui.context.window.request_redraw();
}
self.changed = true;
}
let popup = &mut ui.popups.as_mut().unwrap()[&self.popup_id];
popup.open = false;
}
if self.changed { ui.widget_changed |= WidgetChange::Value; }
self.changed = false;
if ui.widget_changed.contains(WidgetChange::Position) {
self.fill_render.param.rect.offset_to_rect(&ui.draw_rect);
self.fill_render.update(ui, false, false);
self.popup_rect.offset_to_rect(&ui.draw_rect);
self.popup_rect.offset_y(&Offset::new(Pos::new()).covered().with_y(self.fill_render.param.rect.height() + 5.0));
ui.popups.as_mut().unwrap()[&self.popup_id].set_rect(self.popup_rect.clone());
let mut text_rect = self.fill_render.param.rect.clone();
text_rect.add_min_x(2.0);
self.text_buffer.rect = text_rect;
}
if ui.widget_changed.contains(WidgetChange::Value) {
self.text_buffer.update_buffer(ui);
}
}
fn redraw(&mut self, ui: &mut Ui) {
self.update_buffer(ui);
let pass = ui.pass.as_mut().unwrap();
ui.context.render.rectangle.render(&self.fill_render, pass);
self.text_buffer.redraw(ui);
}
}
impl<T: Display + 'static> Widget for ComboBox<T> {
fn update(&mut self, ui: &mut Ui) -> Response<'_> {
match ui.update_type {
UpdateType::Draw => self.redraw(ui),
UpdateType::Init => self.init(ui),
UpdateType::ReInit => self.re_init(ui),
UpdateType::MouseRelease => {
if ui.device.device_input.click_at(&self.fill_render.param.rect) {
let popup = &mut ui.popups.as_mut().unwrap()[&self.popup_id];
popup.open = !popup.open;
ui.update_type = UpdateType::None;
ui.context.window.request_redraw();
}
}
_ => {}
}
Response::new(&self.id, WidgetSize::same(self.fill_render.param.rect.width(), self.fill_render.param.rect.height()))
}
}