add tooltip
This commit is contained in:
@@ -28,6 +28,7 @@ full = [
|
|||||||
"input",
|
"input",
|
||||||
"popover",
|
"popover",
|
||||||
"scroll_area",
|
"scroll_area",
|
||||||
|
"tooltip",
|
||||||
"menu",
|
"menu",
|
||||||
"dialog",
|
"dialog",
|
||||||
"sheet",
|
"sheet",
|
||||||
|
|||||||
@@ -2,17 +2,20 @@
|
|||||||
|
|
||||||
use gpui::prelude::FluentBuilder as _;
|
use gpui::prelude::FluentBuilder as _;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
App, BoxShadow, ClickEvent, ElementId, InteractiveElement as _, IntoElement,
|
AnyView, App, BoxShadow, ClickEvent, ElementId, InteractiveElement as _, IntoElement,
|
||||||
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
|
ParentElement as _, RenderOnce, Role, SharedString, StatefulInteractiveElement as _,
|
||||||
Styled as _, Window, px, relative,
|
Styled as _, Window, px, relative,
|
||||||
};
|
};
|
||||||
use gpui_native_icons::{IconRole, icon};
|
use gpui_native_icons::{IconRole, icon};
|
||||||
use gpui_native_theme::{ActiveTheme as _, Size};
|
use gpui_native_theme::{ActiveTheme as _, Size};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::common::{Sizable, h_flex};
|
use crate::common::{Sizable, h_flex};
|
||||||
|
|
||||||
/// The single activation handler, shared by pointer, Enter, and Space.
|
/// The single activation handler, shared by pointer, Enter, and Space.
|
||||||
type ClickHandler = Box<dyn Fn(&ClickEvent, &mut Window, &mut App)>;
|
type ClickHandler = Box<dyn Fn(&ClickEvent, &mut Window, &mut App)>;
|
||||||
|
/// The tooltip view builder gpui's `.tooltip(...)` expects.
|
||||||
|
type TooltipBuilder = Rc<dyn Fn(&mut Window, &mut App) -> AnyView>;
|
||||||
|
|
||||||
/// Which visual treatment a button uses.
|
/// Which visual treatment a button uses.
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
@@ -38,6 +41,7 @@ pub struct Button {
|
|||||||
size: Size,
|
size: Size,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
a11y_label: Option<SharedString>,
|
a11y_label: Option<SharedString>,
|
||||||
|
tooltip: Option<TooltipBuilder>,
|
||||||
on_click: Option<ClickHandler>,
|
on_click: Option<ClickHandler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +59,7 @@ impl Button {
|
|||||||
size: Size::default(),
|
size: Size::default(),
|
||||||
disabled: false,
|
disabled: false,
|
||||||
a11y_label: None,
|
a11y_label: None,
|
||||||
|
tooltip: None,
|
||||||
on_click: None,
|
on_click: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,6 +79,15 @@ impl Button {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shows a tooltip view next to the button after it is hovered.
|
||||||
|
pub fn tooltip<F>(mut self, tooltip: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn(&mut Window, &mut App) -> AnyView + 'static,
|
||||||
|
{
|
||||||
|
self.tooltip = Some(Rc::new(tooltip));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the visual treatment.
|
/// Sets the visual treatment.
|
||||||
pub fn variant(mut self, variant: ButtonVariant) -> Self {
|
pub fn variant(mut self, variant: ButtonVariant) -> Self {
|
||||||
self.variant = variant;
|
self.variant = variant;
|
||||||
@@ -173,6 +187,9 @@ impl RenderOnce for Button {
|
|||||||
})
|
})
|
||||||
.when_some(self.label, |button, label| button.child(label))
|
.when_some(self.label, |button, label| button.child(label))
|
||||||
.when(self.disabled, |button| button.opacity(0.5))
|
.when(self.disabled, |button| button.opacity(0.5))
|
||||||
|
.when_some(self.tooltip, |button, tooltip| {
|
||||||
|
button.tooltip(move |window, cx| tooltip(window, cx))
|
||||||
|
})
|
||||||
.when(!self.disabled, |button| {
|
.when(!self.disabled, |button| {
|
||||||
button
|
button
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ pub mod popover;
|
|||||||
#[cfg(feature = "scroll_area")]
|
#[cfg(feature = "scroll_area")]
|
||||||
pub mod scroll_area;
|
pub mod scroll_area;
|
||||||
|
|
||||||
|
#[cfg(feature = "tooltip")]
|
||||||
|
pub mod tooltip;
|
||||||
|
|
||||||
/// Initializes everything the components need at startup.
|
/// Initializes everything the components need at startup.
|
||||||
pub fn init(cx: &mut App) {
|
pub fn init(cx: &mut App) {
|
||||||
gpui_native_theme::Theme::init(cx);
|
gpui_native_theme::Theme::init(cx);
|
||||||
|
|||||||
+1
-1
@@ -212,7 +212,7 @@ Each row is one review unit: build it, show it in the gallery, review, then move
|
|||||||
| 11.5 | core | ScrollArea + Scrollbar | ✅ Landed (2026-10-29). Inserted before the remaining overlays — Menus, Lists and Tables all scroll. gpui owns the mechanics (`overflow_*_scroll` + caller-held `ScrollHandle`); the component paints an overlay thumb (`scrollbar_thumb` token, `Scrollbar` metrics) with drag and geometry unit-tested. Wheel input is gpui's; the area stops wheel propagation so nested areas don't co-scroll. Horizontal thumb deferred; auto-hide on idle deferred |
|
| 11.5 | core | ScrollArea + Scrollbar | ✅ Landed (2026-10-29). Inserted before the remaining overlays — Menus, Lists and Tables all scroll. gpui owns the mechanics (`overflow_*_scroll` + caller-held `ScrollHandle`); the component paints an overlay thumb (`scrollbar_thumb` token, `Scrollbar` metrics) with drag and geometry unit-tested. Wheel input is gpui's; the area stops wheel propagation so nested areas don't co-scroll. Horizontal thumb deferred; auto-hide on idle deferred |
|
||||||
| 12 | core | Menu | ✅ Landed (2026-10-29). Controlled `open`/`on_change` + `trigger`/`items` (MenuItem: label, icon, shortcut hint, disabled, destructive, checked, separator). Popover shell patterns duplicated per D16 (promote at Dialog); panel = surface + overlay radius + popover elevation, role Menu; claims focus while open; keyboard nav — arrows/home/end over selectable items (wrapping, skipping separators + disabled), enter/space activates (closes menu before the action), escape dismisses; hover selects, click activates; trigger clicks toggle (popover-owned); selection resets on each open; long menus scroll via the ScrollArea once a height is styled. Deferred: submenus, type-ahead, scroll-selected-item-into-view, real shortcut handling |
|
| 12 | core | Menu | ✅ Landed (2026-10-29). Controlled `open`/`on_change` + `trigger`/`items` (MenuItem: label, icon, shortcut hint, disabled, destructive, checked, separator). Popover shell patterns duplicated per D16 (promote at Dialog); panel = surface + overlay radius + popover elevation, role Menu; claims focus while open; keyboard nav — arrows/home/end over selectable items (wrapping, skipping separators + disabled), enter/space activates (closes menu before the action), escape dismisses; hover selects, click activates; trigger clicks toggle (popover-owned); selection resets on each open; long menus scroll via the ScrollArea once a height is styled. Deferred: submenus, type-ahead, scroll-selected-item-into-view, real shortcut handling |
|
||||||
| 11 | core | Popover | ✅ Landed (2026-10-29). First overlay. Controlled `open` + `on_change` (Rc-shared handler); `trigger`/`content` element closures; panel = surface + `Radius.overlay` + `Elevations.popover` + border, role Dialog with label; `anchored()` + `deferred()` (window-overflow flip for free); enter fade via gpui `with_animation` at `Motion.overlay` (respects reduce-motion); claims keyboard focus while open; escape and click-outside dismiss (outside clicks swallowed). No scrim — native macOS popover model; the overlay token waits for Dialog. Known v1 gaps: no exit animation, trigger aria-expanded left to the app |
|
| 11 | core | Popover | ✅ Landed (2026-10-29). First overlay. Controlled `open` + `on_change` (Rc-shared handler); `trigger`/`content` element closures; panel = surface + `Radius.overlay` + `Elevations.popover` + border, role Dialog with label; `anchored()` + `deferred()` (window-overflow flip for free); enter fade via gpui `with_animation` at `Motion.overlay` (respects reduce-motion); claims keyboard focus while open; escape and click-outside dismiss (outside clicks swallowed). No scrim — native macOS popover model; the overlay token waits for Dialog. Known v1 gaps: no exit animation, trigger aria-expanded left to the app |
|
||||||
| 11+ | core | Tooltip, Menu, Dialog, AlertDialog, Sheet, Toast | One unit each, same gate. Menu reuses the popover shell + `Radius.overlay`/`Elevations.popover`; Dialog consumes `colors.overlay` (scrim) + `Elevations.dialog` |
|
| 11+ | core | Menu ✅ (2026-10-29), Tooltip ✅ (2026-10-29), Dialog, AlertDialog, Sheet, Toast | One unit each, same gate. Menu reuses the popover shell + `Radius.overlay`/`Elevations.popover`; Dialog consumes `colors.overlay` (scrim) + `Elevations.dialog`. Tooltip rides gpui's own `.tooltip()` lifecycle (hover delay, anchoring, dismissal — all framework-owned); ours is the styled view (`surface` + border + `Radius.sm`, small text) plus a `Tooltip::text` closure and a Button passthrough |
|
||||||
| then | data (new) | List, Table, Tree, Tabs, Sidebar | Created on demand, same gate. |
|
| then | data (new) | List, Table, Tree, Tabs, Sidebar | Created on demand, same gate. |
|
||||||
| then | pickers or data | Calendar/DatePicker, TimeField, ColorPicker | Decide at unit time. |
|
| then | pickers or data | Calendar/DatePicker, TimeField, ColorPicker | Decide at unit time. |
|
||||||
| editor | editor (new) | Text/code editor, syntax highlighting (D12) | Same gate. |
|
| editor | editor (new) | Text/code editor, syntax highlighting (D12) | Same gate. |
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use gpui_native_core::scroll_area::ScrollArea;
|
|||||||
use gpui_native_core::slider::Slider;
|
use gpui_native_core::slider::Slider;
|
||||||
use gpui_native_core::switch::Switch;
|
use gpui_native_core::switch::Switch;
|
||||||
use gpui_native_core::toggle::Toggle;
|
use gpui_native_core::toggle::Toggle;
|
||||||
|
use gpui_native_core::tooltip::Tooltip;
|
||||||
use gpui_native_icons::{IconAssets, IconRole, icon};
|
use gpui_native_icons::{IconAssets, IconRole, icon};
|
||||||
use gpui_native_theme::{ActiveTheme as _, Theme};
|
use gpui_native_theme::{ActiveTheme as _, Theme};
|
||||||
use gpui_platform::application;
|
use gpui_platform::application;
|
||||||
@@ -190,7 +191,9 @@ impl Render for Gallery {
|
|||||||
.gap_2()
|
.gap_2()
|
||||||
.children(button_variants.map(|(name, variant)| {
|
.children(button_variants.map(|(name, variant)| {
|
||||||
let gallery = gallery.clone();
|
let gallery = gallery.clone();
|
||||||
Button::new(SharedString::from(format!("gallery-button-{name}")))
|
let mut button = Button::new(SharedString::from(format!(
|
||||||
|
"gallery-button-{name}"
|
||||||
|
)))
|
||||||
.label(button_label(variant))
|
.label(button_label(variant))
|
||||||
.variant(variant)
|
.variant(variant)
|
||||||
.on_click(move |_, _, cx| {
|
.on_click(move |_, _, cx| {
|
||||||
@@ -198,7 +201,12 @@ impl Render for Gallery {
|
|||||||
gallery.button_clicks += 1;
|
gallery.button_clicks += 1;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
if variant == ButtonVariant::Destructive {
|
||||||
|
button =
|
||||||
|
button.tooltip(Tooltip::text("Removes the selected item"));
|
||||||
|
}
|
||||||
|
button
|
||||||
}))
|
}))
|
||||||
.child(
|
.child(
|
||||||
Button::new("gallery-button-disabled")
|
Button::new("gallery-button-disabled")
|
||||||
@@ -208,7 +216,8 @@ impl Render for Gallery {
|
|||||||
.child(
|
.child(
|
||||||
Button::new("gallery-button-icon")
|
Button::new("gallery-button-icon")
|
||||||
.label("Open")
|
.label("Open")
|
||||||
.icon(IconRole::Folder),
|
.icon(IconRole::Folder)
|
||||||
|
.tooltip(Tooltip::text("Opens the selected file")),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
|
|||||||
Reference in New Issue
Block a user