button.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. use kahlo::prelude::*;
  2. use crate::{
  3. component::Component,
  4. input::MouseButton,
  5. layout::{LayoutNode, LayoutNodeAccess, LayoutNodeContainer, SizePolicy},
  6. ui::UIHandle,
  7. };
  8. use super::{Label, Widget};
  9. enum ButtonState {
  10. Idle,
  11. Hovered,
  12. Clicked,
  13. }
  14. pub struct Button<C: Component> {
  15. layout: LayoutNode,
  16. label: Label<C>,
  17. state: ButtonState,
  18. hook: Option<Box<dyn Fn() -> Option<C::Msg>>>,
  19. }
  20. impl<C: Component> Button<C> {
  21. pub fn new(uih: &UIHandle) -> Self {
  22. let mut layout = uih.new_layout_node();
  23. layout
  24. .set_width_policy(SizePolicy::expands(1))
  25. .set_height_policy(SizePolicy {
  26. minimum: uih.theme().ui_font_size as usize,
  27. desired: (uih.theme().ui_font_size * 1.5) as usize,
  28. slack_weight: 0,
  29. });
  30. Self {
  31. layout,
  32. label: Label::new(uih),
  33. state: ButtonState::Idle,
  34. hook: None,
  35. }
  36. }
  37. pub fn set_label(&mut self, label: &str) {
  38. self.label.set_text(label);
  39. }
  40. pub fn set_hook(&mut self, to: Box<dyn Fn() -> Option<C::Msg>>) {
  41. self.hook = Some(to);
  42. }
  43. }
  44. impl<C: Component> LayoutNodeContainer for Button<C> {
  45. fn layout_node(&self) -> &LayoutNode {
  46. &self.layout
  47. }
  48. fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
  49. if ndx == 0 {
  50. Some(self.label.layout_node())
  51. } else {
  52. None
  53. }
  54. }
  55. fn layout_child_count(&self) -> usize {
  56. 1
  57. }
  58. }
  59. impl<C: Component> Widget<C> for Button<C> {
  60. fn poll(
  61. &mut self,
  62. uih: &mut UIHandle,
  63. input_state: Option<&crate::input::InputState>,
  64. ) -> Vec<<C as Component>::Msg> {
  65. let mut result: Vec<<C as Component>::Msg> = vec![];
  66. if let (Some(istate), Some(area)) = (input_state, self.layout.render_area()) {
  67. if area.contains_inclusive(istate.mouse.pos) {
  68. println!("cursor on button");
  69. if istate.mouse.buttons.active(MouseButton::Left) {
  70. self.state = ButtonState::Clicked;
  71. } else if istate.mouse.released().active(MouseButton::Left) {
  72. self.state = ButtonState::Idle;
  73. result.extend(self.hook.as_ref().map(|v| v()).flatten().into_iter());
  74. } else {
  75. self.state = ButtonState::Hovered;
  76. }
  77. } else {
  78. self.state = ButtonState::Idle;
  79. }
  80. }
  81. result.extend(self.label.poll(uih, input_state).into_iter());
  82. result
  83. }
  84. fn layout_node(&self) -> LayoutNodeAccess {
  85. LayoutNodeAccess::new(self)
  86. }
  87. fn layout_node_mut(&mut self) -> &mut LayoutNode {
  88. &mut self.layout
  89. }
  90. fn render(&self, theme: &crate::theme::Theme, target: &mut kahlo::RgbaBitmap) {
  91. let colour = match self.state {
  92. ButtonState::Idle => theme.background,
  93. ButtonState::Hovered => theme.panel,
  94. ButtonState::Clicked => theme.foreground,
  95. };
  96. target.fill_region(self.layout.render_area().unwrap(), colour);
  97. self.label.render(theme, target);
  98. }
  99. }