use crate::widget::{WidgetCommand, WidgetError};
use crate::widget_manager::WidgetBox;
use crate::{SizeConstraints, SystemEvent, Widget, WidgetEvent, WidgetId};
use druid_shell::kurbo::{Point, Rect, RoundedRect, Size};
use druid_shell::piet::{Color, LinearGradient, PaintBrush, Piet, RenderContext, UnitPoint};
use druid_shell::{piet, Region};
pub struct Button {
child_widget: Option<WidgetBox>,
corner_radius: f64,
debug_rendering: bool,
fill_brush_down: Option<PaintBrush>,
fill_brush_up: Option<PaintBrush>,
has_focus: bool,
is_disabled: bool,
is_down: bool,
is_hidden: bool,
is_hot: bool,
padding_horizontal: f64,
padding_vertical: f64,
rectangle: Rect,
size_constraints: SizeConstraints,
stroke_brush: Option<PaintBrush>,
stroke_brush_focused: Option<PaintBrush>,
stroke_width: f64,
widget_id: WidgetId,
}
impl Button {
pub fn new(
widget_id: WidgetId,
child_widget: WidgetBox,
frame_color: Option<Color>,
frame_color_focused: Option<Color>,
) -> Self {
Button {
child_widget: Some(child_widget),
corner_radius: 4.0,
debug_rendering: false,
fill_brush_down: Some(PaintBrush::Color(Color::rgb8(0, 0, 0))),
fill_brush_up: Some(PaintBrush::Linear(LinearGradient::new(
UnitPoint::TOP,
UnitPoint::BOTTOM,
(Color::rgb8(100, 100, 100), Color::rgb8(10, 10, 10)),
))),
has_focus: false,
is_disabled: false,
is_down: false,
is_hidden: false,
is_hot: false,
padding_horizontal: 4.0,
padding_vertical: 4.0,
rectangle: Rect::default(),
size_constraints: SizeConstraints::default(),
stroke_brush: frame_color.map(PaintBrush::Color),
stroke_brush_focused: frame_color_focused.map(PaintBrush::Color),
stroke_width: 1.0,
widget_id,
}
}
fn layout_child(&mut self) {
if let Some(child_widget) = &mut self.child_widget {
let padding_size =
Size::new(2.0 * self.padding_horizontal, 2.0 * self.padding_vertical);
let child_size = child_widget
.borrow_mut()
.apply_size_constraints(self.size_constraints.shrink(padding_size));
child_widget.borrow_mut().set_origin(
self.rectangle.origin()
+ (
0.5 * (self.rectangle.size().width - child_size.width).max(0.0),
0.5 * (self.rectangle.size().height - child_size.height).max(0.0),
),
);
self.rectangle = self.rectangle.with_size(child_size + padding_size);
}
else {
self.rectangle = self.rectangle.with_size(*self.size_constraints.maximum());
}
}
}
impl Widget for Button {
fn apply_size_constraints(&mut self, size_constraints: SizeConstraints) -> Size {
self.size_constraints = size_constraints;
self.layout_child();
self.rectangle.size()
}
fn handle_command(&mut self, widget_command: WidgetCommand) -> Result<(), WidgetError> {
match widget_command {
WidgetCommand::AppendChild(child_widget) => {
self.child_widget = Some(child_widget);
self.layout_child();
}
WidgetCommand::RemoveAllChildren => {
self.child_widget = None;
}
WidgetCommand::RemoveChild(_) => {
println!("`Button::handle_command()`: TODO");
}
WidgetCommand::SetDebugRendering(debug_rendering) => {
self.debug_rendering = debug_rendering;
}
WidgetCommand::SetHasFocus(has_focus) => {
self.has_focus = has_focus;
}
WidgetCommand::SetIsDisabled(is_disabled) => {
self.is_disabled = is_disabled;
}
WidgetCommand::SetIsHidden(is_hidden) => {
self.is_hidden = is_hidden;
}
WidgetCommand::SetValue(_) => {
println!("`Button::handle_command()`: TODO");
}
}
Ok(())
}
fn handle_event(&mut self, system_event: &SystemEvent, widget_events: &mut Vec<WidgetEvent>) {
match system_event {
SystemEvent::KeyDown(_) => {
println!("`Button::handle_event()`: TODO");
}
SystemEvent::KeyUp(_) => {}
SystemEvent::MouseDown(mouse_event) => {
if self.rectangle.contains(mouse_event.pos) {
if !self.has_focus {
self.has_focus = true;
widget_events.push(WidgetEvent::GotFocus(self.widget_id))
}
self.is_down = true;
self.is_hot = true;
}
else {
if self.has_focus {
widget_events.push(WidgetEvent::LostFocus(self.widget_id))
}
self.has_focus = false;
self.is_down = false;
self.is_hot = false;
}
}
SystemEvent::MouseMove(mouse_event) => {
if self.is_hot && self.rectangle.contains(mouse_event.pos) {
self.is_down = true;
}
else {
self.is_down = false;
}
}
SystemEvent::MouseUp(mouse_event) => {
if self.is_hot && self.rectangle.contains(mouse_event.pos) {
widget_events.push(WidgetEvent::Clicked(self.widget_id));
}
self.is_down = false;
self.is_hot = false;
}
}
}
fn paint(&self, piet: &mut Piet, region: &Region) -> Result<(), piet::Error> {
if self.is_hidden {
return Ok(());
}
{
let button_shape = RoundedRect::from_rect(self.rectangle, self.corner_radius);
if self.is_down {
if let Some(brush) = &self.fill_brush_down {
piet.fill(button_shape, brush);
}
} else {
if let Some(brush) = &self.fill_brush_up {
piet.fill(button_shape, brush);
}
}
if self.has_focus {
if let Some(brush) = &self.stroke_brush_focused {
piet.stroke(button_shape, brush, self.stroke_width);
}
}
else {
if let Some(brush) = &self.stroke_brush {
piet.stroke(button_shape, brush, self.stroke_width);
}
}
}
if let Some(child_widget_rc) = &self.child_widget {
child_widget_rc.borrow().paint(piet, region)?;
}
Ok(())
}
fn set_origin(&mut self, origin: Point) {
self.rectangle = self.rectangle.with_origin(origin);
self.layout_child();
}
fn widget_id(&self) -> &WidgetId {
&self.widget_id
}
}