use crate::frame::context::{ContextUpdate, UpdateType};
use crate::frame::App;
use crate::render::circle::param::CircleParam;
use crate::render::{RenderParam, WrcRender};
use crate::response::{Callback, Response};
use crate::size::border::Border;
use crate::size::rect::Rect;
use crate::size::SizeMode;
use crate::style::color::Color;
use crate::style::ClickStyle;
use crate::text::rich::RichText;
use crate::text::buffer::TextBuffer;
use crate::ui::Ui;
use crate::widgets::{Widget, WidgetChange, WidgetSize};
pub struct RadioButton {
pub(crate) id: String,
pub(crate) rect: Rect,
pub(crate) value: bool,
pub(crate) text: TextBuffer,
pub(crate) callback: Option<Box<dyn FnMut(&mut Box<dyn App>, &mut Ui, bool)>>,
size_mode: SizeMode,
outer_render: RenderParam<CircleParam>,
inner_render: RenderParam<CircleParam>,
hovered: bool,
contact_ids: Vec<String>,
changed: bool,
}
impl RadioButton {
pub fn new(v: bool, label: impl Into<RichText>) -> RadioButton {
let mut outer_style = ClickStyle::new();
outer_style.fill.inactive = Color::TRANSPARENT; outer_style.fill.hovered = Color::TRANSPARENT; outer_style.fill.clicked = Color::TRANSPARENT; outer_style.border.inactive = Border::new(1.0).color(Color::rgb(95, 95, 95));
outer_style.border.hovered = Border::new(1.0).color(Color::rgb(56, 160, 200));
outer_style.border.clicked = Border::new(1.0).color(Color::rgb(56, 182, 244));
let mut inner_style = ClickStyle::new();
inner_style.fill.inactive = Color::TRANSPARENT;
inner_style.fill.hovered = Color::rgb(56, 160, 200);
inner_style.fill.clicked = Color::rgb(56, 182, 244);
inner_style.border.inactive = Border::new(0.0).color(Color::TRANSPARENT);
inner_style.border.hovered = Border::new(0.0).color(Color::TRANSPARENT);
inner_style.border.clicked = Border::new(0.0).color(Color::TRANSPARENT);
RadioButton {
id: crate::gen_unique_id(),
rect: Rect::new(),
value: v,
text: TextBuffer::new(label),
callback: None,
size_mode: SizeMode::Auto,
outer_render: RenderParam::new(CircleParam::new(Rect::new().with_size(16.0, 16.0), outer_style)),
inner_render: RenderParam::new(CircleParam::new(Rect::new().with_size(16.0, 16.0), inner_style)),
hovered: false,
contact_ids: vec![],
changed: false,
}
}
fn reset_size(&mut self, ui: &mut Ui) {
self.text.init(ui);
let (w, h) = self.size_mode.size(self.text.rect.width() + 18.0, 18.0 + self.text.rect.height());
self.rect.set_size(w, h);
}
pub fn with_width(mut self, width: f32) -> RadioButton {
self.size_mode.fix_width(width);
self
}
pub fn connect<A: 'static>(mut self, f: fn(&mut A, &mut Ui, bool)) -> Self {
self.callback = Some(Callback::create_check(f));
self
}
pub fn set_callback<A: App>(&mut self, f: fn(&mut A, &mut Ui, bool)) {
self.callback = Some(Callback::create_check(f));
}
pub fn id(mut self, id: impl ToString) -> Self {
self.id = id.to_string();
self
}
pub fn contact(mut self, id: impl ToString) -> Self {
self.contact_ids.push(id.to_string());
self
}
fn init(&mut self, ui: &mut Ui) {
self.reset_size(ui);
self.re_init(ui);
}
fn re_init(&mut self, ui: &mut Ui) {
self.outer_render.init_circle(ui, self.value, self.value);
self.inner_render.param.rect.add_min_x(4.0);
self.inner_render.param.rect.contract_y(4.0);
self.inner_render.param.rect.set_width(self.inner_render.param.rect.height());
self.inner_render.init_circle(ui, self.value, self.value);
self.text.init(ui);
}
fn update_buffer(&mut self, ui: &mut Ui) {
if let Some(v) = ui.context.updates.remove(&self.id) {
v.update_bool(&mut self.value);
ui.widget_changed |= WidgetChange::Value;
}
if self.changed { ui.widget_changed |= WidgetChange::Value; }
self.changed = false;
if ui.widget_changed.contains(WidgetChange::Position) {
self.rect.offset_to_rect(&ui.draw_rect);
self.outer_render.param.rect.offset_to_rect(&ui.draw_rect);
self.outer_render.update(ui, self.hovered || self.value, ui.device.device_input.mouse.pressed || self.value);
let mut text_rect = ui.draw_rect.clone();
text_rect.add_min_x(self.outer_render.param.rect.width() + 2.0);
self.text.rect.offset_to_rect(&text_rect);
let mut inner_rect = ui.draw_rect.clone();
inner_rect.set_width(self.inner_render.param.rect.width());
inner_rect.add_min_x(4.0);
inner_rect.add_min_y(4.0);
self.inner_render.param.rect.offset_to_rect(&inner_rect);
self.inner_render.update(ui, self.value, ui.device.device_input.mouse.pressed || self.value);
}
if ui.widget_changed.contains(WidgetChange::Value) {
self.outer_render.update(ui, self.hovered || self.value, ui.device.device_input.mouse.pressed || self.value);
self.inner_render.update(ui, self.value, ui.device.device_input.mouse.pressed || self.value);
}
}
fn redraw(&mut self, ui: &mut Ui) {
self.update_buffer(ui);
let pass = ui.pass.as_mut().unwrap();
ui.context.render.circle.render(&self.outer_render, pass);
ui.context.render.circle.render(&self.inner_render, pass);
self.text.redraw(ui);
}
}
impl Widget for RadioButton {
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::MouseMove => {
let hovered = ui.device.device_input.hovered_at(&self.rect);
if hovered != self.hovered {
self.hovered = hovered;
self.changed = true;
ui.context.window.request_redraw();
}
}
UpdateType::MouseRelease => {
if ui.device.device_input.click_at(&self.rect) {
self.value = !self.value;
self.changed = true;
if let Some(ref mut callback) = self.callback {
let app = ui.app.take().unwrap();
callback(app, ui, self.value);
ui.app.replace(app);
}
ui.update_type = UpdateType::None;
ui.send_updates(&self.contact_ids, ContextUpdate::Bool(self.value));
ui.context.window.request_redraw();
}
}
_ => {}
}
Response::new(&self.id, WidgetSize::same(self.rect.width(), self.rect.height()))
}
}