1use documented::Documented;
2use gpui::{
3 AnyElement, AnyView, ClickEvent, CursorStyle, DefiniteLength, FocusHandle, Hsla, MouseButton,
4 MouseClickEvent, MouseDownEvent, MouseUpEvent, Rems, StyleRefinement, relative,
5 transparent_black, white,
6};
7use smallvec::SmallVec;
8
9use crate::{DynamicSpacing, ElevationIndex, prelude::*};
10
11pub trait SelectableButton: Toggleable {
13 fn selected_style(self, style: ButtonStyle) -> Self;
14}
15
16pub trait ButtonCommon: Clickable + Disableable {
18 fn id(&self) -> &ElementId;
20
21 fn style(self, style: ButtonStyle) -> Self;
26
27 fn size(self, size: ButtonSize) -> Self;
34
35 fn tooltip(self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self;
40
41 fn tab_index(self, tab_index: impl Into<isize>) -> Self;
42
43 fn layer(self, elevation: ElevationIndex) -> Self;
44
45 fn track_focus(self, focus_handle: &FocusHandle) -> Self;
46}
47
48#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
49pub enum IconPosition {
50 #[default]
51 Start,
52 End,
53}
54
55#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
56pub enum KeybindingPosition {
57 Start,
58 #[default]
59 End,
60}
61
62#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
63pub enum TintColor {
64 #[default]
65 Accent,
66 Error,
67 Warning,
68 Success,
69}
70
71impl TintColor {
72 fn solid_bg(self) -> Hsla {
74 match self {
75 TintColor::Accent => palette::primary(600),
76 TintColor::Error => palette::danger(600),
77 TintColor::Warning => palette::warning(600),
78 TintColor::Success => palette::success(600),
79 }
80 }
81
82 fn solid_bg_hovered(self) -> Hsla {
84 match self {
85 TintColor::Accent => palette::primary(700),
86 TintColor::Error => palette::danger(700),
87 TintColor::Warning => palette::warning(700),
88 TintColor::Success => palette::success(700),
89 }
90 }
91
92 fn button_like_style(self, _cx: &mut App) -> ButtonLikeStyles {
98 let bg = self.solid_bg();
99 ButtonLikeStyles {
100 background: bg,
101 border_color: bg,
102 label_color: white(),
103 icon_color: white(),
104 }
105 }
106}
107
108impl From<TintColor> for Color {
109 fn from(tint: TintColor) -> Self {
110 match tint {
111 TintColor::Accent => Color::Accent,
112 TintColor::Error => Color::Error,
113 TintColor::Warning => Color::Warning,
114 TintColor::Success => Color::Success,
115 }
116 }
117}
118
119impl From<ButtonStyle> for Color {
121 fn from(style: ButtonStyle) -> Self {
122 match style {
123 ButtonStyle::Tinted(tint) => tint.into(),
124 _ => Color::Default,
125 }
126 }
127}
128
129#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
131pub enum ButtonStyle {
132 Filled,
135
136 Tinted(TintColor),
139
140 Outlined,
143
144 OutlinedGhost,
146
147 OutlinedCustom(Hsla),
149
150 #[default]
153 Subtle,
154
155 Transparent,
159}
160
161#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
163pub(crate) struct ButtonLikeRounding {
164 pub top_left: bool,
166 pub top_right: bool,
168 pub bottom_right: bool,
170 pub bottom_left: bool,
172}
173
174impl ButtonLikeRounding {
175 pub const ALL: Self = Self {
176 top_left: true,
177 top_right: true,
178 bottom_right: true,
179 bottom_left: true,
180 };
181 pub const LEFT: Self = Self {
182 top_left: true,
183 top_right: false,
184 bottom_right: false,
185 bottom_left: true,
186 };
187 pub const RIGHT: Self = Self {
188 top_left: false,
189 top_right: true,
190 bottom_right: true,
191 bottom_left: false,
192 };
193}
194
195#[derive(Debug, Clone)]
196pub(crate) struct ButtonLikeStyles {
197 pub background: Hsla,
198 #[allow(unused)]
199 pub border_color: Hsla,
200 #[allow(unused)]
201 pub label_color: Hsla,
202 #[allow(unused)]
203 pub icon_color: Hsla,
204}
205
206fn element_bg_from_elevation(elevation: Option<ElevationIndex>, cx: &mut App) -> Hsla {
207 match elevation {
208 Some(ElevationIndex::Background) => cx.theme().colors().element_background,
209 Some(ElevationIndex::ElevatedSurface) => cx.theme().colors().elevated_surface_background,
210 Some(ElevationIndex::Surface) => cx.theme().colors().surface_background,
211 Some(ElevationIndex::ModalSurface) => cx.theme().colors().background,
212 _ => cx.theme().colors().element_background,
213 }
214}
215
216impl ButtonStyle {
217 pub(crate) fn enabled(
218 self,
219 elevation: Option<ElevationIndex>,
220 cx: &mut App,
221 ) -> ButtonLikeStyles {
222 match self {
223 ButtonStyle::Filled => ButtonLikeStyles {
224 background: element_bg_from_elevation(elevation, cx),
225 border_color: transparent_black(),
226 label_color: Color::Default.color(cx),
227 icon_color: Color::Default.color(cx),
228 },
229 ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
230 ButtonStyle::Outlined => ButtonLikeStyles {
231 background: element_bg_from_elevation(elevation, cx),
232 border_color: cx.theme().colors().border_variant,
233 label_color: Color::Default.color(cx),
234 icon_color: Color::Default.color(cx),
235 },
236 ButtonStyle::OutlinedGhost => ButtonLikeStyles {
237 background: transparent_black(),
238 border_color: cx.theme().colors().border_variant,
239 label_color: Color::Default.color(cx),
240 icon_color: Color::Default.color(cx),
241 },
242 ButtonStyle::OutlinedCustom(border_color) => ButtonLikeStyles {
243 background: transparent_black(),
244 border_color,
245 label_color: Color::Default.color(cx),
246 icon_color: Color::Default.color(cx),
247 },
248 ButtonStyle::Subtle => ButtonLikeStyles {
249 background: cx.theme().colors().ghost_element_background,
250 border_color: transparent_black(),
251 label_color: Color::Default.color(cx),
252 icon_color: Color::Default.color(cx),
253 },
254 ButtonStyle::Transparent => ButtonLikeStyles {
255 background: transparent_black(),
256 border_color: transparent_black(),
257 label_color: Color::Default.color(cx),
258 icon_color: Color::Default.color(cx),
259 },
260 }
261 }
262
263 pub(crate) fn hovered(
264 self,
265 elevation: Option<ElevationIndex>,
266 cx: &mut App,
267 ) -> ButtonLikeStyles {
268 match self {
269 ButtonStyle::Filled => {
270 let mut filled_background = element_bg_from_elevation(elevation, cx);
271 filled_background.fade_out(0.5);
272
273 ButtonLikeStyles {
274 background: filled_background,
275 border_color: transparent_black(),
276 label_color: Color::Default.color(cx),
277 icon_color: Color::Default.color(cx),
278 }
279 }
280 ButtonStyle::Tinted(tint) => {
281 let bg = tint.solid_bg_hovered();
282 ButtonLikeStyles {
283 background: bg,
284 border_color: bg,
285 label_color: white(),
286 icon_color: white(),
287 }
288 }
289 ButtonStyle::Outlined => ButtonLikeStyles {
290 background: cx.theme().colors().ghost_element_hover,
291 border_color: cx.theme().colors().border,
292 label_color: Color::Default.color(cx),
293 icon_color: Color::Default.color(cx),
294 },
295 ButtonStyle::OutlinedGhost => ButtonLikeStyles {
296 background: cx.theme().colors().ghost_element_hover,
297 border_color: cx.theme().colors().border,
298 label_color: Color::Default.color(cx),
299 icon_color: Color::Default.color(cx),
300 },
301 ButtonStyle::OutlinedCustom(border_color) => ButtonLikeStyles {
302 background: cx.theme().colors().ghost_element_hover,
303 border_color,
304 label_color: Color::Default.color(cx),
305 icon_color: Color::Default.color(cx),
306 },
307 ButtonStyle::Subtle => ButtonLikeStyles {
308 background: cx.theme().colors().ghost_element_hover,
309 border_color: transparent_black(),
310 label_color: Color::Default.color(cx),
311 icon_color: Color::Default.color(cx),
312 },
313 ButtonStyle::Transparent => ButtonLikeStyles {
314 background: transparent_black(),
315 border_color: transparent_black(),
316 label_color: Color::Muted.color(cx),
318 icon_color: Color::Muted.color(cx),
320 },
321 }
322 }
323
324 pub(crate) fn active(self, cx: &mut App) -> ButtonLikeStyles {
325 match self {
326 ButtonStyle::Filled => ButtonLikeStyles {
327 background: cx.theme().colors().element_active,
328 border_color: transparent_black(),
329 label_color: Color::Default.color(cx),
330 icon_color: Color::Default.color(cx),
331 },
332 ButtonStyle::Tinted(tint) => {
333 let bg = tint.solid_bg_hovered();
334 ButtonLikeStyles {
335 background: bg,
336 border_color: bg,
337 label_color: white(),
338 icon_color: white(),
339 }
340 }
341 ButtonStyle::Subtle => ButtonLikeStyles {
342 background: cx.theme().colors().ghost_element_active,
343 border_color: transparent_black(),
344 label_color: Color::Default.color(cx),
345 icon_color: Color::Default.color(cx),
346 },
347 ButtonStyle::Outlined => ButtonLikeStyles {
348 background: cx.theme().colors().element_active,
349 border_color: cx.theme().colors().border_variant,
350 label_color: Color::Default.color(cx),
351 icon_color: Color::Default.color(cx),
352 },
353 ButtonStyle::OutlinedGhost => ButtonLikeStyles {
354 background: transparent_black(),
355 border_color: cx.theme().colors().border_variant,
356 label_color: Color::Default.color(cx),
357 icon_color: Color::Default.color(cx),
358 },
359 ButtonStyle::OutlinedCustom(border_color) => ButtonLikeStyles {
360 background: cx.theme().colors().element_active,
361 border_color,
362 label_color: Color::Default.color(cx),
363 icon_color: Color::Default.color(cx),
364 },
365 ButtonStyle::Transparent => ButtonLikeStyles {
366 background: transparent_black(),
367 border_color: transparent_black(),
368 label_color: Color::Muted.color(cx),
370 icon_color: Color::Muted.color(cx),
372 },
373 }
374 }
375
376 #[allow(unused)]
377 pub(crate) fn focused(self, window: &mut Window, cx: &mut App) -> ButtonLikeStyles {
378 match self {
379 ButtonStyle::Filled => ButtonLikeStyles {
380 background: cx.theme().colors().element_background,
381 border_color: cx.theme().colors().border_focused,
382 label_color: Color::Default.color(cx),
383 icon_color: Color::Default.color(cx),
384 },
385 ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
386 ButtonStyle::Subtle => ButtonLikeStyles {
387 background: cx.theme().colors().ghost_element_background,
388 border_color: cx.theme().colors().border_focused,
389 label_color: Color::Default.color(cx),
390 icon_color: Color::Default.color(cx),
391 },
392 ButtonStyle::Outlined => ButtonLikeStyles {
393 background: cx.theme().colors().ghost_element_background,
394 border_color: cx.theme().colors().border,
395 label_color: Color::Default.color(cx),
396 icon_color: Color::Default.color(cx),
397 },
398 ButtonStyle::OutlinedGhost => ButtonLikeStyles {
399 background: transparent_black(),
400 border_color: cx.theme().colors().border,
401 label_color: Color::Default.color(cx),
402 icon_color: Color::Default.color(cx),
403 },
404 ButtonStyle::OutlinedCustom(border_color) => ButtonLikeStyles {
405 background: cx.theme().colors().ghost_element_background,
406 border_color,
407 label_color: Color::Default.color(cx),
408 icon_color: Color::Default.color(cx),
409 },
410 ButtonStyle::Transparent => ButtonLikeStyles {
411 background: transparent_black(),
412 border_color: cx.theme().colors().border_focused,
413 label_color: Color::Accent.color(cx),
414 icon_color: Color::Accent.color(cx),
415 },
416 }
417 }
418
419 #[allow(unused)]
420 pub(crate) fn disabled(
421 self,
422 elevation: Option<ElevationIndex>,
423 window: &mut Window,
424 cx: &mut App,
425 ) -> ButtonLikeStyles {
426 match self {
427 ButtonStyle::Filled => ButtonLikeStyles {
428 background: cx.theme().colors().element_disabled,
429 border_color: cx.theme().colors().border_disabled,
430 label_color: Color::Disabled.color(cx),
431 icon_color: Color::Disabled.color(cx),
432 },
433 ButtonStyle::Tinted(tint) => tint.button_like_style(cx),
434 ButtonStyle::Subtle => ButtonLikeStyles {
435 background: cx.theme().colors().ghost_element_disabled,
436 border_color: cx.theme().colors().border_disabled,
437 label_color: Color::Disabled.color(cx),
438 icon_color: Color::Disabled.color(cx),
439 },
440 ButtonStyle::Outlined => ButtonLikeStyles {
441 background: cx.theme().colors().element_disabled,
442 border_color: cx.theme().colors().border_disabled,
443 label_color: Color::Default.color(cx),
444 icon_color: Color::Default.color(cx),
445 },
446 ButtonStyle::OutlinedGhost => ButtonLikeStyles {
447 background: transparent_black(),
448 border_color: cx.theme().colors().border_disabled,
449 label_color: Color::Default.color(cx),
450 icon_color: Color::Default.color(cx),
451 },
452 ButtonStyle::OutlinedCustom(_) => ButtonLikeStyles {
453 background: cx.theme().colors().element_disabled,
454 border_color: cx.theme().colors().border_disabled,
455 label_color: Color::Default.color(cx),
456 icon_color: Color::Default.color(cx),
457 },
458 ButtonStyle::Transparent => ButtonLikeStyles {
459 background: transparent_black(),
460 border_color: transparent_black(),
461 label_color: Color::Disabled.color(cx),
462 icon_color: Color::Disabled.color(cx),
463 },
464 }
465 }
466}
467
468#[derive(Default, PartialEq, Clone, Copy)]
472pub enum ButtonSize {
473 Large,
474 Medium,
475 #[default]
476 Default,
477 Compact,
478 None,
479}
480
481impl ButtonSize {
482 pub fn rems(self) -> Rems {
483 match self {
484 ButtonSize::Large => rems_from_px(32.),
485 ButtonSize::Medium => rems_from_px(28.),
486 ButtonSize::Default => rems_from_px(22.),
487 ButtonSize::Compact => rems_from_px(18.),
488 ButtonSize::None => rems_from_px(16.),
489 }
490 }
491}
492
493#[derive(IntoElement, Documented, RegisterComponent)]
499pub struct ButtonLike {
500 pub(super) base: Div,
501 id: ElementId,
502 pub(super) style: ButtonStyle,
503 pub(super) disabled: bool,
504 pub(super) selected: bool,
505 pub(super) selected_style: Option<ButtonStyle>,
506 pub(super) width: Option<DefiniteLength>,
507 pub(super) height: Option<DefiniteLength>,
508 pub(super) layer: Option<ElevationIndex>,
509 tab_index: Option<isize>,
510 size: ButtonSize,
511 rounding: Option<ButtonLikeRounding>,
512 pub(super) background_override: Option<Hsla>,
516 tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
517 hoverable_tooltip: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyView>>,
518 cursor_style: CursorStyle,
519 on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
520 on_right_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
521 children: SmallVec<[AnyElement; 2]>,
522 focus_handle: Option<FocusHandle>,
523}
524
525impl ButtonLike {
526 pub fn new(id: impl Into<ElementId>) -> Self {
527 Self {
528 base: div(),
529 id: id.into(),
530 style: ButtonStyle::default(),
531 disabled: false,
532 selected: false,
533 selected_style: None,
534 width: None,
535 height: None,
536 size: ButtonSize::Default,
537 rounding: Some(ButtonLikeRounding::ALL),
538 background_override: None,
539 tooltip: None,
540 hoverable_tooltip: None,
541 children: SmallVec::new(),
542 cursor_style: CursorStyle::PointingHand,
543 on_click: None,
544 on_right_click: None,
545 layer: None,
546 tab_index: None,
547 focus_handle: None,
548 }
549 }
550
551 pub fn new_rounded_left(id: impl Into<ElementId>) -> Self {
552 Self::new(id).rounding(ButtonLikeRounding::LEFT)
553 }
554
555 pub fn new_rounded_right(id: impl Into<ElementId>) -> Self {
556 Self::new(id).rounding(ButtonLikeRounding::RIGHT)
557 }
558
559 pub fn new_rounded_all(id: impl Into<ElementId>) -> Self {
560 Self::new(id).rounding(ButtonLikeRounding::ALL)
561 }
562
563 pub fn opacity(mut self, opacity: f32) -> Self {
564 self.base = self.base.opacity(opacity);
565 self
566 }
567
568 pub fn height(mut self, height: DefiniteLength) -> Self {
569 self.height = Some(height);
570 self
571 }
572
573 pub(crate) fn rounding(mut self, rounding: impl Into<Option<ButtonLikeRounding>>) -> Self {
574 self.rounding = rounding.into();
575 self
576 }
577
578 pub(crate) fn background_override(mut self, color: impl Into<Option<Hsla>>) -> Self {
580 self.background_override = color.into();
581 self
582 }
583
584 pub fn on_right_click(
585 mut self,
586 handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
587 ) -> Self {
588 self.on_right_click = Some(Box::new(handler));
589 self
590 }
591
592 pub fn hoverable_tooltip(
593 mut self,
594 tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
595 ) -> Self {
596 self.hoverable_tooltip = Some(Box::new(tooltip));
597 self
598 }
599}
600
601impl Disableable for ButtonLike {
602 fn disabled(mut self, disabled: bool) -> Self {
603 self.disabled = disabled;
604 self
605 }
606}
607
608impl Toggleable for ButtonLike {
609 fn toggle_state(mut self, selected: bool) -> Self {
610 self.selected = selected;
611 self
612 }
613}
614
615impl SelectableButton for ButtonLike {
616 fn selected_style(mut self, style: ButtonStyle) -> Self {
617 self.selected_style = Some(style);
618 self
619 }
620}
621
622impl Clickable for ButtonLike {
623 fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
624 self.on_click = Some(Box::new(handler));
625 self
626 }
627
628 fn cursor_style(mut self, cursor_style: CursorStyle) -> Self {
629 self.cursor_style = cursor_style;
630 self
631 }
632}
633
634impl FixedWidth for ButtonLike {
635 fn width(mut self, width: impl Into<DefiniteLength>) -> Self {
636 self.width = Some(width.into());
637 self
638 }
639
640 fn full_width(mut self) -> Self {
641 self.width = Some(relative(1.));
642 self
643 }
644}
645
646impl ButtonCommon for ButtonLike {
647 fn id(&self) -> &ElementId {
648 &self.id
649 }
650
651 fn style(mut self, style: ButtonStyle) -> Self {
652 self.style = style;
653 self
654 }
655
656 fn size(mut self, size: ButtonSize) -> Self {
657 self.size = size;
658 self
659 }
660
661 fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
662 self.tooltip = Some(Box::new(tooltip));
663 self
664 }
665
666 fn tab_index(mut self, tab_index: impl Into<isize>) -> Self {
667 self.tab_index = Some(tab_index.into());
668 self
669 }
670
671 fn layer(mut self, elevation: ElevationIndex) -> Self {
672 self.layer = Some(elevation);
673 self
674 }
675
676 fn track_focus(mut self, focus_handle: &gpui::FocusHandle) -> Self {
677 self.focus_handle = Some(focus_handle.clone());
678 self
679 }
680}
681
682impl VisibleOnHover for ButtonLike {
683 fn visible_on_hover(mut self, group_name: impl Into<SharedString>) -> Self {
684 self.base = self.base.visible_on_hover(group_name);
685 self
686 }
687}
688
689impl ParentElement for ButtonLike {
690 fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
691 self.children.extend(elements)
692 }
693}
694
695impl RenderOnce for ButtonLike {
696 fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
697 let style = self
698 .selected_style
699 .filter(|_| self.selected)
700 .unwrap_or(self.style);
701
702 let is_outlined = matches!(
703 self.style,
704 ButtonStyle::Outlined | ButtonStyle::OutlinedGhost | ButtonStyle::OutlinedCustom(_)
705 );
706
707 self.base
708 .h_flex()
709 .id(self.id.clone())
710 .when_some(self.tab_index, |this, tab_index| this.tab_index(tab_index))
711 .when_some(self.focus_handle, |this, focus_handle| {
712 this.track_focus(&focus_handle)
713 })
714 .font_ui(cx)
715 .group("")
716 .flex_none()
717 .h(self.height.unwrap_or(self.size.rems().into()))
718 .when_some(self.width, |this, width| {
719 this.w(width).justify_center().text_center()
720 })
721 .when(is_outlined, |this| this.border_1())
722 .when_some(self.rounding, |this, rounding| {
723 this.when(rounding.top_left, |this| this.rounded_tl_sm())
724 .when(rounding.top_right, |this| this.rounded_tr_sm())
725 .when(rounding.bottom_right, |this| this.rounded_br_sm())
726 .when(rounding.bottom_left, |this| this.rounded_bl_sm())
727 })
728 .gap(DynamicSpacing::Base04.rems(cx))
729 .map(|this| match self.size {
730 ButtonSize::Large | ButtonSize::Medium => this.px(DynamicSpacing::Base08.rems(cx)),
731 ButtonSize::Default | ButtonSize::Compact => {
732 this.px(DynamicSpacing::Base04.rems(cx))
733 }
734 ButtonSize::None => this.px_px(),
735 })
736 .border_color(style.enabled(self.layer, cx).border_color)
737 .bg(style.enabled(self.layer, cx).background)
738 .when_some(self.background_override, |this, color| this.bg(color))
739 .when(self.disabled, |this| {
740 if self.cursor_style == CursorStyle::PointingHand {
741 this.cursor_not_allowed()
742 } else {
743 this.cursor(self.cursor_style)
744 }
745 })
746 .when(!self.disabled, |this| {
747 let hovered_style = style.hovered(self.layer, cx);
748 let hover_bg = self.background_override.unwrap_or(hovered_style.background);
753 let active_bg = self
754 .background_override
755 .unwrap_or(style.active(cx).background);
756 let focus_color = move |refinement: StyleRefinement| refinement.bg(hover_bg);
757
758 this.cursor(self.cursor_style)
759 .hover(focus_color)
760 .map(|this| {
761 if is_outlined {
762 this.focus_visible(|s| {
763 s.border_color(cx.theme().colors().border_focused)
764 })
765 } else {
766 this.focus_visible(focus_color)
767 }
768 })
769 .active(move |active| active.bg(active_bg))
770 })
771 .when_some(
772 self.on_right_click.filter(|_| !self.disabled),
773 |this, on_right_click| {
774 this.on_mouse_down(MouseButton::Right, |_event, window, cx| {
775 window.prevent_default();
776 cx.stop_propagation();
777 })
778 .on_mouse_up(
779 MouseButton::Right,
780 move |event, window, cx| {
781 cx.stop_propagation();
782 let click_event = ClickEvent::Mouse(MouseClickEvent {
783 down: MouseDownEvent {
784 button: MouseButton::Right,
785 position: event.position,
786 modifiers: event.modifiers,
787 click_count: 1,
788 first_mouse: false,
789 },
790 up: MouseUpEvent {
791 button: MouseButton::Right,
792 position: event.position,
793 modifiers: event.modifiers,
794 click_count: 1,
795 },
796 });
797 (on_right_click)(&click_event, window, cx)
798 },
799 )
800 },
801 )
802 .when_some(
803 self.on_click.filter(|_| !self.disabled),
804 |this, on_click| {
805 this.on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
806 .on_click(move |event, window, cx| {
807 cx.stop_propagation();
808 (on_click)(event, window, cx)
809 })
810 },
811 )
812 .when_some(self.tooltip, |this, tooltip| {
813 this.tooltip(move |window, cx| tooltip(window, cx))
814 })
815 .when_some(self.hoverable_tooltip, |this, tooltip| {
816 this.hoverable_tooltip(move |window, cx| tooltip(window, cx))
817 })
818 .children(self.children)
819 }
820}
821
822impl Component for ButtonLike {
823 fn scope() -> ComponentScope {
824 ComponentScope::Input
825 }
826
827 fn sort_name() -> &'static str {
828 "ButtonZ"
830 }
831
832 fn description() -> Option<&'static str> {
833 Some(ButtonLike::DOCS)
834 }
835
836 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
837 Some(
838 v_flex()
839 .gap_6()
840 .children(vec![
841 example_group(vec![
842 single_example(
843 "Default",
844 ButtonLike::new("default")
845 .child(Label::new("Default"))
846 .into_any_element(),
847 ),
848 single_example(
849 "Filled",
850 ButtonLike::new("filled")
851 .style(ButtonStyle::Filled)
852 .child(Label::new("Filled"))
853 .into_any_element(),
854 ),
855 single_example(
856 "Subtle",
857 ButtonLike::new("outline")
858 .style(ButtonStyle::Subtle)
859 .child(Label::new("Subtle"))
860 .into_any_element(),
861 ),
862 single_example(
863 "Tinted",
864 ButtonLike::new("tinted_accent_style")
865 .style(ButtonStyle::Tinted(TintColor::Accent))
866 .child(Label::new("Accent"))
867 .into_any_element(),
868 ),
869 single_example(
870 "Transparent",
871 ButtonLike::new("transparent")
872 .style(ButtonStyle::Transparent)
873 .child(Label::new("Transparent"))
874 .into_any_element(),
875 ),
876 ]),
877 example_group_with_title(
878 "Button Group Constructors",
879 vec![
880 single_example(
881 "Left Rounded",
882 ButtonLike::new_rounded_left("left_rounded")
883 .child(Label::new("Left Rounded"))
884 .style(ButtonStyle::Filled)
885 .into_any_element(),
886 ),
887 single_example(
888 "Right Rounded",
889 ButtonLike::new_rounded_right("right_rounded")
890 .child(Label::new("Right Rounded"))
891 .style(ButtonStyle::Filled)
892 .into_any_element(),
893 ),
894 single_example(
895 "Button Group",
896 h_flex()
897 .gap_px()
898 .child(
899 ButtonLike::new_rounded_left("bg_left")
900 .child(Label::new("Left"))
901 .style(ButtonStyle::Filled),
902 )
903 .child(
904 ButtonLike::new_rounded_right("bg_right")
905 .child(Label::new("Right"))
906 .style(ButtonStyle::Filled),
907 )
908 .into_any_element(),
909 ),
910 ],
911 ),
912 ])
913 .into_any_element(),
914 )
915 }
916}