button.rs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. use kahlo::{math::PixelSideOffsets, prelude::*};
  2. use crate::{
  3. component::Component,
  4. input::MouseButton,
  5. layout::{
  6. HorizontalAlignment, LayoutNode, LayoutNodeAccess, LayoutNodeContainer, SizePolicy,
  7. VerticalAlignment,
  8. },
  9. ui::UIHandle,
  10. window::RenderFormat,
  11. };
  12. use super::{Label, Widget};
  13. #[derive(Clone, Copy, PartialEq)]
  14. enum ButtonState {
  15. Idle,
  16. Hovered,
  17. Clicked,
  18. }
  19. pub struct Button<C: Component> {
  20. layout: LayoutNode,
  21. label: Label<C>,
  22. state: ButtonState,
  23. hook: Option<Box<dyn Fn() -> Option<C::Msg>>>,
  24. }
  25. impl<C: Component> Button<C> {
  26. pub fn new(uih: &UIHandle) -> Self {
  27. let mut layout = uih.new_layout_node();
  28. layout
  29. .set_width_policy(SizePolicy::expanding(1))
  30. .set_height_policy(SizePolicy {
  31. minimum: uih.theme().ui_font_size as usize,
  32. desired: (uih.theme().ui_font_size * 1.5) as usize,
  33. slack_weight: 0,
  34. })
  35. .set_valign(VerticalAlignment::Centre)
  36. .set_halign(HorizontalAlignment::Centre);
  37. *layout.margin_mut() = PixelSideOffsets::new_all_same(uih.theme().border_width as i32);
  38. Self {
  39. layout,
  40. label: Label::new(uih),
  41. state: ButtonState::Idle,
  42. hook: None,
  43. }
  44. }
  45. pub fn new_with_label(uih: &UIHandle, label: &str) -> Self {
  46. let mut r = Self::new(uih);
  47. r.set_label(label);
  48. r
  49. }
  50. pub fn set_label(&mut self, label: &str) {
  51. self.label.set_text(label);
  52. }
  53. pub fn set_hook(&mut self, to: Box<dyn Fn() -> Option<C::Msg>>) {
  54. self.hook = Some(to);
  55. }
  56. }
  57. impl<C: Component> LayoutNodeContainer for Button<C> {
  58. fn layout_node(&self) -> &LayoutNode {
  59. &self.layout
  60. }
  61. fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
  62. if ndx == 0 {
  63. Some(self.label.layout_node())
  64. } else {
  65. None
  66. }
  67. }
  68. fn layout_child_count(&self) -> usize {
  69. 1
  70. }
  71. }
  72. impl<C: Component> Widget<C> for Button<C> {
  73. fn poll(
  74. &mut self,
  75. uih: &mut UIHandle,
  76. input_state: Option<&crate::input::InputState>,
  77. ) -> Vec<<C as Component>::Msg> {
  78. let mut result: Vec<<C as Component>::Msg> = vec![];
  79. if let (Some(istate), Some(area)) = (input_state, self.layout.render_area()) {
  80. let before = self.state;
  81. if area.contains_inclusive(istate.mouse.pos) {
  82. if istate.mouse.buttons.active(MouseButton::Left) {
  83. self.state = ButtonState::Clicked;
  84. } else if istate.mouse.released().active(MouseButton::Left) {
  85. self.state = ButtonState::Idle;
  86. result.extend(self.hook.as_ref().and_then(|v| v()));
  87. } else {
  88. self.state = ButtonState::Hovered;
  89. }
  90. } else {
  91. self.state = ButtonState::Idle;
  92. }
  93. if self.state != before {
  94. self.layout.render_needed();
  95. }
  96. }
  97. result.extend(self.label.poll(uih, input_state));
  98. result
  99. }
  100. fn layout_node(&self) -> LayoutNodeAccess {
  101. LayoutNodeAccess::new(self)
  102. }
  103. fn layout_node_mut(&mut self) -> &mut LayoutNode {
  104. &mut self.layout
  105. }
  106. fn render(&self, theme: &crate::theme::Theme, target: &mut kahlo::BitmapMut<RenderFormat>) {
  107. let colour = match self.state {
  108. ButtonState::Idle => theme.background,
  109. ButtonState::Hovered => theme.panel,
  110. ButtonState::Clicked => theme.active,
  111. };
  112. if self.layout.render_check() {
  113. target.fill_region(self.layout.render_area().unwrap(), colour);
  114. target.rectangle(
  115. self.layout.render_area().unwrap(),
  116. theme.border_width,
  117. theme.border,
  118. );
  119. }
  120. self.label.render(theme, target);
  121. }
  122. }