Compare commits

...
Sign in to create a new pull request.

10 commits

Author SHA1 Message Date
Lukas Wirth
e56672e542 impl Focusable for Button 2025-08-13 16:41:56 +02:00
Lukas Wirth
6e540a58fa Make tab cycling behave symmetrical in search buffer 2025-08-13 14:57:28 +02:00
Lukas Wirth
9b6b7f1412 Remove style differences between project and buffer search 2025-08-13 13:36:12 +02:00
Lukas Wirth
3cda09f875 More cleanups 2025-08-13 13:36:12 +02:00
Lukas Wirth
e2ab26ef41 Cleanup render_action_button some more 2025-08-13 12:08:07 +02:00
Lukas Wirth
88daa45537 Rearrange for clarity 2025-08-13 11:56:49 +02:00
Lukas Wirth
036bc75799 Deduplicate replace button code in search bars 2025-08-13 11:47:33 +02:00
Lukas Wirth
ccd5fc20bd Deduplicate prev/next matches button code in search bars 2025-08-13 11:41:00 +02:00
Lukas Wirth
bce501c696 Render query text red in project search if no results are found 2025-08-13 11:23:57 +02:00
Lukas Wirth
e354159f77 Fix replace button in project-search not rendering as filled when active 2025-08-13 10:38:36 +02:00
110 changed files with 1015 additions and 1067 deletions

View file

@ -1415,7 +1415,7 @@ impl AcpThreadView {
.text_color(cx.theme().colors().text_muted)
.child(self.render_markdown(markdown, default_markdown_style(false, window, cx)))
.child(
Button::new(button_id, "Collapse Output")
Button::new(button_id, "Collapse Output", cx)
.full_width()
.style(ButtonStyle::Outlined)
.label_size(LabelSize::Small)
@ -1455,7 +1455,7 @@ impl AcpThreadView {
.border_color(self.tool_card_border_color(cx))
.overflow_hidden()
.child(
Button::new(button_id, label)
Button::new(button_id, label, cx)
.label_size(LabelSize::Small)
.color(Color::Muted)
.icon(IconName::ArrowUpRight)
@ -1498,7 +1498,7 @@ impl AcpThreadView {
)
.child(h_flex().gap_0p5().children(options.iter().map(|option| {
let option_id = SharedString::from(option.id.0.clone());
Button::new((option_id, entry_ix), option.name.clone())
Button::new((option_id, entry_ix), option.name.clone(), cx)
.map(|this| match option.kind {
acp::PermissionOptionKind::AllowOnce => {
this.icon(IconName::Check).icon_color(Color::Success)
@ -1628,6 +1628,7 @@ impl AcpThreadView {
Button::new(
SharedString::from(format!("stop-terminal-{}", terminal.entity_id())),
"Stop",
cx,
)
.icon(IconName::Stop)
.icon_position(IconPosition::Start)
@ -1932,7 +1933,7 @@ impl AcpThreadView {
{
let upgrade_message = upgrade_message.clone();
let upgrade_command = upgrade_command.clone();
container = container.child(Button::new("upgrade", upgrade_message).on_click(
container = container.child(Button::new("upgrade", upgrade_message, cx).on_click(
cx.listener(move |this, _, window, cx| {
this.workspace
.update(cx, |workspace, cx| {
@ -2264,7 +2265,7 @@ impl AcpThreadView {
)
.child(Divider::vertical().color(DividerColor::Border))
.child(
Button::new("reject-all-changes", "Reject All")
Button::new("reject-all-changes", "Reject All", cx)
.label_size(LabelSize::Small)
.disabled(pending_edits)
.when(pending_edits, |this| {
@ -2289,7 +2290,7 @@ impl AcpThreadView {
}),
)
.child(
Button::new("keep-all-changes", "Keep All")
Button::new("keep-all-changes", "Keep All", cx)
.label_size(LabelSize::Small)
.disabled(pending_edits)
.when(pending_edits, |this| {
@ -2395,7 +2396,7 @@ impl AcpThreadView {
.gap_1()
.visible_on_hover("edited-code")
.child(
Button::new("review", "Review")
Button::new("review", "Review", cx)
.label_size(LabelSize::Small)
.on_click({
let buffer = buffer.clone();
@ -2406,7 +2407,7 @@ impl AcpThreadView {
)
.child(Divider::vertical().color(DividerColor::BorderVariant))
.child(
Button::new("reject-file", "Reject")
Button::new("reject-file", "Reject", cx)
.label_size(LabelSize::Small)
.disabled(pending_edits)
.on_click({
@ -2426,7 +2427,7 @@ impl AcpThreadView {
}),
)
.child(
Button::new("keep-file", "Keep")
Button::new("keep-file", "Keep", cx)
.label_size(LabelSize::Small)
.disabled(pending_edits)
.on_click({
@ -3114,6 +3115,7 @@ impl Render for AcpThreadView {
Button::new(
SharedString::from(method.id.0.clone()),
method.name.clone(),
cx,
)
.on_click({
let method_id = method.id.clone();

View file

@ -2281,7 +2281,7 @@ impl ActiveThread {
}
let restore_checkpoint_button =
Button::new(("restore-checkpoint", ix), "Restore Checkpoint")
Button::new(("restore-checkpoint", ix), "Restore Checkpoint", cx)
.icon(if error.is_some() {
IconName::XCircle
} else {
@ -2371,7 +2371,7 @@ impl ActiveThread {
.gap_1()
.justify_end()
.child(
Button::new("dismiss-feedback-message", "Cancel")
Button::new("dismiss-feedback-message", "Cancel", cx)
.label_size(LabelSize::Small)
.key_binding(
KeyBinding::for_action_in(
@ -2394,6 +2394,7 @@ impl ActiveThread {
Button::new(
"submit-feedback-message",
"Share Feedback",
cx,
)
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.label_size(LabelSize::Small)
@ -3217,6 +3218,7 @@ impl ActiveThread {
Button::new(
"always-allow-tool-action",
"Always Allow",
cx,
)
.label_size(LabelSize::Small)
.icon(IconName::CheckDouble)
@ -3254,7 +3256,7 @@ impl ActiveThread {
})
.child({
let tool_id = tool_use.id.clone();
Button::new("allow-tool-action", "Allow")
Button::new("allow-tool-action", "Allow", cx)
.label_size(LabelSize::Small)
.icon(IconName::Check)
.icon_position(IconPosition::Start)
@ -3274,7 +3276,7 @@ impl ActiveThread {
.child({
let tool_id = tool_use.id.clone();
let tool_name: Arc<str> = tool_use.name.into();
Button::new("deny-tool", "Deny")
Button::new("deny-tool", "Deny", cx)
.label_size(LabelSize::Small)
.icon(IconName::Close)
.icon_position(IconPosition::Start)

View file

@ -281,6 +281,7 @@ impl AgentConfiguration {
Button::new(
SharedString::from(format!("new-thread-{provider_id}")),
"Start New Thread",
cx,
)
.icon_position(IconPosition::Start)
.icon(IconName::Plus)
@ -339,7 +340,7 @@ impl AgentConfiguration {
.child(
PopoverMenu::new("add-provider-popover")
.trigger(
Button::new("add-provider", "Add Provider")
Button::new("add-provider", "Add Provider", cx)
.icon_position(IconPosition::Start)
.icon(IconName::Plus)
.icon_size(IconSize::Small)
@ -552,7 +553,7 @@ impl AgentConfiguration {
.gap_2()
.child(
h_flex().w_full().child(
Button::new("add-context-server", "Add Custom Server")
Button::new("add-context-server", "Add Custom Server", cx)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.full_width()
@ -569,6 +570,7 @@ impl AgentConfiguration {
Button::new(
"install-context-server-extensions",
"Install MCP Extensions",
cx,
)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)

View file

@ -281,7 +281,7 @@ impl AddLlmProviderModal {
.justify_between()
.child(Label::new("Models").size(LabelSize::Small))
.child(
Button::new("add-model", "Add Model")
Button::new("add-model", "Add Model", cx)
.icon(IconName::Plus)
.icon_position(IconPosition::Start)
.icon_size(IconSize::XSmall)
@ -324,7 +324,7 @@ impl AddLlmProviderModal {
.child(model.max_tokens.clone())
.when(has_more_than_one_model, |this| {
this.child(
Button::new(("remove-model", ix), "Remove Model")
Button::new(("remove-model", ix), "Remove Model", cx)
.icon(IconName::Trash)
.icon_position(IconPosition::Start)
.icon_size(IconSize::XSmall)
@ -400,7 +400,7 @@ impl Render for AddLlmProviderModal {
h_flex()
.gap_1()
.child(
Button::new("cancel", "Cancel")
Button::new("cancel", "Cancel", cx)
.key_binding(
KeyBinding::for_action_in(
&menu::Cancel,
@ -415,7 +415,7 @@ impl Render for AddLlmProviderModal {
})),
)
.child(
Button::new("save-server", "Save Provider")
Button::new("save-server", "Save Provider", cx)
.key_binding(
KeyBinding::for_action_in(
&menu::Confirm,

View file

@ -564,7 +564,7 @@ impl ConfigureContextServerModal {
} = &self.source
{
Some(
Button::new("open-repository", "Open Repository")
Button::new("open-repository", "Open Repository", cx)
.icon(IconName::ArrowUpRight)
.icon_color(Color::Muted)
.icon_size(IconSize::Small)
@ -600,6 +600,7 @@ impl ConfigureContextServerModal {
} else {
"Dismiss"
},
cx,
)
.key_binding(
KeyBinding::for_action_in(&menu::Cancel, &focus_handle, window, cx)
@ -617,6 +618,7 @@ impl ConfigureContextServerModal {
} else {
"Configure Server"
},
cx,
)
.disabled(is_connecting)
.key_binding(

View file

@ -726,7 +726,7 @@ impl Render for AgentDiffPane {
.gap_2()
.child("No changes to review")
.child(
Button::new("continue-iterating", "Continue Iterating")
Button::new("continue-iterating", "Continue Iterating", cx)
.style(ButtonStyle::Filled)
.icon(IconName::ForwardArrow)
.icon_position(IconPosition::Start)
@ -806,7 +806,7 @@ fn render_diff_hunk_controls(
.block_mouse_except_scroll()
.shadow_md()
.children(vec![
Button::new(("reject", row as u64), "Reject")
Button::new(("reject", row as u64), "Reject", cx)
.disabled(is_created_file)
.key_binding(
KeyBinding::for_action_in(
@ -834,7 +834,7 @@ fn render_diff_hunk_controls(
})
}
}),
Button::new(("keep", row as u64), "Keep")
Button::new(("keep", row as u64), "Keep", cx)
.key_binding(
KeyBinding::for_action_in(&Keep, &editor.read(cx).focus_handle(cx), window, cx)
.map(|kb| kb.size(rems_from_px(12.))),
@ -1147,7 +1147,7 @@ impl Render for AgentDiffToolbar {
h_flex()
.gap_0p5()
.child(
Button::new("reject-all", "Reject All")
Button::new("reject-all", "Reject All", cx)
.key_binding({
KeyBinding::for_action_in(
&RejectAll,
@ -1162,7 +1162,7 @@ impl Render for AgentDiffToolbar {
})),
)
.child(
Button::new("keep-all", "Keep All")
Button::new("keep-all", "Keep All", cx)
.key_binding({
KeyBinding::for_action_in(
&KeepAll,
@ -1242,7 +1242,7 @@ impl Render for AgentDiffToolbar {
.child(
h_group_sm()
.child(
Button::new("reject-all", "Reject All")
Button::new("reject-all", "Reject All", cx)
.key_binding({
KeyBinding::for_action_in(
&RejectAll,
@ -1257,7 +1257,7 @@ impl Render for AgentDiffToolbar {
})),
)
.child(
Button::new("keep-all", "Keep All")
Button::new("keep-all", "Keep All", cx)
.key_binding({
KeyBinding::for_action_in(
&KeepAll,

View file

@ -2444,7 +2444,7 @@ impl AgentPanel {
.gap_1()
.max_w_48()
.child(
Button::new("context", "Add Context")
Button::new("context", "Add Context", cx)
.label_size(LabelSize::Small)
.icon(IconName::FileCode)
.icon_position(IconPosition::Start)
@ -2465,7 +2465,7 @@ impl AgentPanel {
}),
)
.child(
Button::new("mode", "Switch Model")
Button::new("mode", "Switch Model", cx)
.label_size(LabelSize::Small)
.icon(IconName::DatabaseZap)
.icon_position(IconPosition::Start)
@ -2486,7 +2486,7 @@ impl AgentPanel {
}),
)
.child(
Button::new("settings", "View Settings")
Button::new("settings", "View Settings", cx)
.label_size(LabelSize::Small)
.icon(IconName::Settings)
.icon_position(IconPosition::Start)
@ -2529,7 +2529,7 @@ impl AgentPanel {
self.render_empty_state_section_header(
"Recent",
Some(
Button::new("view-history", "View All")
Button::new("view-history", "View All", cx)
.style(ButtonStyle::Subtle)
.label_size(LabelSize::Small)
.key_binding(
@ -2727,7 +2727,7 @@ impl AgentPanel {
.severity(ui::Severity::Warning)
.child(Label::new(configuration_error.to_string()))
.action_slot(
Button::new("settings", "Configure Provider")
Button::new("settings", "Configure Provider", cx)
.style(ButtonStyle::Tinted(ui::TintColor::Warning))
.label_size(LabelSize::Small)
.key_binding(
@ -2784,7 +2784,7 @@ impl AgentPanel {
h_flex()
.gap_1()
.child(
Button::new("continue-conversation", "Continue")
Button::new("continue-conversation", "Continue", cx)
.layer(ElevationIndex::ModalSurface)
.label_size(LabelSize::Small)
.key_binding(
@ -2802,7 +2802,7 @@ impl AgentPanel {
)
.when(model.supports_burn_mode(), |this| {
this.child(
Button::new("continue-burn-mode", "Continue with Burn Mode")
Button::new("continue-burn-mode", "Continue with Burn Mode", cx)
.style(ButtonStyle::Filled)
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.layer(ElevationIndex::ModalSurface)
@ -2873,7 +2873,7 @@ impl AgentPanel {
thread: &Entity<ActiveThread>,
cx: &mut Context<Self>,
) -> impl IntoElement {
Button::new("upgrade", "Upgrade")
Button::new("upgrade", "Upgrade", cx)
.label_size(LabelSize::Small)
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.on_click(cx.listener({
@ -2965,7 +2965,7 @@ impl AgentPanel {
.size(IconSize::Small)
.color(Color::Error);
let retry_button = Button::new("retry", "Retry")
let retry_button = Button::new("retry", "Retry", cx)
.icon(IconName::RotateCw)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
@ -3009,7 +3009,7 @@ impl AgentPanel {
.size(IconSize::Small)
.color(Color::Error);
let retry_button = Button::new("retry", "Retry")
let retry_button = Button::new("retry", "Retry", cx)
.icon(IconName::RotateCw)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
@ -3034,22 +3034,26 @@ impl AgentPanel {
.primary_action(retry_button);
if can_enable_burn_mode {
let burn_mode_button = Button::new("enable_burn_retry", "Enable Burn Mode and Retry")
.icon(IconName::ZedBurnMode)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
.label_size(LabelSize::Small)
.on_click({
let thread = thread.clone();
move |_, window, cx| {
thread.update(cx, |thread, cx| {
thread.clear_last_error();
thread.thread().update(cx, |thread, cx| {
thread.enable_burn_mode_and_retry(Some(window.window_handle()), cx);
let burn_mode_button =
Button::new("enable_burn_retry", "Enable Burn Mode and Retry", cx)
.icon(IconName::ZedBurnMode)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
.label_size(LabelSize::Small)
.on_click({
let thread = thread.clone();
move |_, window, cx| {
thread.update(cx, |thread, cx| {
thread.clear_last_error();
thread.thread().update(cx, |thread, cx| {
thread.enable_burn_mode_and_retry(
Some(window.window_handle()),
cx,
);
});
});
});
}
});
}
});
callout = callout.secondary_action(burn_mode_button);
}

View file

@ -478,7 +478,7 @@ impl<T: 'static> PromptEditor<T> {
match codegen_status {
CodegenStatus::Idle => {
vec![
Button::new("start", mode.start_label())
Button::new("start", mode.start_label(), cx)
.label_size(LabelSize::Small)
.icon(IconName::Return)
.icon_size(IconSize::XSmall)
@ -745,11 +745,11 @@ impl<T: 'static> PromptEditor<T> {
h_flex()
.gap_2()
.child(
Button::new("dismiss", "Dismiss")
Button::new("dismiss", "Dismiss", cx)
.style(ButtonStyle::Transparent)
.on_click(cx.listener(Self::toggle_rate_limit_notice)),
)
.child(Button::new("more-info", "More Info").on_click(
.child(Button::new("more-info", "More Info", cx).on_click(
|_event, window, cx| {
window.dispatch_action(
Box::new(zed_actions::OpenAccountSettings),

View file

@ -548,7 +548,7 @@ impl PickerDelegate for LanguageModelPickerDelegate {
.justify_between()
.when(cx.has_flag::<ZedProFeatureFlag>(), |this| {
this.child(match plan {
Plan::ZedPro => Button::new("zed-pro", "Zed Pro")
Plan::ZedPro => Button::new("zed-pro", "Zed Pro", cx)
.icon(IconName::ZedAssistant)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
@ -564,12 +564,13 @@ impl PickerDelegate for LanguageModelPickerDelegate {
} else {
"Try Pro"
},
cx,
)
.on_click(|_, _, cx| cx.open_url(TRY_ZED_PRO_URL)),
})
})
.child(
Button::new("configure", "Configure")
Button::new("configure", "Configure", cx)
.icon(IconName::Settings)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)

View file

@ -1074,7 +1074,7 @@ impl MessageEditor {
)
.child(Divider::vertical().color(DividerColor::Border))
.child(
Button::new("reject-all-changes", "Reject All")
Button::new("reject-all-changes", "Reject All", cx)
.label_size(LabelSize::Small)
.disabled(pending_edits)
.when(pending_edits, |this| {
@ -1094,7 +1094,7 @@ impl MessageEditor {
})),
)
.child(
Button::new("accept-all-changes", "Accept All")
Button::new("accept-all-changes", "Accept All", cx)
.label_size(LabelSize::Small)
.disabled(pending_edits)
.when(pending_edits, |this| {
@ -1204,7 +1204,7 @@ impl MessageEditor {
.gap_1()
.visible_on_hover("edited-code")
.child(
Button::new("review", "Review")
Button::new("review", "Review", cx)
.label_size(LabelSize::Small)
.on_click({
let buffer = buffer.clone();
@ -1221,7 +1221,7 @@ impl MessageEditor {
Divider::vertical().color(DividerColor::BorderVariant),
)
.child(
Button::new("reject-file", "Reject")
Button::new("reject-file", "Reject", cx)
.label_size(LabelSize::Small)
.disabled(pending_edits)
.on_click({
@ -1236,7 +1236,7 @@ impl MessageEditor {
}),
)
.child(
Button::new("accept-file", "Accept")
Button::new("accept-file", "Accept", cx)
.label_size(LabelSize::Small)
.disabled(pending_edits)
.on_click({
@ -1332,7 +1332,7 @@ impl MessageEditor {
.title(title)
.description(description)
.primary_action(
Button::new("start-new-thread", "Start New Thread")
Button::new("start-new-thread", "Start New Thread", cx)
.label_size(LabelSize::Small)
.on_click(cx.listener(|this, _, window, cx| {
let from_thread_id = Some(this.thread.read(cx).id().clone());

View file

@ -167,7 +167,7 @@ impl Render for ProfileSelector {
if configured_model.model.supports_tools() {
let this = cx.entity().clone();
let focus_handle = self.focus_handle.clone();
let trigger_button = Button::new("profile-selector-model", selected_profile)
let trigger_button = Button::new("profile-selector-model", selected_profile, cx)
.label_size(LabelSize::Small)
.color(Color::Muted)
.icon(IconName::ChevronDown)
@ -201,7 +201,7 @@ impl Render for ProfileSelector {
})
.into_any_element()
} else {
Button::new("tools-not-supported-button", "Tools Unsupported")
Button::new("tools-not-supported-button", "Tools Unsupported", cx)
.disabled(true)
.label_size(LabelSize::Small)
.color(Color::Muted)

View file

@ -1200,7 +1200,7 @@ impl TextThreadEditor {
})
.children(match &message.status {
MessageStatus::Error(error) => Some(
Button::new("show-error", "Error")
Button::new("show-error", "Error", cx)
.color(Color::Error)
.selected_label_color(Color::Error)
.selected_icon_color(Color::Error)
@ -1920,7 +1920,7 @@ impl TextThreadEditor {
None => (ButtonStyle::Filled, None),
};
Button::new("send_button", "Send")
Button::new("send_button", "Send", cx)
.label_size(LabelSize::Small)
.disabled(self.sending_disabled(cx))
.style(style)
@ -2124,14 +2124,16 @@ impl TextThreadEditor {
h_flex()
.justify_end()
.mt_1()
.child(Button::new("subscribe", "Subscribe").on_click(cx.listener(
|this, _, _window, cx| {
this.last_error = None;
cx.open_url(&zed_urls::account_url(cx));
cx.notify();
},
)))
.child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
.child(
Button::new("subscribe", "Subscribe", cx).on_click(cx.listener(
|this, _, _window, cx| {
this.last_error = None;
cx.open_url(&zed_urls::account_url(cx));
cx.notify();
},
)),
)
.child(Button::new("dismiss", "Dismiss", cx).on_click(cx.listener(
|this, _, _window, cx| {
this.last_error = None;
cx.notify();
@ -2165,17 +2167,14 @@ impl TextThreadEditor {
.overflow_y_scroll()
.child(Label::new(error_message.clone())),
)
.child(
h_flex()
.justify_end()
.mt_1()
.child(Button::new("dismiss", "Dismiss").on_click(cx.listener(
|this, _, _window, cx| {
this.last_error = None;
cx.notify();
},
))),
)
.child(h_flex().justify_end().mt_1().child(
Button::new("dismiss", "Dismiss", cx).on_click(cx.listener(
|this, _, _window, cx| {
this.last_error = None;
cx.notify();
},
)),
))
.into_any()
}
}

View file

@ -171,7 +171,7 @@ impl Render for AgentNotification {
.gap_1()
.items_center()
.child(
Button::new("open", "View Panel")
Button::new("open", "View Panel", cx)
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.full_width()
.on_click({
@ -180,11 +180,15 @@ impl Render for AgentNotification {
})
}),
)
.child(Button::new("dismiss", "Dismiss").full_width().on_click({
cx.listener(move |_, _event, _, cx| {
cx.emit(AgentNotificationEvent::Dismissed);
})
})),
.child(
Button::new("dismiss", "Dismiss", cx)
.full_width()
.on_click({
cx.listener(move |_, _event, _, cx| {
cx.emit(AgentNotificationEvent::Dismissed);
})
}),
),
)
}
}

View file

@ -35,7 +35,7 @@ impl RenderOnce for EndTrialUpsell {
)
.child(plan_definitions.pro_plan(false))
.child(
Button::new("cta-button", "Upgrade to Zed Pro")
Button::new("cta-button", "Upgrade to Zed Pro", cx)
.full_width()
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.on_click(move |_, _window, cx| {

View file

@ -147,13 +147,13 @@ impl Render for AgentOnboardingModal {
)),
));
let open_panel_button = Button::new("open-panel", "Get Started with the Agent Panel")
let open_panel_button = Button::new("open-panel", "Get Started with the Agent Panel", cx)
.icon_size(IconSize::Indicator)
.style(ButtonStyle::Tinted(TintColor::Accent))
.full_width()
.on_click(cx.listener(Self::open_panel));
let blog_post_button = Button::new("view-blog", "Check out the Blog Post")
let blog_post_button = Button::new("view-blog", "Check out the Blog Post", cx)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Indicator)
.icon_color(Color::Muted)

View file

@ -99,7 +99,7 @@ impl RenderOnce for UsageCallout {
.title(title)
.description(message)
.primary_action(
Button::new("upgrade", button_text)
Button::new("upgrade", button_text, cx)
.label_size(LabelSize::Small)
.on_click(move |_, _, cx| {
cx.open_url(&url);

View file

@ -130,7 +130,7 @@ impl RenderOnce for ApiKeysWithoutProviders {
"Add your own keys to use AI without signing in.",
)))
.child(
Button::new("configure-providers", "Configure Providers")
Button::new("configure-providers", "Configure Providers", cx)
.full_width()
.style(ButtonStyle::Outlined)
.on_click(move |_, window, cx| {

View file

@ -94,7 +94,7 @@ impl ZedAiOnboarding {
self
}
fn render_accept_terms_of_service(&self) -> AnyElement {
fn render_accept_terms_of_service(&self, cx: &mut App) -> AnyElement {
v_flex()
.gap_1()
.w_full()
@ -105,7 +105,7 @@ impl ZedAiOnboarding {
.mb_2(),
)
.child(
Button::new("terms_of_service", "Review Terms of Service")
Button::new("terms_of_service", "Review Terms of Service", cx)
.full_width()
.style(ButtonStyle::Outlined)
.icon(IconName::ArrowUpRight)
@ -117,7 +117,7 @@ impl ZedAiOnboarding {
}),
)
.child(
Button::new("accept_terms", "Accept")
Button::new("accept_terms", "Accept", cx)
.full_width()
.style(ButtonStyle::Tinted(TintColor::Accent))
.on_click({
@ -130,7 +130,7 @@ impl ZedAiOnboarding {
.into_any_element()
}
fn render_sign_in_disclaimer(&self, _cx: &mut App) -> AnyElement {
fn render_sign_in_disclaimer(&self, cx: &mut App) -> AnyElement {
let signing_in = matches!(self.sign_in_status, SignInStatus::SigningIn);
let plan_definitions = PlanDefinitions;
@ -144,7 +144,7 @@ impl ZedAiOnboarding {
)
.child(plan_definitions.pro_plan(false))
.child(
Button::new("sign_in", "Try Zed Pro for Free")
Button::new("sign_in", "Try Zed Pro for Free", cx)
.disabled(signing_in)
.full_width()
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
@ -187,7 +187,7 @@ impl ZedAiOnboarding {
)
.child(plan_definitions.pro_plan(true))
.child(
Button::new("pro", "Get Started")
Button::new("pro", "Get Started", cx)
.full_width()
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.on_click(move |_, _window, cx| {
@ -268,7 +268,7 @@ impl ZedAiOnboarding {
)
.child(plan_definitions.pro_trial(true))
.child(
Button::new("pro", "Start Free Trial")
Button::new("pro", "Start Free Trial", cx)
.full_width()
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.on_click(move |_, _window, cx| {
@ -320,7 +320,7 @@ impl ZedAiOnboarding {
.into_any_element()
}
fn render_pro_plan_state(&self, _cx: &mut App) -> AnyElement {
fn render_pro_plan_state(&self, cx: &mut App) -> AnyElement {
let plan_definitions = PlanDefinitions;
v_flex()
@ -333,7 +333,7 @@ impl ZedAiOnboarding {
)
.child(plan_definitions.pro_plan(false))
.child(
Button::new("pro", "Continue with Zed Pro")
Button::new("pro", "Continue with Zed Pro", cx)
.full_width()
.style(ButtonStyle::Outlined)
.on_click({
@ -358,7 +358,7 @@ impl RenderOnce for ZedAiOnboarding {
Some(Plan::ZedPro) => self.render_pro_plan_state(cx),
}
} else {
self.render_accept_terms_of_service()
self.render_accept_terms_of_service(cx)
}
} else {
self.render_sign_in_disclaimer(cx)

View file

@ -186,7 +186,7 @@ impl RenderOnce for AiUpsellCard {
)
.child(plan_definitions.pro_plan(true))
.child(
Button::new("pro", "Get Started")
Button::new("pro", "Get Started", cx)
.full_width()
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.on_click(move |_, _window, cx| {
@ -209,19 +209,25 @@ impl RenderOnce for AiUpsellCard {
.child(
footer_container
.child(
Button::new("start_trial", "Start 14-day Free Pro Trial")
.full_width()
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.when_some(self.tab_index, |this, tab_index| {
this.tab_index(tab_index)
})
.on_click(move |_, _window, cx| {
Button::new(
"start_trial",
"Start 14-day Free Pro Trial",
cx,
)
.full_width()
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.when_some(self.tab_index, |this, tab_index| {
this.tab_index(tab_index)
})
.on_click(
move |_, _window, cx| {
telemetry::event!(
"Start Trial Clicked",
state = "post-sign-in"
);
cx.open_url(&zed_urls::start_trial_url(cx))
}),
},
),
)
.child(
Label::new("No credit card required")
@ -261,7 +267,7 @@ impl RenderOnce for AiUpsellCard {
)
.child(plans_section)
.child(
Button::new("sign_in", "Sign In")
Button::new("sign_in", "Sign In", cx)
.full_width()
.style(ButtonStyle::Tinted(ui::TintColor::Accent))
.when_some(self.tab_index, |this, tab_index| this.tab_index(tab_index))

View file

@ -50,6 +50,7 @@ impl Render for EditPredictionOnboarding {
} else {
"Configure Copilot"
},
cx,
)
.full_width()
.style(ButtonStyle::Outlined)

View file

@ -256,7 +256,7 @@ impl ToolCard for FindPathToolCard {
let workspace_clone = workspace.clone();
let button_label = path.to_string_lossy().to_string();
Button::new(("path", index), button_label)
Button::new(("path", index), button_label, cx)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Small)
.icon_position(IconPosition::End)

View file

@ -174,7 +174,7 @@ impl ToolCard for WebSearchToolCard {
let title = result.title.clone();
let url = SharedString::from(result.url.clone());
Button::new(("result", index), title)
Button::new(("result", index), title, cx)
.label_size(LabelSize::Small)
.color(Color::Muted)
.icon(IconName::ArrowUpRight)

View file

@ -987,7 +987,7 @@ impl Render for ChatPanel {
)
.child(
div().pt_1().w_full().items_center().child(
Button::new("toggle-collab", "Open")
Button::new("toggle-collab", "Open", cx)
.full_width()
.key_binding(KeyBinding::for_action(
&collab_panel::ToggleFocus,

View file

@ -2307,7 +2307,7 @@ impl CollabPanel {
v_flex()
.gap_2()
.child(
Button::new("sign_in", "Sign in")
Button::new("sign_in", "Sign in", cx)
.icon_color(Color::Muted)
.icon(IconName::Github)
.icon_position(IconPosition::Start)

View file

@ -177,7 +177,7 @@ impl Render for ChannelModal {
))
.children(
Some(
Button::new("copy-link", "Copy Link")
Button::new("copy-link", "Copy Link", cx)
.label_size(LabelSize::Small)
.on_click(cx.listener(move |this, _, _, cx| {
if let Some(channel) = this

View file

@ -319,20 +319,22 @@ impl NotificationPanel {
h_flex()
.flex_grow()
.justify_end()
.child(Button::new("decline", "Decline").on_click({
let notification = notification.clone();
let entity = cx.entity().clone();
move |_, _, cx| {
entity.update(cx, |this, cx| {
this.respond_to_notification(
notification.clone(),
false,
cx,
)
});
}
}))
.child(Button::new("accept", "Accept").on_click({
.child(Button::new("decline", "Decline", cx).on_click(
{
let notification = notification.clone();
let entity = cx.entity().clone();
move |_, _, cx| {
entity.update(cx, |this, cx| {
this.respond_to_notification(
notification.clone(),
false,
cx,
)
});
}
},
))
.child(Button::new("accept", "Accept", cx).on_click({
let notification = notification.clone();
let entity = cx.entity().clone();
move |_, _, cx| {
@ -631,7 +633,7 @@ impl Render for NotificationPanel {
.gap_2()
.p_4()
.child(
Button::new("connect_prompt_button", "Connect")
Button::new("connect_prompt_button", "Connect", cx)
.icon_color(Color::Muted)
.icon(IconName::Github)
.icon_position(IconPosition::Start)

View file

@ -117,11 +117,11 @@ impl Render for IncomingCallNotification {
div().size_full().font(ui_font).child(
CollabNotification::new(
self.state.call.calling_user.avatar_uri.clone(),
Button::new("accept", "Accept").on_click({
Button::new("accept", "Accept", cx).on_click({
let state = self.state.clone();
move |_, _, cx| state.respond(true, cx)
}),
Button::new("decline", "Decline").on_click({
Button::new("decline", "Decline", cx).on_click({
let state = self.state.clone();
move |_, _, cx| state.respond(false, cx)
}),

View file

@ -126,10 +126,12 @@ impl Render for ProjectSharedNotification {
div().size_full().font(ui_font).child(
CollabNotification::new(
self.owner.avatar_uri.clone(),
Button::new("open", "Open").on_click(cx.listener(move |this, _event, _, cx| {
this.join(cx);
})),
Button::new("dismiss", "Dismiss").on_click(cx.listener(
Button::new("open", "Open", cx).on_click(cx.listener(
move |this, _event, _, cx| {
this.join(cx);
},
)),
Button::new("dismiss", "Dismiss", cx).on_click(cx.listener(
move |this, _event, _, cx| {
this.dismiss(cx);
},

View file

@ -18,8 +18,8 @@ impl Render for CollabNotificationStory {
window_container(400., 72.).child(
CollabNotification::new(
"https://avatars.githubusercontent.com/u/1486634?v=4",
Button::new("accept", "Accept"),
Button::new("decline", "Decline"),
Button::new("accept", "Accept", cx),
Button::new("decline", "Decline", cx),
)
.child(
v_flex()
@ -35,8 +35,8 @@ impl Render for CollabNotificationStory {
window_container(400., 72.).child(
CollabNotification::new(
"https://avatars.githubusercontent.com/u/1714999?v=4",
Button::new("open", "Open"),
Button::new("dismiss", "Dismiss"),
Button::new("open", "Open", cx),
Button::new("dismiss", "Dismiss", cx),
)
.child(Label::new("iamnbutler"))
.child(Label::new("is sharing a project in Zed:"))

View file

@ -264,7 +264,7 @@ impl CopilotCodeVerification {
.size(ui::LabelSize::Small),
)
.child(
Button::new("connect-button", connect_button_label)
Button::new("connect-button", connect_button_label, cx)
.on_click({
let verification_uri = data.verification_uri.clone();
cx.listener(move |this, _, _window, cx| {
@ -276,7 +276,7 @@ impl CopilotCodeVerification {
.style(ButtonStyle::Filled),
)
.child(
Button::new("copilot-enable-cancel-button", "Cancel")
Button::new("copilot-enable-cancel-button", "Cancel", cx)
.full_width()
.on_click(cx.listener(|_, _, _, cx| {
cx.emit(DismissEvent);
@ -292,7 +292,7 @@ impl CopilotCodeVerification {
"You can update your settings or sign out from the Copilot menu in the status bar.",
))
.child(
Button::new("copilot-enabled-done-button", "Done")
Button::new("copilot-enabled-done-button", "Done", cx)
.full_width()
.on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
)
@ -306,12 +306,12 @@ impl CopilotCodeVerification {
"You can enable Copilot by connecting your existing license once you have subscribed or renewed your subscription.",
).color(Color::Warning))
.child(
Button::new("copilot-subscribe-button", "Subscribe on GitHub")
Button::new("copilot-subscribe-button", "Subscribe on GitHub", cx)
.full_width()
.on_click(|_, _, cx| cx.open_url(COPILOT_SIGN_UP_URL)),
)
.child(
Button::new("copilot-subscribe-cancel-button", "Cancel")
Button::new("copilot-subscribe-cancel-button", "Cancel", cx)
.full_width()
.on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
)

View file

@ -535,6 +535,7 @@ impl Render for DapLogToolbarItemView {
))
})
.unwrap_or_else(|| "No adapter selected".into()),
cx,
))
.menu(move |mut window, cx| {
let log_view = log_view.clone();
@ -632,7 +633,7 @@ impl Render for DapLogToolbarItemView {
.child(
div()
.child(
Button::new("clear_log_button", "Clear").on_click(cx.listener(
Button::new("clear_log_button", "Clear", cx).on_click(cx.listener(
|this, _, window, cx| {
if let Some(log_view) = this.log_view.as_ref() {
log_view.update(cx, |log_view, cx| {

View file

@ -1715,7 +1715,7 @@ impl Render for DebugPanel {
.justify_center()
.gap_2()
.child(
Button::new("spawn-new-session-empty-state", "New Session")
Button::new("spawn-new-session-empty-state", "New Session", cx)
.icon(IconName::Plus)
.icon_size(IconSize::XSmall)
.icon_color(Color::Muted)
@ -1725,7 +1725,7 @@ impl Render for DebugPanel {
}),
)
.child(
Button::new("edit-debug-settings", "Edit debug.json")
Button::new("edit-debug-settings", "Edit debug.json", cx)
.icon(IconName::Code)
.icon_size(IconSize::XSmall)
.color(Color::Muted)
@ -1739,7 +1739,7 @@ impl Render for DebugPanel {
}),
)
.child(
Button::new("open-debugger-docs", "Debugger Docs")
Button::new("open-debugger-docs", "Debugger Docs", cx)
.icon(IconName::Book)
.color(Color::Muted)
.icon_size(IconSize::XSmall)
@ -1751,6 +1751,7 @@ impl Render for DebugPanel {
Button::new(
"spawn-new-session-install-extensions",
"Debugger Extensions",
cx,
)
.icon(IconName::Blocks)
.color(Color::Muted)

View file

@ -703,7 +703,7 @@ impl Render for NewProcessModal {
container
.child(
h_flex().child(
Button::new("edit-custom-debug", "Edit in debug.json")
Button::new("edit-custom-debug", "Edit in debug.json", cx)
.on_click(cx.listener(|this, _, window, cx| {
this.save_debug_scenario(window, cx);
}))
@ -719,7 +719,7 @@ impl Render for NewProcessModal {
),
)
.child(
Button::new("debugger-spawn", "Start")
Button::new("debugger-spawn", "Start", cx)
.on_click(cx.listener(|this, _, window, cx| {
this.start_new_session(window, cx)
}))
@ -751,7 +751,7 @@ impl Render for NewProcessModal {
.child(div().children(
KeyBinding::for_action(&*secondary_action, window, cx).map(
|keybind| {
Button::new("edit-attach-task", "Edit in debug.json")
Button::new("edit-attach-task", "Edit in debug.json", cx)
.label_size(LabelSize::Small)
.key_binding(keybind)
.on_click(move |_, window, cx| {
@ -1389,7 +1389,7 @@ impl PickerDelegate for DebugDelegate {
.children({
let action = menu::SecondaryConfirm.boxed_clone();
KeyBinding::for_action(&*action, window, cx).map(|keybind| {
Button::new("edit-debug-task", "Edit in debug.json")
Button::new("edit-debug-task", "Edit in debug.json", cx)
.label_size(LabelSize::Small)
.key_binding(keybind)
.on_click(move |_, window, cx| {
@ -1401,7 +1401,7 @@ impl PickerDelegate for DebugDelegate {
if (current_modifiers.alt || self.matches.is_empty()) && !self.prompt.is_empty() {
let action = picker::ConfirmInput { secondary: false }.boxed_clone();
this.children(KeyBinding::for_action(&*action, window, cx).map(|keybind| {
Button::new("launch-custom", "Launch Custom")
Button::new("launch-custom", "Launch Custom", cx)
.key_binding(keybind)
.on_click(move |_, window, cx| {
window.dispatch_action(action.boxed_clone(), cx)
@ -1415,7 +1415,7 @@ impl PickerDelegate for DebugDelegate {
let run_entry_label =
if is_recent_selected { "Rerun" } else { "Spawn" };
Button::new("spawn", run_entry_label)
Button::new("spawn", run_entry_label, cx)
.key_binding(keybind)
.on_click(|_, window, cx| {
window.dispatch_action(menu::Confirm.boxed_clone(), cx);

View file

@ -139,13 +139,13 @@ impl Render for DebuggerOnboardingModal {
)),
));
let open_panel_button = Button::new("open-panel", "Get Started with the Debugger")
let open_panel_button = Button::new("open-panel", "Get Started with the Debugger", cx)
.icon_size(IconSize::Indicator)
.style(ButtonStyle::Tinted(TintColor::Accent))
.full_width()
.on_click(cx.listener(Self::open_panel));
let blog_post_button = Button::new("view-blog", "Check out the Blog Post")
let blog_post_button = Button::new("view-blog", "Check out the Blog Post", cx)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Indicator)
.icon_color(Color::Muted)

View file

@ -128,12 +128,12 @@ impl Render for ProjectDiagnosticsEditor {
self.summary.warning_count, plural_suffix
);
this.child(
Button::new("diagnostics-show-warning-label", label).on_click(cx.listener(
|this, _, window, cx| {
Button::new("diagnostics-show-warning-label", label, cx).on_click(
cx.listener(|this, _, window, cx| {
this.toggle_warnings(&Default::default(), window, cx);
cx.notify();
},
)),
}),
),
)
})
} else {

View file

@ -74,7 +74,7 @@ impl Render for DiagnosticIndicator {
let status = if let Some(diagnostic) = &self.current_diagnostic {
let message = diagnostic.message.split('\n').next().unwrap().to_string();
Some(
Button::new("diagnostic_message", message)
Button::new("diagnostic_message", message, cx)
.label_size(LabelSize::Small)
.tooltip(|window, cx| {
Tooltip::for_action(

View file

@ -23754,10 +23754,10 @@ impl Render for MissingEditPredictionKeybindingTooltip {
.gap_1()
.items_end()
.w_full()
.child(Button::new("open-keymap", "Assign Keybinding").size(ButtonSize::Compact).on_click(|_ev, window, cx| {
.child(Button::new("see-key-binding", "See Keybinding", cx).size(ButtonSize::Compact).on_click(|_ev, window, cx| {
window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx)
}))
.child(Button::new("see-docs", "See Docs").size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
.child(Button::new("learn-more", "Learn More", cx).size(ButtonSize::Compact).on_click(|_ev, _window, cx| {
cx.open_url("https://zed.dev/docs/completions#edit-predictions-missing-keybinding");
})),
)
@ -23804,7 +23804,7 @@ fn render_diff_hunk_controls(
.block_mouse_except_scroll()
.shadow_md()
.child(if status.has_secondary_hunk() {
Button::new(("stage", row as u64), "Stage")
Button::new(("stage", row as u64), "Stage", cx)
.alpha(if status.is_pending() { 0.66 } else { 1.0 })
.tooltip({
let focus_handle = editor.focus_handle(cx);
@ -23831,7 +23831,7 @@ fn render_diff_hunk_controls(
}
})
} else {
Button::new(("unstage", row as u64), "Unstage")
Button::new(("unstage", row as u64), "Unstage", cx)
.alpha(if status.is_pending() { 0.66 } else { 1.0 })
.tooltip({
let focus_handle = editor.focus_handle(cx);
@ -23859,7 +23859,7 @@ fn render_diff_hunk_controls(
})
})
.child(
Button::new(("restore", row as u64), "Restore")
Button::new(("restore", row as u64), "Restore", cx)
.tooltip({
let focus_handle = editor.focus_handle(cx);
move |window, cx| {

View file

@ -56,7 +56,7 @@ impl RenderOnce for FeatureUpsell {
self.docs_url,
|el, docs_url| {
el.child(
Button::new("open_docs", "View Documentation")
Button::new("open_docs", "View Documentation", cx)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Small)
.icon_position(IconPosition::End)

View file

@ -596,6 +596,7 @@ impl ExtensionsPage {
Button::new(
SharedString::from(format!("rebuild-{}", extension.id)),
"Rebuild",
cx,
)
.color(Color::Accent)
.disabled(matches!(status, ExtensionStatus::Upgrading))
@ -609,7 +610,7 @@ impl ExtensionsPage {
}),
)
.child(
Button::new(SharedString::from(extension.id.clone()), "Uninstall")
Button::new(SharedString::from(extension.id.clone()), "Uninstall", cx)
.color(Color::Accent)
.disabled(matches!(status, ExtensionStatus::Removing))
.on_click({
@ -626,6 +627,7 @@ impl ExtensionsPage {
Button::new(
SharedString::from(format!("configure-{}", extension.id)),
"Configure",
cx,
)
.color(Color::Accent)
.disabled(matches!(status, ExtensionStatus::Installing))
@ -947,6 +949,7 @@ impl ExtensionsPage {
install_or_uninstall: Button::new(
SharedString::from(extension.id.clone()),
"Install",
cx,
),
configure: None,
upgrade: None,
@ -963,6 +966,7 @@ impl ExtensionsPage {
install_or_uninstall: Button::new(
SharedString::from(extension.id.clone()),
"Install",
cx,
)
.on_click({
let extension_id = extension.id.clone();
@ -980,6 +984,7 @@ impl ExtensionsPage {
install_or_uninstall: Button::new(
SharedString::from(extension.id.clone()),
"Install",
cx,
)
.disabled(true),
configure: None,
@ -989,23 +994,27 @@ impl ExtensionsPage {
install_or_uninstall: Button::new(
SharedString::from(extension.id.clone()),
"Uninstall",
cx,
)
.disabled(true),
configure: is_configurable.then(|| {
Button::new(
SharedString::from(format!("configure-{}", extension.id)),
"Configure",
cx,
)
.disabled(true)
}),
upgrade: Some(
Button::new(SharedString::from(extension.id.clone()), "Upgrade").disabled(true),
Button::new(SharedString::from(extension.id.clone()), "Upgrade", cx)
.disabled(true),
),
},
ExtensionStatus::Installed(installed_version) => ExtensionCardButtons {
install_or_uninstall: Button::new(
SharedString::from(extension.id.clone()),
"Uninstall",
cx,
)
.on_click({
let extension_id = extension.id.clone();
@ -1022,6 +1031,7 @@ impl ExtensionsPage {
Button::new(
SharedString::from(format!("configure-{}", extension.id)),
"Configure",
cx,
)
.on_click({
let extension_id = extension.id.clone();
@ -1047,7 +1057,7 @@ impl ExtensionsPage {
None
} else {
Some(
Button::new(SharedString::from(extension.id.clone()), "Upgrade")
Button::new(SharedString::from(extension.id.clone()), "Upgrade", cx)
.when(!is_compatible, |upgrade_button| {
upgrade_button.disabled(true).tooltip({
let version = extension.manifest.version.clone();
@ -1085,12 +1095,14 @@ impl ExtensionsPage {
install_or_uninstall: Button::new(
SharedString::from(extension.id.clone()),
"Uninstall",
cx,
)
.disabled(true),
configure: is_configurable.then(|| {
Button::new(
SharedString::from(format!("configure-{}", extension.id)),
"Configure",
cx,
)
.disabled(true)
}),
@ -1394,7 +1406,7 @@ impl Render for ExtensionsPage {
.justify_between()
.child(Headline::new("Extensions").size(HeadlineSize::XLarge))
.child(
Button::new("install-dev-extension", "Install Dev Extension")
Button::new("install-dev-extension", "Install Dev Extension", cx)
.style(ButtonStyle::Filled)
.size(ButtonSize::Large)
.on_click(|_event, window, cx| {
@ -1470,7 +1482,7 @@ impl Render for ExtensionsPage {
.border_color(cx.theme().colors().border_variant)
.overflow_x_scroll()
.child(
Button::new("filter-all-categories", "All")
Button::new("filter-all-categories", "All", cx)
.when(self.provides_filter.is_none(), |button| {
button.style(ButtonStyle::Filled)
})
@ -1493,7 +1505,7 @@ impl Render for ExtensionsPage {
let button_id = SharedString::from(format!("filter-category-{}", label));
Some(
Button::new(button_id, label)
Button::new(button_id, label, cx)
.style(if self.provides_filter == Some(provides) {
ButtonStyle::Filled
} else {

View file

@ -68,7 +68,7 @@ impl Render for FeedbackModal {
)
.child(Label::new("Thanks for using Zed! To share your experience with us, reach for the channel that's the most appropriate:"))
.child(
Button::new("file-a-bug-report", "File a Bug Report")
Button::new("file-a-bug-report", "File a Bug Report", cx)
.full_width()
.icon(IconName::Debug)
.icon_size(IconSize::XSmall)
@ -79,7 +79,7 @@ impl Render for FeedbackModal {
})),
)
.child(
Button::new("request-a-feature", "Request a Feature")
Button::new("request-a-feature", "Request a Feature", cx)
.full_width()
.icon(IconName::Sparkle)
.icon_size(IconSize::XSmall)
@ -90,7 +90,7 @@ impl Render for FeedbackModal {
})),
)
.child(
Button::new("send-us_an-email", "Send an Email")
Button::new("send-us_an-email", "Send an Email", cx)
.full_width()
.icon(IconName::Envelope)
.icon_size(IconSize::XSmall)
@ -101,7 +101,7 @@ impl Render for FeedbackModal {
})),
)
.child(
Button::new("zed_repository", "GitHub Repository")
Button::new("zed_repository", "GitHub Repository", cx)
.full_width()
.icon(IconName::Github)
.icon_size(IconSize::XSmall)

View file

@ -1779,7 +1779,7 @@ impl PickerDelegate for FileFinderDelegate {
}),
)
.child(
Button::new("open-selection", "Open")
Button::new("open-selection", "Open", cx)
.key_binding(
KeyBinding::for_action_in(
&menu::Confirm,

View file

@ -301,6 +301,7 @@ impl BlameRenderer for GitBlameRenderer {
Button::new(
"pull-request-button",
format!("#{}", pr.number),
cx,
)
.color(Color::Muted)
.icon(IconName::PullRequest)
@ -318,6 +319,7 @@ impl BlameRenderer for GitBlameRenderer {
Button::new(
"commit-sha-button",
short_commit_id.clone(),
cx,
)
.style(ButtonStyle::Subtle)
.color(Color::Muted)

View file

@ -363,7 +363,7 @@ impl CommitModal {
.map(|b| b.name().to_owned())
.unwrap_or_else(|| "<no branch>".to_owned());
let branch_picker_button = panel_button(branch)
let branch_picker_button = panel_button(branch, cx)
.icon(IconName::GitBranch)
.icon_size(IconSize::Small)
.icon_color(Color::Placeholder)

View file

@ -283,6 +283,7 @@ impl Render for CommitTooltip {
Button::new(
"pull-request-button",
format!("#{}", pr.number),
cx,
)
.color(Color::Muted)
.icon(IconName::PullRequest)
@ -300,6 +301,7 @@ impl Render for CommitTooltip {
Button::new(
"commit-sha-button",
short_commit_id.clone(),
cx,
)
.style(ButtonStyle::Subtle)
.color(Color::Muted)

View file

@ -394,7 +394,7 @@ fn render_conflict_buttons(
.gap_1()
.bg(cx.theme().colors().editor_background)
.child(
Button::new("head", "Use HEAD")
Button::new("head", "Use HEAD", cx)
.label_size(LabelSize::Small)
.on_click({
let editor = editor.clone();
@ -414,7 +414,7 @@ fn render_conflict_buttons(
}),
)
.child(
Button::new("origin", "Use Origin")
Button::new("origin", "Use Origin", cx)
.label_size(LabelSize::Small)
.on_click({
let editor = editor.clone();
@ -434,7 +434,7 @@ fn render_conflict_buttons(
}),
)
.child(
Button::new("both", "Use Both")
Button::new("both", "Use Both", cx)
.label_size(LabelSize::Small)
.on_click({
let editor = editor.clone();

View file

@ -3327,7 +3327,7 @@ impl GitPanel {
.px_2()
.justify_between()
.child(
panel_button(change_string)
panel_button(change_string, cx)
.color(Color::Muted)
.tooltip(Tooltip::for_action_title_in(
"Open Diff",
@ -3345,7 +3345,7 @@ impl GitPanel {
.gap_1()
.child(self.render_overflow_menu("overflow_menu"))
.child(
panel_filled_button(text)
panel_filled_button(text, cx)
.tooltip(Tooltip::for_action_title_in(
tooltip,
action.as_ref(),
@ -3604,7 +3604,7 @@ impl GitPanel {
),
)
.child(
panel_button("Cancel")
panel_button("Cancel", cx)
.size(ButtonSize::Default)
.on_click(cx.listener(|this, _, _, cx| this.set_amend_pending(false, cx))),
)
@ -3703,7 +3703,7 @@ impl GitPanel {
let worktree_count = self.project.read(cx).visible_worktrees(cx).count();
(worktree_count > 0 && self.active_repository.is_none()).then(|| {
h_flex().w_full().justify_around().child(
panel_filled_button("Initialize Repository")
panel_filled_button("Initialize Repository", cx)
.tooltip(Tooltip::for_action_title_in(
"git init",
&git::Init,
@ -4841,7 +4841,7 @@ impl RenderOnce for PanelRepoFooter {
util::truncate_and_trailoff(branch_name.trim_ascii(), branch_display_len)
};
let repo_selector_trigger = Button::new("repo-selector", truncated_repo_name)
let repo_selector_trigger = Button::new("repo-selector", truncated_repo_name, cx)
.style(ButtonStyle::Transparent)
.size(ButtonSize::None)
.label_size(LabelSize::Small)
@ -4862,7 +4862,7 @@ impl RenderOnce for PanelRepoFooter {
.anchor(Corner::BottomLeft)
.into_any_element();
let branch_selector_button = Button::new("branch-selector", truncated_branch_name)
let branch_selector_button = Button::new("branch-selector", truncated_branch_name, cx)
.style(ButtonStyle::Transparent)
.size(ButtonSize::None)
.label_size(LabelSize::Small)

View file

@ -118,13 +118,13 @@ impl Render for GitOnboardingModal {
)),
));
let open_panel_button = Button::new("open-panel", "Get Started with the Git Panel")
let open_panel_button = Button::new("open-panel", "Get Started with the Git Panel", cx)
.icon_size(IconSize::Indicator)
.style(ButtonStyle::Tinted(TintColor::Accent))
.full_width()
.on_click(cx.listener(Self::open_panel));
let blog_post_button = Button::new("view-blog", "Check out the Blog Post")
let blog_post_button = Button::new("view-blog", "Check out the Blog Post", cx)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Indicator)
.icon_color(Color::Muted)

View file

@ -759,7 +759,7 @@ impl Render for ProjectDiff {
})
.child(
h_flex().justify_around().mt_1().child(
Button::new("project-diff-close-button", "Close")
Button::new("project-diff-close-button", "Close", cx)
// .style(ButtonStyle::Transparent)
.key_binding(KeyBinding::for_action_in(
&CloseActiveItem::default(),
@ -936,7 +936,7 @@ impl Render for ProjectDiffToolbar {
h_group_sm()
.when(button_states.selection, |el| {
el.child(
Button::new("stage", "Toggle Staged")
Button::new("stage", "Toggle Staged", cx)
.tooltip(Tooltip::for_action_title_in(
"Toggle Staged",
&ToggleStaged,
@ -950,7 +950,7 @@ impl Render for ProjectDiffToolbar {
})
.when(!button_states.selection, |el| {
el.child(
Button::new("stage", "Stage")
Button::new("stage", "Stage", cx)
.tooltip(Tooltip::for_action_title_in(
"Stage and go to next hunk",
&StageAndNext,
@ -961,7 +961,7 @@ impl Render for ProjectDiffToolbar {
})),
)
.child(
Button::new("unstage", "Unstage")
Button::new("unstage", "Unstage", cx)
.tooltip(Tooltip::for_action_title_in(
"Unstage and go to next hunk",
&UnstageAndNext,
@ -1011,7 +1011,7 @@ impl Render for ProjectDiffToolbar {
button_states.unstage_all && !button_states.stage_all,
|el| {
el.child(
Button::new("unstage-all", "Unstage All")
Button::new("unstage-all", "Unstage All", cx)
.tooltip(Tooltip::for_action_title_in(
"Unstage all changes",
&UnstageAll,
@ -1030,7 +1030,7 @@ impl Render for ProjectDiffToolbar {
// todo make it so that changing to say "Unstaged"
// doesn't change the position.
div().child(
Button::new("stage-all", "Stage All")
Button::new("stage-all", "Stage All", cx)
.disabled(!button_states.stage_all)
.tooltip(Tooltip::for_action_title_in(
"Stage all changes",
@ -1045,7 +1045,7 @@ impl Render for ProjectDiffToolbar {
},
)
.child(
Button::new("commit", "Commit")
Button::new("commit", "Commit", cx)
.tooltip(Tooltip::for_action_title_in(
"Commit",
&Commit,

View file

@ -219,7 +219,7 @@ impl Render for CursorPosition {
let context = self.context.clone();
el.child(
Button::new("go-to-line-column", text)
Button::new("go-to-line-column", text, cx)
.label_size(LabelSize::Small)
.on_click(cx.listener(|this, _, window, cx| {
if let Some(workspace) = this.workspace.upgrade() {

View file

@ -78,7 +78,7 @@ impl Render for ImageInfo {
);
div().child(
Button::new("image-metadata", components.join("")).label_size(LabelSize::Small),
Button::new("image-metadata", components.join(""), cx).label_size(LabelSize::Small),
)
}
}

View file

@ -1017,13 +1017,14 @@ impl Render for ConfigurationView {
List::new()
.child(
InstructionListItem::new(
cx,
"Create one by visiting",
Some("Anthropic's settings"),
Some("https://console.anthropic.com/settings/keys")
)
)
.child(
InstructionListItem::text_only("Paste your API key below and hit enter to start using the assistant")
InstructionListItem::text_only(cx, "Paste your API key below and hit enter to start using the assistant")
)
)
.child(
@ -1066,7 +1067,7 @@ impl Render for ConfigurationView {
})),
)
.child(
Button::new("reset-key", "Reset Key")
Button::new("reset-key", "Reset Key", cx)
.label_size(LabelSize::Small)
.icon(Some(IconName::Trash))
.icon_size(IconSize::Small)

View file

@ -1232,7 +1232,7 @@ impl Render for ConfigurationView {
})),
)
.child(
Button::new("reset-key", "Reset Key")
Button::new("reset-key", "Reset Key", cx)
.icon(Some(IconName::Trash))
.icon_size(IconSize::Small)
.icon_position(IconPosition::Start)
@ -1257,6 +1257,7 @@ impl Render for ConfigurationView {
List::new()
.child(
InstructionListItem::new(
cx,
"Grant permissions to the strategy you'll use according to the:",
Some("Prerequisites"),
Some("https://docs.aws.amazon.com/bedrock/latest/userguide/inference-prereq.html"),
@ -1264,6 +1265,7 @@ impl Render for ConfigurationView {
)
.child(
InstructionListItem::new(
cx,
"Select the models you would like access to:",
Some("Bedrock Model Catalog"),
Some("https://us-east-1.console.aws.amazon.com/bedrock/home?region=us-east-1#/modelaccess"),
@ -1365,19 +1367,23 @@ impl ConfigurationView {
.child(
List::new()
.child(InstructionListItem::new(
cx,
"Create an IAM user in the AWS console with programmatic access",
Some("IAM Console"),
Some("https://us-east-1.console.aws.amazon.com/iam/home?region=us-east-1#/users"),
))
.child(InstructionListItem::new(
cx,
"Attach the necessary Bedrock permissions to this ",
Some("user"),
Some("https://docs.aws.amazon.com/bedrock/latest/userguide/inference-prereq.html"),
))
.child(InstructionListItem::text_only(
cx,
"Copy the access key ID and secret access key when provided",
))
.child(InstructionListItem::text_only(
cx,
"Enter these credentials below",
)),
)

View file

@ -410,12 +410,17 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
return None;
}
Some(
render_accept_terms(view, state.accept_terms_of_service_task.is_some(), {
let state = self.state.clone();
move |_window, cx| {
state.update(cx, |state, cx| state.accept_terms_of_service(cx));
}
})
render_accept_terms(
view,
state.accept_terms_of_service_task.is_some(),
{
let state = self.state.clone();
move |_window, cx| {
state.update(cx, |state, cx| state.accept_terms_of_service(cx));
}
},
cx,
)
.into_any_element(),
)
}
@ -429,11 +434,12 @@ fn render_accept_terms(
view_kind: LanguageModelProviderTosView,
accept_terms_of_service_in_progress: bool,
accept_terms_callback: impl Fn(&mut Window, &mut App) + 'static,
app: &App,
) -> impl IntoElement {
let thread_fresh_start = matches!(view_kind, LanguageModelProviderTosView::ThreadFreshStart);
let thread_empty_state = matches!(view_kind, LanguageModelProviderTosView::ThreadEmptyState);
let terms_button = Button::new("terms_of_service", "Terms of Service")
let terms_button = Button::new("terms_of_service", "Terms of Service", app)
.style(ButtonStyle::Subtle)
.icon(IconName::ArrowUpRight)
.icon_color(Color::Muted)
@ -442,7 +448,7 @@ fn render_accept_terms(
.on_click(move |_, _window, cx| cx.open_url("https://zed.dev/terms-of-service"));
let button_container = h_flex().child(
Button::new("accept_terms", "I accept the Terms of Service")
Button::new("accept_terms", "I accept the Terms of Service", app)
.when(!thread_empty_state, |this| {
this.full_width()
.style(ButtonStyle::Tinted(TintColor::Accent))
@ -1135,19 +1141,19 @@ impl RenderOnce for ZedAiConfiguration {
};
let manage_subscription_buttons = if is_pro {
Button::new("manage_settings", "Manage Subscription")
Button::new("manage_settings", "Manage Subscription", _cx)
.full_width()
.style(ButtonStyle::Tinted(TintColor::Accent))
.on_click(|_, _, cx| cx.open_url(&zed_urls::account_url(cx)))
.into_any_element()
} else if self.plan.is_none() || self.eligible_for_trial {
Button::new("start_trial", "Start 14-day Free Pro Trial")
Button::new("start_trial", "Start 14-day Free Pro Trial", _cx)
.full_width()
.style(ui::ButtonStyle::Tinted(ui::TintColor::Accent))
.on_click(|_, _, cx| cx.open_url(&zed_urls::start_trial_url(cx)))
.into_any_element()
} else {
Button::new("upgrade", "Upgrade to Pro")
Button::new("upgrade", "Upgrade to Pro", _cx)
.full_width()
.style(ui::ButtonStyle::Tinted(ui::TintColor::Accent))
.on_click(|_, _, cx| cx.open_url(&zed_urls::upgrade_to_zed_pro_url(cx)))
@ -1159,7 +1165,7 @@ impl RenderOnce for ZedAiConfiguration {
.gap_2()
.child(Label::new("Sign in to have access to Zed's complete agentic experience with hosted models."))
.child(
Button::new("sign_in", "Sign In to use Zed AI")
Button::new("sign_in", "Sign In to use Zed AI", _cx)
.icon_color(Color::Muted)
.icon(IconName::Github)
.icon_size(IconSize::Small)
@ -1183,12 +1189,13 @@ impl RenderOnce for ZedAiConfiguration {
let callback = self.accept_terms_of_service_callback.clone();
move |window, cx| (callback)(window, cx)
},
_cx,
))
})
.map(|this| {
if self.has_accepted_terms_of_service && self.account_too_young {
this.child(young_account_banner).child(
Button::new("upgrade", "Upgrade to Pro")
Button::new("upgrade", "Upgrade to Pro", _cx)
.style(ui::ButtonStyle::Tinted(ui::TintColor::Accent))
.full_width()
.on_click(|_, _, cx| {

View file

@ -670,7 +670,7 @@ impl Render for ConfigurationView {
.child(Label::new("Authorized")),
)
.child(
Button::new("sign_out", "Sign Out")
Button::new("sign_out", "Sign Out", cx)
.label_size(LabelSize::Small)
.on_click(|_, window, cx| {
window.dispatch_action(copilot::SignOut.boxed_clone(), cx);
@ -709,7 +709,7 @@ impl Render for ConfigurationView {
const LABEL: &str = "To use Zed's agent with GitHub Copilot, you need to be logged in to GitHub. Note that your GitHub account must have an active Copilot Chat subscription.";
v_flex().gap_2().child(Label::new(LABEL)).child(
Button::new("sign_in", "Sign in to use GitHub Copilot")
Button::new("sign_in", "Sign in to use GitHub Copilot", cx)
.icon_color(Color::Muted)
.icon(IconName::Github)
.icon_position(IconPosition::Start)

View file

@ -679,11 +679,13 @@ impl Render for ConfigurationView {
.child(
List::new()
.child(InstructionListItem::new(
cx,
"Get your API key from the",
Some("DeepSeek console"),
Some("https://platform.deepseek.com/api_keys"),
))
.child(InstructionListItem::text_only(
cx,
"Paste your API key below and hit enter to start using the assistant",
)),
)
@ -728,7 +730,7 @@ impl Render for ConfigurationView {
})),
)
.child(
Button::new("reset-key", "Reset Key")
Button::new("reset-key", "Reset Key", cx)
.label_size(LabelSize::Small)
.icon(Some(IconName::Trash))
.icon_size(IconSize::Small)

View file

@ -884,11 +884,13 @@ impl Render for ConfigurationView {
.child(
List::new()
.child(InstructionListItem::new(
cx,
"Create one by visiting",
Some("Google AI's console"),
Some("https://aistudio.google.com/app/apikey"),
))
.child(InstructionListItem::text_only(
cx,
"Paste your API key below and hit enter to start using the assistant",
)),
)
@ -931,7 +933,7 @@ impl Render for ConfigurationView {
})),
)
.child(
Button::new("reset-key", "Reset Key")
Button::new("reset-key", "Reset Key", cx)
.label_size(LabelSize::Small)
.icon(Some(IconName::Trash))
.icon_size(IconSize::Small)

View file

@ -668,9 +668,11 @@ impl Render for ConfigurationView {
v_flex().gap_1().child(Label::new(lmstudio_intro)).child(
List::new()
.child(InstructionListItem::text_only(
cx,
"LM Studio needs to be running with at least one model downloaded.",
))
.child(InstructionListItem::text_only(
cx,
"To get your first model, try running `lms get qwen2.5-coder-7b`",
)),
),
@ -687,7 +689,7 @@ impl Render for ConfigurationView {
.map(|this| {
if is_authenticated {
this.child(
Button::new("lmstudio-site", "LM Studio")
Button::new("lmstudio-site", "LM Studio", cx)
.style(ButtonStyle::Subtle)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Small)
@ -702,6 +704,7 @@ impl Render for ConfigurationView {
Button::new(
"download_lmstudio_button",
"Download LM Studio",
cx,
)
.style(ButtonStyle::Subtle)
.icon(IconName::ArrowUpRight)
@ -715,7 +718,7 @@ impl Render for ConfigurationView {
}
})
.child(
Button::new("view-models", "Model Catalog")
Button::new("view-models", "Model Catalog", cx)
.style(ButtonStyle::Subtle)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Small)
@ -741,7 +744,7 @@ impl Render for ConfigurationView {
)
} else {
this.child(
Button::new("retry_lmstudio_models", "Connect")
Button::new("retry_lmstudio_models", "Connect", cx)
.icon_position(IconPosition::Start)
.icon_size(IconSize::XSmall)
.icon(IconName::PlayFilled)

View file

@ -848,14 +848,17 @@ impl Render for ConfigurationView {
.child(
List::new()
.child(InstructionListItem::new(
cx,
"Create one by visiting",
Some("Mistral's console"),
Some("https://console.mistral.ai/api-keys"),
))
.child(InstructionListItem::text_only(
cx,
"Ensure your Mistral account has credits",
))
.child(InstructionListItem::text_only(
cx,
"Paste your API key below and hit enter to start using the assistant",
)),
)
@ -898,7 +901,7 @@ impl Render for ConfigurationView {
})),
)
.child(
Button::new("reset-key", "Reset Key")
Button::new("reset-key", "Reset Key", cx)
.label_size(LabelSize::Small)
.icon(Some(IconName::Trash))
.icon_size(IconSize::Small)

View file

@ -587,8 +587,9 @@ impl Render for ConfigurationView {
.child(
v_flex().gap_1().child(Label::new(ollama_intro)).child(
List::new()
.child(InstructionListItem::text_only("Ollama must be running with at least one model installed to use it in the assistant."))
.child(InstructionListItem::text_only(cx, "Ollama must be running with at least one model installed to use it in the assistant."))
.child(InstructionListItem::text_only(
cx,
"Once installed, try `ollama run llama3.2`",
)),
),
@ -605,7 +606,7 @@ impl Render for ConfigurationView {
.map(|this| {
if is_authenticated {
this.child(
Button::new("ollama-site", "Ollama")
Button::new("ollama-site", "Ollama", cx)
.style(ButtonStyle::Subtle)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Small)
@ -618,6 +619,7 @@ impl Render for ConfigurationView {
Button::new(
"download_ollama_button",
"Download Ollama",
cx,
)
.style(ButtonStyle::Subtle)
.icon(IconName::ArrowUpRight)
@ -631,7 +633,7 @@ impl Render for ConfigurationView {
}
})
.child(
Button::new("view-models", "View All Models")
Button::new("view-models", "View All Models", cx)
.style(ButtonStyle::Subtle)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Small)
@ -655,7 +657,7 @@ impl Render for ConfigurationView {
)
} else {
this.child(
Button::new("retry_ollama_models", "Connect")
Button::new("retry_ollama_models", "Connect", cx)
.icon_position(IconPosition::Start)
.icon_size(IconSize::XSmall)
.icon(IconName::PlayFilled)

View file

@ -812,14 +812,17 @@ impl Render for ConfigurationView {
.child(
List::new()
.child(InstructionListItem::new(
cx,
"Create one by visiting",
Some("OpenAI's console"),
Some("https://platform.openai.com/api-keys"),
))
.child(InstructionListItem::text_only(
cx,
"Ensure your OpenAI account has credits",
))
.child(InstructionListItem::text_only(
cx,
"Paste your API key below and hit enter to start using the assistant",
)),
)
@ -857,7 +860,7 @@ impl Render for ConfigurationView {
})),
)
.child(
Button::new("reset-api-key", "Reset API Key")
Button::new("reset-api-key", "Reset API Key",cx)
.label_size(LabelSize::Small)
.icon(IconName::Undo)
.icon_size(IconSize::Small)
@ -891,7 +894,7 @@ impl Render for ConfigurationView {
.child(Label::new("Zed also supports OpenAI-compatible models.")),
)
.child(
Button::new("docs", "Learn More")
Button::new("docs", "Learn More", cx)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)

View file

@ -505,7 +505,7 @@ impl Render for ConfigurationView {
})),
)
.child(
Button::new("reset-api-key", "Reset API Key")
Button::new("reset-api-key", "Reset API Key",cx)
.label_size(LabelSize::Small)
.icon(IconName::Undo)
.icon_size(IconSize::Small)

View file

@ -859,14 +859,17 @@ impl Render for ConfigurationView {
.child(
List::new()
.child(InstructionListItem::new(
cx,
"Create an API key by visiting",
Some("OpenRouter's console"),
Some("https://openrouter.ai/keys"),
))
.child(InstructionListItem::text_only(
cx,
"Ensure your OpenRouter account has credits",
))
.child(InstructionListItem::text_only(
cx,
"Paste your API key below and hit enter to start using the assistant",
)),
)
@ -909,7 +912,7 @@ impl Render for ConfigurationView {
})),
)
.child(
Button::new("reset-key", "Reset Key")
Button::new("reset-key", "Reset Key",cx)
.label_size(LabelSize::Small)
.icon(Some(IconName::Trash))
.icon_size(IconSize::Small)

View file

@ -517,11 +517,13 @@ impl Render for ConfigurationView {
.child(
List::new()
.child(InstructionListItem::new(
cx,
"Create one by visiting",
Some("Vercel v0's console"),
Some("https://v0.dev/chat/settings/keys"),
))
.child(InstructionListItem::text_only(
cx,
"Paste your API key below and hit enter to start using the agent",
)),
)
@ -559,7 +561,7 @@ impl Render for ConfigurationView {
})),
)
.child(
Button::new("reset-api-key", "Reset API Key")
Button::new("reset-api-key", "Reset API Key",cx)
.label_size(LabelSize::Small)
.icon(IconName::Undo)
.icon_size(IconSize::Small)

View file

@ -507,11 +507,13 @@ impl Render for ConfigurationView {
.child(
List::new()
.child(InstructionListItem::new(
cx,
"Create one by visiting",
Some("xAI console"),
Some("https://console.x.ai/team/default/api-keys"),
))
.child(InstructionListItem::text_only(
cx,
"Paste your API key below and hit enter to start using the agent",
)),
)
@ -549,7 +551,7 @@ impl Render for ConfigurationView {
})),
)
.child(
Button::new("reset-api-key", "Reset API Key")
Button::new("reset-api-key", "Reset API Key",cx)
.label_size(LabelSize::Small)
.icon(IconName::Undo)
.icon_size(IconSize::Small)

View file

@ -2,27 +2,31 @@ use gpui::{AnyElement, IntoElement, ParentElement, SharedString};
use ui::{ListItem, prelude::*};
/// A reusable list item component for adding LLM provider configuration instructions
pub struct InstructionListItem {
pub struct InstructionListItem<'app> {
app: &'app App,
label: SharedString,
button_label: Option<SharedString>,
button_link: Option<String>,
}
impl InstructionListItem {
impl<'app> InstructionListItem<'app> {
pub fn new(
app: &'app App,
label: impl Into<SharedString>,
button_label: Option<impl Into<SharedString>>,
button_link: Option<impl Into<String>>,
) -> Self {
Self {
app,
label: label.into(),
button_label: button_label.map(|l| l.into()),
button_link: button_link.map(|l| l.into()),
}
}
pub fn text_only(label: impl Into<SharedString>) -> Self {
pub fn text_only(app: &'app App, label: impl Into<SharedString>) -> Self {
Self {
app,
label: label.into(),
button_label: None,
button_link: None,
@ -30,7 +34,7 @@ impl InstructionListItem {
}
}
impl IntoElement for InstructionListItem {
impl IntoElement for InstructionListItem<'_> {
type Element = AnyElement;
fn into_element(self) -> Self::Element {
@ -44,7 +48,7 @@ impl IntoElement for InstructionListItem {
.flex_wrap()
.child(Label::new(self.label))
.child(
Button::new(unique_id, button_label)
Button::new(unique_id, button_label, self.app)
.style(ButtonStyle::Subtle)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Small)

View file

@ -55,7 +55,7 @@ impl Render for ActiveBufferLanguage {
};
el.child(
Button::new("change-language", active_language_text)
Button::new("change-language", active_language_text, cx)
.label_size(LabelSize::Small)
.on_click(cx.listener(|this, _, window, cx| {
if let Some(workspace) = this.workspace.upgrade() {

View file

@ -206,12 +206,12 @@ impl Render for KeyContextView {
.mt_4()
.gap_4()
.child(
Button::new("open_documentation", "Open Documentation")
Button::new("open_documentation", "Open Documentation", cx)
.style(ButtonStyle::Filled)
.on_click(|_, _, cx| cx.open_url("https://zed.dev/docs/key-bindings")),
)
.child(
Button::new("view_default_keymap", "View default keymap")
Button::new("view_default_keymap", "View default keymap", cx)
.style(ButtonStyle::Filled)
.key_binding(ui::KeyBinding::for_action(
&zed_actions::OpenDefaultKeymap,
@ -223,7 +223,7 @@ impl Render for KeyContextView {
}),
)
.child(
Button::new("edit_your_keymap", "Edit your keymap")
Button::new("edit_your_keymap", "Edit your keymap", cx)
.style(ButtonStyle::Filled)
.key_binding(ui::KeyBinding::for_action(&zed_actions::OpenKeymap, window, cx))
.on_click(|_, window, cx| {

View file

@ -1374,6 +1374,7 @@ impl Render for LspLogToolbarItemView {
))
})
.unwrap_or_else(|| "No server selected".into()),
cx,
)
.icon(IconName::ChevronDown)
.icon_size(IconSize::Small)
@ -1431,10 +1432,14 @@ impl Render for LspLogToolbarItemView {
PopoverMenu::new("LspViewSelector")
.anchor(Corner::TopLeft)
.trigger(
Button::new("language_server_menu_header", server.selected_entry.label())
.icon(IconName::ChevronDown)
.icon_size(IconSize::Small)
.icon_color(Color::Muted),
Button::new(
"language_server_menu_header",
server.selected_entry.label(),
cx,
)
.icon(IconName::ChevronDown)
.icon_size(IconSize::Small)
.icon_color(Color::Muted),
)
.menu(move |window, cx| {
let log_toolbar_view = log_toolbar_view.clone();
@ -1516,22 +1521,26 @@ impl Render for LspLogToolbarItemView {
.gap_0p5()
.child(lsp_menu)
.children(view_selector)
.child(
.child({
let trace_button =
Button::new("language_server_trace_level_selector", "Trace level", cx)
.icon(IconName::ChevronDown)
.icon_size(IconSize::Small)
.icon_color(Color::Muted);
let log_button =
Button::new("language_server_log_level_selector", "Log level", cx)
.icon(IconName::ChevronDown)
.icon_size(IconSize::Small)
.icon_color(Color::Muted);
log_view.update(cx, |this, _cx| match this.active_entry_kind {
LogKind::Trace => {
let log_view = log_view.clone();
div().child(
PopoverMenu::new("lsp-trace-level-menu")
.anchor(Corner::TopLeft)
.trigger(
Button::new(
"language_server_trace_level_selector",
"Trace level",
)
.icon(IconName::ChevronDown)
.icon_size(IconSize::Small)
.icon_color(Color::Muted),
)
.trigger(trace_button)
.menu({
let log_view = log_view.clone();
@ -1591,15 +1600,7 @@ impl Render for LspLogToolbarItemView {
div().child(
PopoverMenu::new("lsp-log-level-menu")
.anchor(Corner::TopLeft)
.trigger(
Button::new(
"language_server_log_level_selector",
"Log level",
)
.icon(IconName::ChevronDown)
.icon_size(IconSize::Small)
.icon_color(Color::Muted),
)
.trigger(log_button)
.menu({
let log_view = log_view.clone();
@ -1656,11 +1657,11 @@ impl Render for LspLogToolbarItemView {
)
}
_ => div(),
}),
),
})
}),
)
.child(
Button::new("clear_log_button", "Clear").on_click(cx.listener(
Button::new("clear_log_button", "Clear", cx).on_click(cx.listener(
|this, _, window, cx| {
if let Some(log_view) = this.log_view.as_ref() {
log_view.update(cx, |log_view, cx| {

View file

@ -122,7 +122,7 @@ impl Render for StatusToast {
.child(Label::new(self.text.clone()).color(Color::Default))
.when_some(self.action.as_ref(), |this, action| {
this.child(
Button::new(action.id.clone(), action.label.clone())
Button::new(action.id.clone(), action.label.clone(), cx)
.tooltip(Tooltip::for_action_title(
action.label.clone(),
&toast::RunAction,

View file

@ -80,7 +80,7 @@ fn render_privacy_card(tab_index: &mut isize, disabled: bool, cx: &mut App) -> i
.tooltip(move |_, cx| cx.new(|_| AiPrivacyTooltip::new()).into()),
)
.child(
Button::new("learn_more", "Learn More")
Button::new("learn_more", "Learn More", cx)
.style(ButtonStyle::Outlined)
.label_size(LabelSize::Small)
.icon(IconName::ArrowUpRight)
@ -206,7 +206,7 @@ fn render_llm_provider_card(
))
.child(Divider::horizontal())
.child(
Button::new("agent_settings", "Add Many Others")
Button::new("agent_settings", "Add Many Others", cx)
.size(ButtonSize::Large)
.icon(IconName::Plus)
.icon_position(IconPosition::Start)
@ -363,7 +363,7 @@ impl Render for AiConfigurationModal {
.section(Section::new().child(self.configuration_view.clone()))
.footer(
ModalFooter::new().end_slot(
Button::new("ai-onb-modal-Done", "Done")
Button::new("ai-onb-modal-Done", "Done", cx)
.key_binding(
KeyBinding::for_action_in(
&menu::Cancel,

View file

@ -458,7 +458,7 @@ impl Onboarding {
)
.into_any_element()
} else {
Button::new("sign_in", "Sign In")
Button::new("sign_in", "Sign In", cx)
.full_width()
.style(ButtonStyle::Outlined)
.size(ButtonSize::Medium)

View file

@ -235,7 +235,7 @@ impl Render for WelcomePage {
.border_color(cx.theme().colors().border.opacity(0.6))
.border_dashed()
.child(
Button::new("welcome-exit", "Return to Setup")
Button::new("welcome-exit", "Return to Setup", cx)
.tab_index(last_index as isize)
.full_width()
.label_size(LabelSize::XSmall)

View file

@ -50,10 +50,10 @@ impl RenderOnce for PanelTab {
}
}
pub fn panel_button(label: impl Into<SharedString>) -> ui::Button {
pub fn panel_button(label: impl Into<SharedString>, app: &App) -> ui::Button {
let label = label.into();
let id = ElementId::Name(label.clone().to_lowercase().replace(' ', "_").into());
ui::Button::new(id, label)
ui::Button::new(id, label, app)
.label_size(ui::LabelSize::Small)
.icon_size(ui::IconSize::Small)
// TODO: Change this once we use on_surface_bg in button_like
@ -61,8 +61,8 @@ pub fn panel_button(label: impl Into<SharedString>) -> ui::Button {
.size(ui::ButtonSize::Compact)
}
pub fn panel_filled_button(label: impl Into<SharedString>) -> ui::Button {
panel_button(label).style(ui::ButtonStyle::Filled)
pub fn panel_filled_button(label: impl Into<SharedString>, app: &App) -> ui::Button {
panel_button(label, app).style(ui::ButtonStyle::Filled)
}
pub fn panel_icon_button(id: impl Into<SharedString>, icon: IconName) -> ui::IconButton {

View file

@ -5531,7 +5531,7 @@ impl Render for ProjectPanel {
.p_4()
.track_focus(&self.focus_handle(cx))
.child(
Button::new("open_project", "Open a project")
Button::new("open_project", "Open a project", cx)
.full_width()
.key_binding(KeyBinding::for_action_in(
&OpenRecent::default(),

View file

@ -185,7 +185,7 @@ impl Render for DisconnectedOverlay {
h_flex()
.gap_2()
.child(
Button::new("close-window", "Close Window")
Button::new("close-window", "Close Window", cx)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.on_click(cx.listener(move |_, _, window, _| {
@ -194,7 +194,7 @@ impl Render for DisconnectedOverlay {
)
.when(can_reconnect, |el| {
el.child(
Button::new("reconnect", "Reconnect")
Button::new("reconnect", "Reconnect", cx)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ModalSurface)
.icon(IconName::ArrowCircle)

View file

@ -479,7 +479,7 @@ impl PickerDelegate for RecentProjectsDelegate {
.border_t_1()
.border_color(cx.theme().colors().border_variant)
.child(
Button::new("remote", "Open Remote Folder")
Button::new("remote", "Open Remote Folder", cx)
.key_binding(KeyBinding::for_action(
&OpenRemote {
from_existing_connection: false,
@ -500,7 +500,7 @@ impl PickerDelegate for RecentProjectsDelegate {
}),
)
.child(
Button::new("local", "Open Local Folder")
Button::new("local", "Open Local Folder", cx)
.key_binding(KeyBinding::for_action(&workspace::Open, window, cx))
.on_click(|_, window, cx| {
window.dispatch_action(workspace::Open.boxed_clone(), cx)

View file

@ -1094,7 +1094,7 @@ impl RemoteServerProjects {
.size(LabelSize::Small),
)
.child(
Button::new("learn-more", "Learn more…")
Button::new("learn-more", "Learn more…", cx)
.label_size(LabelSize::Small)
.size(ButtonSize::None)
.color(Color::Accent)

View file

@ -234,7 +234,7 @@ impl PickerDelegate for KernelPickerDelegate {
.p_1()
.gap_4()
.child(
Button::new("kernel-docs", "Kernel Docs")
Button::new("kernel-docs", "Kernel Docs", cx)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)

View file

@ -655,7 +655,7 @@ impl Render for Session {
.as_ref()
.map(|info| info.language_info.name.clone()),
Some(
Button::new("interrupt", "Interrupt")
Button::new("interrupt", "Interrupt", cx)
.style(ButtonStyle::Subtle)
.on_click(cx.listener(move |session, _, _, cx| {
session.interrupt(cx);
@ -684,7 +684,7 @@ impl Render for Session {
.child(Label::new(self.kernel_specification.name()))
.children(status_text.map(|status_text| Label::new(format!("({status_text})"))))
.button(
Button::new("shutdown", "Shutdown")
Button::new("shutdown", "Shutdown", cx)
.style(ButtonStyle::Subtle)
.disabled(self.kernel.is_shutting_down())
.on_click(cx.listener(move |session, _, window, cx| {

View file

@ -1318,19 +1318,21 @@ impl Render for RulesLibrary {
"Create your first rule:",
))
.child(
Button::new("create-rule", "New Rule")
.full_width()
.key_binding(
KeyBinding::for_action(
&NewRule, window, cx,
),
Button::new(
"create-rule",
"New Rule",
cx,
)
.full_width()
.key_binding(KeyBinding::for_action(
&NewRule, window, cx,
))
.on_click(|_, window, cx| {
window.dispatch_action(
NewRule.boxed_clone(),
cx,
)
.on_click(|_, window, cx| {
window.dispatch_action(
NewRule.boxed_clone(),
cx,
)
}),
}),
),
)
.child(h_flex()),

View file

@ -3,20 +3,23 @@ mod registrar;
use crate::{
FocusSearch, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext, SearchOptions,
SelectAllMatches, SelectNextMatch, SelectPreviousMatch, ToggleCaseSensitive, ToggleRegex,
ToggleReplace, ToggleSelection, ToggleWholeWord, search_bar::render_nav_button,
ToggleReplace, ToggleSelection, ToggleWholeWord,
search_bar::{
input_base_styles, render_action_button, render_text_input, toggle_replace_button,
},
};
use any_vec::AnyVec;
use anyhow::Context as _;
use collections::HashMap;
use editor::{
DisplayPoint, Editor, EditorElement, EditorSettings, EditorStyle,
DisplayPoint, Editor, EditorSettings,
actions::{Backtab, Tab},
};
use futures::channel::oneshot;
use gpui::{
Action, App, ClickEvent, Context, Entity, EventEmitter, FocusHandle, Focusable,
InteractiveElement as _, IntoElement, KeyContext, ParentElement as _, Render, ScrollHandle,
Styled, Subscription, Task, TextStyle, Window, actions, div,
Action, App, ClickEvent, Context, Entity, EventEmitter, Focusable, InteractiveElement as _,
IntoElement, KeyContext, ParentElement as _, Render, ScrollHandle, Styled, Subscription, Task,
Window, actions, div,
};
use language::{Language, LanguageRegistry};
use project::{
@ -27,7 +30,6 @@ use schemars::JsonSchema;
use serde::Deserialize;
use settings::Settings;
use std::sync::Arc;
use theme::ThemeSettings;
use zed_actions::outline::ToggleOutline;
use ui::{
@ -125,46 +127,6 @@ pub struct BufferSearchBar {
}
impl BufferSearchBar {
fn render_text_input(
&self,
editor: &Entity<Editor>,
color_override: Option<Color>,
cx: &mut Context<Self>,
) -> impl IntoElement {
let (color, use_syntax) = if editor.read(cx).read_only(cx) {
(cx.theme().colors().text_disabled, false)
} else {
match color_override {
Some(color_override) => (color_override.color(cx), false),
None => (cx.theme().colors().text, true),
}
};
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color,
font_family: settings.buffer_font.family.clone(),
font_features: settings.buffer_font.features.clone(),
font_fallbacks: settings.buffer_font.fallbacks.clone(),
font_size: rems(0.875).into(),
font_weight: settings.buffer_font.weight,
line_height: relative(1.3),
..TextStyle::default()
};
let mut editor_style = EditorStyle {
background: cx.theme().colors().toolbar_background,
local_player: cx.theme().players().local(),
text: text_style,
..EditorStyle::default()
};
if use_syntax {
editor_style.syntax = cx.theme().syntax().clone();
}
EditorElement::new(editor, editor_style)
}
pub fn query_editor_focused(&self) -> bool {
self.query_editor_focused
}
@ -185,7 +147,14 @@ impl Render for BufferSearchBar {
let hide_inline_icons = self.editor_needed_width
> self.editor_scroll_handle.bounds().size.width - window.rem_size() * 6.;
let supported_options = self.supported_options(cx);
let workspace::searchable::SearchOptions {
case,
word,
regex,
replacement,
selection,
find_in_results,
} = self.supported_options(cx);
if self.query_editor.update(cx, |query_editor, _cx| {
query_editor.placeholder_text().is_none()
@ -220,268 +189,205 @@ impl Render for BufferSearchBar {
}
})
.unwrap_or_else(|| "0/0".to_string());
let should_show_replace_input = self.replace_enabled && supported_options.replacement;
let should_show_replace_input = self.replace_enabled && replacement;
let in_replace = self.replacement_editor.focus_handle(cx).is_focused(window);
let theme_colors = cx.theme().colors();
let query_border = if self.query_error.is_some() {
Color::Error.color(cx)
} else {
theme_colors.border
};
let replacement_border = theme_colors.border;
let container_width = window.viewport_size().width;
let input_width = SearchInputWidth::calc_width(container_width);
let input_base_styles =
|border_color| input_base_styles(border_color, |div| div.w(input_width));
let query_column = input_base_styles(query_border)
.id("editor-scroll")
.track_scroll(&self.editor_scroll_handle)
.child(render_text_input(&self.query_editor, color_override, cx))
.when(!hide_inline_icons, |div| {
div.child(
h_flex()
.gap_1()
.when(case, |div| {
div.child(SearchOptions::CASE_SENSITIVE.as_button(
self.search_options.contains(SearchOptions::CASE_SENSITIVE),
focus_handle.clone(),
cx.listener(|this, _, window, cx| {
this.toggle_case_sensitive(&ToggleCaseSensitive, window, cx)
}),
))
})
.when(word, |div| {
div.child(SearchOptions::WHOLE_WORD.as_button(
self.search_options.contains(SearchOptions::WHOLE_WORD),
focus_handle.clone(),
cx.listener(|this, _, window, cx| {
this.toggle_whole_word(&ToggleWholeWord, window, cx)
}),
))
})
.when(regex, |div| {
div.child(SearchOptions::REGEX.as_button(
self.search_options.contains(SearchOptions::REGEX),
focus_handle.clone(),
cx.listener(|this, _, window, cx| {
this.toggle_regex(&ToggleRegex, window, cx)
}),
))
}),
)
});
let mode_column = h_flex()
.gap_1()
.min_w_64()
.when(replacement, |this| {
this.child(toggle_replace_button(
"buffer-search-bar-toggle-replace-button",
focus_handle.clone(),
self.replace_enabled,
cx.listener(|this, _: &ClickEvent, window, cx| {
this.toggle_replace(&ToggleReplace, window, cx);
}),
))
})
.when(selection, |this| {
this.child(
IconButton::new(
"buffer-search-bar-toggle-search-selection-button",
IconName::Quote,
)
.style(ButtonStyle::Subtle)
.shape(IconButtonShape::Square)
.when(self.selection_search_enabled, |button| {
button.style(ButtonStyle::Filled)
})
.on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
this.toggle_selection(&ToggleSelection, window, cx);
}))
.toggle_state(self.selection_search_enabled)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Toggle Search Selection",
&ToggleSelection,
&focus_handle,
window,
cx,
)
}
}),
)
})
.when(!find_in_results, |el| {
let query_focus = self.query_editor.focus_handle(cx);
let matches_column = h_flex()
.pl_2()
.ml_2()
.border_l_1()
.border_color(theme_colors.border_variant)
.child(render_action_button(
"buffer-search-nav-button",
ui::IconName::ChevronLeft,
self.active_match_index.is_some(),
"Select Previous Match",
&SelectPreviousMatch,
query_focus.clone(),
))
.child(render_action_button(
"buffer-search-nav-button",
ui::IconName::ChevronRight,
self.active_match_index.is_some(),
"Select Next Match",
&SelectNextMatch,
query_focus.clone(),
))
.when(!narrow_mode, |this| {
this.child(div().ml_2().min_w(rems_from_px(40.)).child(
Label::new(match_text).size(LabelSize::Small).color(
if self.active_match_index.is_some() {
Color::Default
} else {
Color::Disabled
},
),
))
});
el.child(render_action_button(
"buffer-search-nav-button",
IconName::SelectAll,
true,
"Select All Matches",
&SelectAllMatches,
query_focus,
))
.child(matches_column)
})
.when(find_in_results, |el| {
el.child(render_action_button(
"buffer-search",
IconName::Close,
true,
"Close Search Bar",
&Dismiss,
focus_handle.clone(),
))
});
let search_line = h_flex()
.w_full()
.gap_2()
.when(find_in_results, |el| {
el.child(Label::new("Find in results").color(Color::Hint))
})
.child(query_column)
.child(mode_column);
let replace_line =
should_show_replace_input.then(|| {
let replace_column = input_base_styles(replacement_border)
.child(render_text_input(&self.replacement_editor, None, cx));
let focus_handle = self.replacement_editor.read(cx).focus_handle(cx);
let replace_actions = h_flex()
.min_w_64()
.gap_1()
.child(render_action_button(
"buffer-search-replace-button",
IconName::ReplaceNext,
true,
"Replace Next Match",
&ReplaceNext,
focus_handle.clone(),
))
.child(render_action_button(
"buffer-search-replace-button",
IconName::ReplaceAll,
true,
"Replace All Matches",
&ReplaceAll,
focus_handle,
));
h_flex()
.w_full()
.gap_2()
.child(replace_column)
.child(replace_actions)
});
let mut key_context = KeyContext::new_with_defaults();
key_context.add("BufferSearchBar");
if in_replace {
key_context.add("in_replace");
}
let query_border = if self.query_error.is_some() {
Color::Error.color(cx)
} else {
cx.theme().colors().border
};
let replacement_border = cx.theme().colors().border;
let container_width = window.viewport_size().width;
let input_width = SearchInputWidth::calc_width(container_width);
let input_base_styles = |border_color| {
h_flex()
.min_w_32()
.w(input_width)
.h_8()
.pl_2()
.pr_1()
.py_1()
.border_1()
.border_color(border_color)
.rounded_lg()
};
let search_line = h_flex()
.gap_2()
.when(supported_options.find_in_results, |el| {
el.child(Label::new("Find in results").color(Color::Hint))
})
.child(
input_base_styles(query_border)
.id("editor-scroll")
.track_scroll(&self.editor_scroll_handle)
.child(self.render_text_input(&self.query_editor, color_override, cx))
.when(!hide_inline_icons, |div| {
div.child(
h_flex()
.gap_1()
.children(supported_options.case.then(|| {
self.render_search_option_button(
SearchOptions::CASE_SENSITIVE,
focus_handle.clone(),
cx.listener(|this, _, window, cx| {
this.toggle_case_sensitive(
&ToggleCaseSensitive,
window,
cx,
)
}),
)
}))
.children(supported_options.word.then(|| {
self.render_search_option_button(
SearchOptions::WHOLE_WORD,
focus_handle.clone(),
cx.listener(|this, _, window, cx| {
this.toggle_whole_word(&ToggleWholeWord, window, cx)
}),
)
}))
.children(supported_options.regex.then(|| {
self.render_search_option_button(
SearchOptions::REGEX,
focus_handle.clone(),
cx.listener(|this, _, window, cx| {
this.toggle_regex(&ToggleRegex, window, cx)
}),
)
})),
)
}),
)
.child(
h_flex()
.gap_1()
.min_w_64()
.when(supported_options.replacement, |this| {
this.child(
IconButton::new(
"buffer-search-bar-toggle-replace-button",
IconName::Replace,
)
.style(ButtonStyle::Subtle)
.shape(IconButtonShape::Square)
.when(self.replace_enabled, |button| {
button.style(ButtonStyle::Filled)
})
.on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
this.toggle_replace(&ToggleReplace, window, cx);
}))
.toggle_state(self.replace_enabled)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Toggle Replace",
&ToggleReplace,
&focus_handle,
window,
cx,
)
}
}),
)
})
.when(supported_options.selection, |this| {
this.child(
IconButton::new(
"buffer-search-bar-toggle-search-selection-button",
IconName::Quote,
)
.style(ButtonStyle::Subtle)
.shape(IconButtonShape::Square)
.when(self.selection_search_enabled, |button| {
button.style(ButtonStyle::Filled)
})
.on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
this.toggle_selection(&ToggleSelection, window, cx);
}))
.toggle_state(self.selection_search_enabled)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Toggle Search Selection",
&ToggleSelection,
&focus_handle,
window,
cx,
)
}
}),
)
})
.when(!supported_options.find_in_results, |el| {
el.child(
IconButton::new("select-all", ui::IconName::SelectAll)
.on_click(|_, window, cx| {
window.dispatch_action(SelectAllMatches.boxed_clone(), cx)
})
.shape(IconButtonShape::Square)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Select All Matches",
&SelectAllMatches,
&focus_handle,
window,
cx,
)
}
}),
)
.child(
h_flex()
.pl_2()
.ml_1()
.border_l_1()
.border_color(cx.theme().colors().border_variant)
.child(render_nav_button(
ui::IconName::ChevronLeft,
self.active_match_index.is_some(),
"Select Previous Match",
&SelectPreviousMatch,
focus_handle.clone(),
))
.child(render_nav_button(
ui::IconName::ChevronRight,
self.active_match_index.is_some(),
"Select Next Match",
&SelectNextMatch,
focus_handle.clone(),
)),
)
.when(!narrow_mode, |this| {
this.child(h_flex().ml_2().min_w(rems_from_px(40.)).child(
Label::new(match_text).size(LabelSize::Small).color(
if self.active_match_index.is_some() {
Color::Default
} else {
Color::Disabled
},
),
))
})
})
.when(supported_options.find_in_results, |el| {
el.child(
IconButton::new(SharedString::from("Close"), IconName::Close)
.shape(IconButtonShape::Square)
.tooltip(move |window, cx| {
Tooltip::for_action("Close Search Bar", &Dismiss, window, cx)
})
.on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
this.dismiss(&Dismiss, window, cx)
})),
)
}),
);
let replace_line = should_show_replace_input.then(|| {
h_flex()
.gap_2()
.child(
input_base_styles(replacement_border).child(self.render_text_input(
&self.replacement_editor,
None,
cx,
)),
)
.child(
h_flex()
.min_w_64()
.gap_1()
.child(
IconButton::new("search-replace-next", ui::IconName::ReplaceNext)
.shape(IconButtonShape::Square)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Replace Next Match",
&ReplaceNext,
&focus_handle,
window,
cx,
)
}
})
.on_click(cx.listener(|this, _, window, cx| {
this.replace_next(&ReplaceNext, window, cx)
})),
)
.child(
IconButton::new("search-replace-all", ui::IconName::ReplaceAll)
.shape(IconButtonShape::Square)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Replace All Matches",
&ReplaceAll,
&focus_handle,
window,
cx,
)
}
})
.on_click(cx.listener(|this, _, window, cx| {
this.replace_all(&ReplaceAll, window, cx)
})),
),
)
});
let query_error_line = self.query_error.as_ref().map(|error| {
Label::new(error)
@ -491,10 +397,26 @@ impl Render for BufferSearchBar {
.ml_2()
});
let search_line =
h_flex()
.relative()
.child(search_line)
.when(!narrow_mode && !find_in_results, |div| {
div.child(h_flex().absolute().right_0().child(render_action_button(
"buffer-search",
IconName::Close,
true,
"Close Search Bar",
&Dismiss,
focus_handle.clone(),
)))
.w_full()
});
v_flex()
.id("buffer_search")
.gap_2()
.py(px(1.0))
.w_full()
.track_scroll(&self.scroll_handle)
.key_context(key_context)
.capture_action(cx.listener(Self::tab))
@ -509,43 +431,26 @@ impl Render for BufferSearchBar {
active_searchable_item.relay_action(Box::new(ToggleOutline), window, cx);
}
}))
.when(self.supported_options(cx).replacement, |this| {
.when(replacement, |this| {
this.on_action(cx.listener(Self::toggle_replace))
.when(in_replace, |this| {
this.on_action(cx.listener(Self::replace_next))
.on_action(cx.listener(Self::replace_all))
})
})
.when(self.supported_options(cx).case, |this| {
.when(case, |this| {
this.on_action(cx.listener(Self::toggle_case_sensitive))
})
.when(self.supported_options(cx).word, |this| {
.when(word, |this| {
this.on_action(cx.listener(Self::toggle_whole_word))
})
.when(self.supported_options(cx).regex, |this| {
.when(regex, |this| {
this.on_action(cx.listener(Self::toggle_regex))
})
.when(self.supported_options(cx).selection, |this| {
.when(selection, |this| {
this.on_action(cx.listener(Self::toggle_selection))
})
.child(h_flex().relative().child(search_line.w_full()).when(
!narrow_mode && !supported_options.find_in_results,
|div| {
div.child(
h_flex().absolute().right_0().child(
IconButton::new(SharedString::from("Close"), IconName::Close)
.shape(IconButtonShape::Square)
.tooltip(move |window, cx| {
Tooltip::for_action("Close Search Bar", &Dismiss, window, cx)
})
.on_click(cx.listener(|this, _: &ClickEvent, window, cx| {
this.dismiss(&Dismiss, window, cx)
})),
),
)
.w_full()
},
))
.child(search_line)
.children(query_error_line)
.children(replace_line)
}
@ -792,7 +697,7 @@ impl BufferSearchBar {
active_editor.search_bar_visibility_changed(false, window, cx);
active_editor.toggle_filtered_search_ranges(false, window, cx);
let handle = active_editor.item_focus_handle(cx);
self.focus(&handle, window, cx);
self.focus(&handle, window);
}
cx.emit(Event::UpdateLocation);
cx.emit(ToolbarItemEvent::ChangeLocation(
@ -948,7 +853,7 @@ impl BufferSearchBar {
}
pub fn focus_replace(&mut self, window: &mut Window, cx: &mut Context<Self>) {
self.focus(&self.replacement_editor.focus_handle(cx), window, cx);
self.focus(&self.replacement_editor.focus_handle(cx), window);
cx.notify();
}
@ -975,16 +880,6 @@ impl BufferSearchBar {
self.update_matches(!updated, window, cx)
}
fn render_search_option_button<Action: Fn(&ClickEvent, &mut Window, &mut App) + 'static>(
&self,
option: SearchOptions,
focus_handle: FocusHandle,
action: Action,
) -> impl IntoElement + use<Action> {
let is_active = self.search_options.contains(option);
option.as_button(is_active, focus_handle, action)
}
pub fn focus_editor(&mut self, _: &FocusEditor, window: &mut Window, cx: &mut Context<Self>) {
if let Some(active_editor) = self.active_searchable_item.as_ref() {
let handle = active_editor.item_focus_handle(cx);
@ -1400,28 +1295,32 @@ impl BufferSearchBar {
}
fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
// Search -> Replace -> Editor
let focus_handle = if self.replace_enabled && self.query_editor_focused {
self.replacement_editor.focus_handle(cx)
} else if let Some(item) = self.active_searchable_item.as_ref() {
item.item_focus_handle(cx)
} else {
return;
};
self.focus(&focus_handle, window, cx);
cx.stop_propagation();
self.cycle_field(Direction::Next, window, cx);
}
fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
// Search -> Replace -> Search
let focus_handle = if self.replace_enabled && self.query_editor_focused {
self.replacement_editor.focus_handle(cx)
} else if self.replacement_editor_focused {
self.query_editor.focus_handle(cx)
} else {
return;
self.cycle_field(Direction::Prev, window, cx);
}
fn cycle_field(&mut self, direction: Direction, window: &mut Window, cx: &mut Context<Self>) {
let mut handles = vec![self.query_editor.focus_handle(cx)];
if self.replace_enabled {
handles.push(self.replacement_editor.focus_handle(cx));
}
if let Some(item) = self.active_searchable_item.as_ref() {
handles.push(item.item_focus_handle(cx));
}
let current_index = match handles.iter().position(|focus| focus.is_focused(window)) {
Some(index) => index,
None => return,
};
self.focus(&focus_handle, window, cx);
let new_index = match direction {
Direction::Next => (current_index + 1) % handles.len(),
Direction::Prev if current_index == 0 => handles.len() - 1,
Direction::Prev => (current_index - 1) % handles.len(),
};
let next_focus_handle = &handles[new_index];
self.focus(next_focus_handle, window);
cx.stop_propagation();
}
@ -1469,10 +1368,8 @@ impl BufferSearchBar {
}
}
fn focus(&self, handle: &gpui::FocusHandle, window: &mut Window, cx: &mut Context<Self>) {
cx.on_next_frame(window, |_, window, _| {
window.invalidate_character_coordinates();
});
fn focus(&self, handle: &gpui::FocusHandle, window: &mut Window) {
window.invalidate_character_coordinates();
window.focus(handle);
}
@ -1484,7 +1381,7 @@ impl BufferSearchBar {
} else {
self.query_editor.focus_handle(cx)
};
self.focus(&handle, window, cx);
self.focus(&handle, window);
cx.notify();
}
}

View file

@ -1,20 +1,25 @@
use crate::{
BufferSearchBar, FocusSearch, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext,
SearchOptions, SelectNextMatch, SelectPreviousMatch, ToggleCaseSensitive, ToggleIncludeIgnored,
ToggleRegex, ToggleReplace, ToggleWholeWord, buffer_search::Deploy,
ToggleRegex, ToggleReplace, ToggleWholeWord,
buffer_search::Deploy,
search_bar::{
input_base_styles, render_action_button, render_text_input, toggle_replace_button,
},
};
use anyhow::Context as _;
use collections::{HashMap, HashSet};
use editor::{
Anchor, Editor, EditorElement, EditorEvent, EditorSettings, EditorStyle, MAX_TAB_TITLE_LEN,
MultiBuffer, SelectionEffects, actions::SelectAll, items::active_match_index,
Anchor, Editor, EditorEvent, EditorSettings, MAX_TAB_TITLE_LEN, MultiBuffer, SelectionEffects,
actions::{Backtab, SelectAll, Tab},
items::active_match_index,
};
use futures::{StreamExt, stream::FuturesOrdered};
use gpui::{
Action, AnyElement, AnyView, App, Axis, Context, Entity, EntityId, EventEmitter, FocusHandle,
Focusable, Global, Hsla, InteractiveElement, IntoElement, KeyContext, ParentElement, Point,
Render, SharedString, Styled, Subscription, Task, TextStyle, UpdateGlobal, WeakEntity, Window,
actions, div,
Render, SharedString, Styled, Subscription, Task, UpdateGlobal, WeakEntity, Window, actions,
div,
};
use language::{Buffer, Language};
use menu::Confirm;
@ -32,7 +37,6 @@ use std::{
pin::pin,
sync::Arc,
};
use theme::ThemeSettings;
use ui::{
Icon, IconButton, IconButtonShape, IconName, KeyBinding, Label, LabelCommon, LabelSize,
Toggleable, Tooltip, h_flex, prelude::*, utils::SearchInputWidth, v_flex,
@ -1435,7 +1439,7 @@ impl ProjectSearchView {
.mb_2(),
)
.child(
Button::new("filter-paths", "Include/exclude specific paths")
Button::new("filter-paths", "Include/exclude specific paths", cx)
.icon(IconName::Filter)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
@ -1450,7 +1454,7 @@ impl ProjectSearchView {
}),
)
.child(
Button::new("find-replace", "Find and replace")
Button::new("find-replace", "Find and replace", cx)
.icon(IconName::Replace)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
@ -1465,7 +1469,7 @@ impl ProjectSearchView {
}),
)
.child(
Button::new("regex", "Match with regex")
Button::new("regex", "Match with regex", cx)
.icon(IconName::Regex)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
@ -1480,7 +1484,7 @@ impl ProjectSearchView {
}),
)
.child(
Button::new("match-case", "Match case")
Button::new("match-case", "Match case", cx)
.icon(IconName::CaseSensitive)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
@ -1495,7 +1499,7 @@ impl ProjectSearchView {
}),
)
.child(
Button::new("match-whole-words", "Match whole words")
Button::new("match-whole-words", "Match whole words", cx)
.icon(IconName::WholeWord)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
@ -1610,16 +1614,11 @@ impl ProjectSearchBar {
}
}
fn tab(&mut self, _: &editor::actions::Tab, window: &mut Window, cx: &mut Context<Self>) {
fn tab(&mut self, _: &Tab, window: &mut Window, cx: &mut Context<Self>) {
self.cycle_field(Direction::Next, window, cx);
}
fn backtab(
&mut self,
_: &editor::actions::Backtab,
window: &mut Window,
cx: &mut Context<Self>,
) {
fn backtab(&mut self, _: &Backtab, window: &mut Window, cx: &mut Context<Self>) {
self.cycle_field(Direction::Prev, window, cx);
}
@ -1634,29 +1633,22 @@ impl ProjectSearchBar {
fn cycle_field(&mut self, direction: Direction, window: &mut Window, cx: &mut Context<Self>) {
let active_project_search = match &self.active_project_search {
Some(active_project_search) => active_project_search,
None => {
return;
}
None => return,
};
active_project_search.update(cx, |project_view, cx| {
let mut views = vec![&project_view.query_editor];
let mut views = vec![project_view.query_editor.focus_handle(cx)];
if project_view.replace_enabled {
views.push(&project_view.replacement_editor);
views.push(project_view.replacement_editor.focus_handle(cx));
}
if project_view.filters_enabled {
views.extend([
&project_view.included_files_editor,
&project_view.excluded_files_editor,
project_view.included_files_editor.focus_handle(cx),
project_view.excluded_files_editor.focus_handle(cx),
]);
}
let current_index = match views
.iter()
.enumerate()
.find(|(_, editor)| editor.focus_handle(cx).is_focused(window))
{
Some((index, _)) => index,
let current_index = match views.iter().position(|focus| focus.is_focused(window)) {
Some(index) => index,
None => return,
};
@ -1665,8 +1657,8 @@ impl ProjectSearchBar {
Direction::Prev if current_index == 0 => views.len() - 1,
Direction::Prev => (current_index - 1) % views.len(),
};
let next_focus_handle = views[new_index].focus_handle(cx);
window.focus(&next_focus_handle);
let next_focus_handle = &views[new_index];
window.focus(next_focus_handle);
cx.stop_propagation();
});
}
@ -1915,37 +1907,6 @@ impl ProjectSearchBar {
})
}
}
fn render_text_input(&self, editor: &Entity<Editor>, cx: &Context<Self>) -> impl IntoElement {
let (color, use_syntax) = if editor.read(cx).read_only(cx) {
(cx.theme().colors().text_disabled, false)
} else {
(cx.theme().colors().text, true)
};
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color,
font_family: settings.buffer_font.family.clone(),
font_features: settings.buffer_font.features.clone(),
font_fallbacks: settings.buffer_font.fallbacks.clone(),
font_size: rems(0.875).into(),
font_weight: settings.buffer_font.weight,
line_height: relative(1.3),
..TextStyle::default()
};
let mut editor_style = EditorStyle {
background: cx.theme().colors().toolbar_background,
local_player: cx.theme().players().local(),
text: text_style,
..EditorStyle::default()
};
if use_syntax {
editor_style.syntax = cx.theme().syntax().clone();
}
EditorElement::new(editor, editor_style)
}
}
impl Render for ProjectSearchBar {
@ -1965,21 +1926,41 @@ impl Render for ProjectSearchBar {
}
let input_base_styles = |base_style: BaseStyle, panel: InputPanel| {
h_flex()
.min_w_32()
.map(|div| match base_style {
BaseStyle::SingleInput => div.w(input_width),
BaseStyle::MultipleInputs => div.flex_grow(),
})
.h_8()
.pl_2()
.pr_1()
.py_1()
.border_1()
.border_color(search.border_color_for(panel, cx))
.rounded_lg()
input_base_styles(search.border_color_for(panel, cx), |div| match base_style {
BaseStyle::SingleInput => div.w(input_width),
BaseStyle::MultipleInputs => div.flex_grow(),
})
};
let project_search = search.entity.read(cx);
let limit_reached = project_search.limit_reached;
let color_override = match (
project_search.no_results,
&project_search.active_query,
&project_search.last_search_query_text,
) {
(Some(true), Some(q), Some(p)) if q.as_str() == p => Some(Color::Error),
_ => None,
};
let match_text = search
.active_match_index
.and_then(|index| {
let index = index + 1;
let match_quantity = project_search.match_ranges.len();
if match_quantity > 0 {
debug_assert!(match_quantity >= index);
if limit_reached {
Some(format!("{index}/{match_quantity}+"))
} else {
Some(format!("{index}/{match_quantity}"))
}
} else {
None
}
})
.unwrap_or_else(|| "0/0".to_string());
let query_column = input_base_styles(BaseStyle::SingleInput, InputPanel::Query)
.on_action(cx.listener(|this, action, window, cx| this.confirm(action, window, cx)))
.on_action(cx.listener(|this, action, window, cx| {
@ -1988,7 +1969,7 @@ impl Render for ProjectSearchBar {
.on_action(
cx.listener(|this, action, window, cx| this.next_history_query(action, window, cx)),
)
.child(self.render_text_input(&search.query_editor, cx))
.child(render_text_input(&search.query_editor, color_override, cx))
.child(
h_flex()
.gap_1()
@ -2017,6 +1998,7 @@ impl Render for ProjectSearchBar {
let mode_column = h_flex()
.gap_1()
.min_w_64()
.child(
IconButton::new("project-search-filter-button", IconName::Filter)
.shape(IconButtonShape::Square)
@ -2045,109 +2027,46 @@ impl Render for ProjectSearchBar {
}
}),
)
.child(
IconButton::new("project-search-toggle-replace", IconName::Replace)
.shape(IconButtonShape::Square)
.on_click(cx.listener(|this, _, window, cx| {
this.toggle_replace(&ToggleReplace, window, cx);
}))
.toggle_state(
self.active_project_search
.as_ref()
.map(|search| search.read(cx).replace_enabled)
.unwrap_or_default(),
)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Toggle Replace",
&ToggleReplace,
&focus_handle,
window,
cx,
)
}
}),
);
.child(toggle_replace_button(
"project-search-toggle-replace",
focus_handle.clone(),
self.active_project_search
.as_ref()
.map(|search| search.read(cx).replace_enabled)
.unwrap_or_default(),
cx.listener(|this, _, window, cx| {
this.toggle_replace(&ToggleReplace, window, cx);
}),
));
let limit_reached = search.entity.read(cx).limit_reached;
let match_text = search
.active_match_index
.and_then(|index| {
let index = index + 1;
let match_quantity = search.entity.read(cx).match_ranges.len();
if match_quantity > 0 {
debug_assert!(match_quantity >= index);
if limit_reached {
Some(format!("{index}/{match_quantity}+"))
} else {
Some(format!("{index}/{match_quantity}"))
}
} else {
None
}
})
.unwrap_or_else(|| "0/0".to_string());
let query_focus = search.query_editor.focus_handle(cx);
let matches_column = h_flex()
.pl_2()
.ml_2()
.border_l_1()
.border_color(cx.theme().colors().border_variant)
.child(
IconButton::new("project-search-prev-match", IconName::ChevronLeft)
.shape(IconButtonShape::Square)
.disabled(search.active_match_index.is_none())
.on_click(cx.listener(|this, _, window, cx| {
if let Some(search) = this.active_project_search.as_ref() {
search.update(cx, |this, cx| {
this.select_match(Direction::Prev, window, cx);
})
}
}))
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Go To Previous Match",
&SelectPreviousMatch,
&focus_handle,
window,
cx,
)
}
}),
)
.child(
IconButton::new("project-search-next-match", IconName::ChevronRight)
.shape(IconButtonShape::Square)
.disabled(search.active_match_index.is_none())
.on_click(cx.listener(|this, _, window, cx| {
if let Some(search) = this.active_project_search.as_ref() {
search.update(cx, |this, cx| {
this.select_match(Direction::Next, window, cx);
})
}
}))
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Go To Next Match",
&SelectNextMatch,
&focus_handle,
window,
cx,
)
}
}),
)
.child(render_action_button(
"project-search-nav-button",
IconName::ChevronLeft,
search.active_match_index.is_some(),
"Select Previous Match",
&SelectPreviousMatch,
query_focus.clone(),
))
.child(render_action_button(
"project-search-nav-button",
IconName::ChevronRight,
search.active_match_index.is_some(),
"Select Next Match",
&SelectNextMatch,
query_focus,
))
.child(
div()
.id("matches")
.ml_1()
.ml_2()
.min_w(rems_from_px(40.))
.child(Label::new(match_text).size(LabelSize::Small).color(
if search.active_match_index.is_some() {
Color::Default
@ -2170,62 +2089,29 @@ impl Render for ProjectSearchBar {
let replace_line = search.replace_enabled.then(|| {
let replace_column = input_base_styles(BaseStyle::SingleInput, InputPanel::Replacement)
.child(self.render_text_input(&search.replacement_editor, cx));
.child(render_text_input(&search.replacement_editor, None, cx));
let focus_handle = search.replacement_editor.read(cx).focus_handle(cx);
let replace_actions =
h_flex()
.min_w_64()
.gap_1()
.when(search.replace_enabled, |this| {
this.child(
IconButton::new("project-search-replace-next", IconName::ReplaceNext)
.shape(IconButtonShape::Square)
.on_click(cx.listener(|this, _, window, cx| {
if let Some(search) = this.active_project_search.as_ref() {
search.update(cx, |this, cx| {
this.replace_next(&ReplaceNext, window, cx);
})
}
}))
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Replace Next Match",
&ReplaceNext,
&focus_handle,
window,
cx,
)
}
}),
)
.child(
IconButton::new("project-search-replace-all", IconName::ReplaceAll)
.shape(IconButtonShape::Square)
.on_click(cx.listener(|this, _, window, cx| {
if let Some(search) = this.active_project_search.as_ref() {
search.update(cx, |this, cx| {
this.replace_all(&ReplaceAll, window, cx);
})
}
}))
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Replace All Matches",
&ReplaceAll,
&focus_handle,
window,
cx,
)
}
}),
)
});
let replace_actions = h_flex()
.min_w_64()
.gap_1()
.child(render_action_button(
"project-search-replace-button",
IconName::ReplaceNext,
true,
"Replace Next Match",
&ReplaceNext,
focus_handle.clone(),
))
.child(render_action_button(
"project-search-replace-button",
IconName::ReplaceAll,
true,
"Replace All Matches",
&ReplaceAll,
focus_handle,
));
h_flex()
.w_full()
@ -2235,6 +2121,45 @@ impl Render for ProjectSearchBar {
});
let filter_line = search.filters_enabled.then(|| {
let include = input_base_styles(BaseStyle::MultipleInputs, InputPanel::Include)
.on_action(cx.listener(|this, action, window, cx| {
this.previous_history_query(action, window, cx)
}))
.on_action(cx.listener(|this, action, window, cx| {
this.next_history_query(action, window, cx)
}))
.child(render_text_input(&search.included_files_editor, None, cx));
let exclude = input_base_styles(BaseStyle::MultipleInputs, InputPanel::Exclude)
.on_action(cx.listener(|this, action, window, cx| {
this.previous_history_query(action, window, cx)
}))
.on_action(cx.listener(|this, action, window, cx| {
this.next_history_query(action, window, cx)
}))
.child(render_text_input(&search.excluded_files_editor, None, cx));
let mode_column = h_flex()
.gap_1()
.min_w_64()
.child(
IconButton::new("project-search-opened-only", IconName::FolderSearch)
.shape(IconButtonShape::Square)
.toggle_state(self.is_opened_only_enabled(cx))
.tooltip(Tooltip::text("Only Search Open Files"))
.on_click(cx.listener(|this, _, window, cx| {
this.toggle_opened_only(window, cx);
})),
)
.child(
SearchOptions::INCLUDE_IGNORED.as_button(
search
.search_options
.contains(SearchOptions::INCLUDE_IGNORED),
focus_handle.clone(),
cx.listener(|this, _, window, cx| {
this.toggle_search_option(SearchOptions::INCLUDE_IGNORED, window, cx);
}),
),
);
h_flex()
.w_full()
.gap_2()
@ -2242,62 +2167,14 @@ impl Render for ProjectSearchBar {
h_flex()
.gap_2()
.w(input_width)
.child(
input_base_styles(BaseStyle::MultipleInputs, InputPanel::Include)
.on_action(cx.listener(|this, action, window, cx| {
this.previous_history_query(action, window, cx)
}))
.on_action(cx.listener(|this, action, window, cx| {
this.next_history_query(action, window, cx)
}))
.child(self.render_text_input(&search.included_files_editor, cx)),
)
.child(
input_base_styles(BaseStyle::MultipleInputs, InputPanel::Exclude)
.on_action(cx.listener(|this, action, window, cx| {
this.previous_history_query(action, window, cx)
}))
.on_action(cx.listener(|this, action, window, cx| {
this.next_history_query(action, window, cx)
}))
.child(self.render_text_input(&search.excluded_files_editor, cx)),
),
)
.child(
h_flex()
.min_w_64()
.gap_1()
.child(
IconButton::new("project-search-opened-only", IconName::FolderSearch)
.shape(IconButtonShape::Square)
.toggle_state(self.is_opened_only_enabled(cx))
.tooltip(Tooltip::text("Only Search Open Files"))
.on_click(cx.listener(|this, _, window, cx| {
this.toggle_opened_only(window, cx);
})),
)
.child(
SearchOptions::INCLUDE_IGNORED.as_button(
search
.search_options
.contains(SearchOptions::INCLUDE_IGNORED),
focus_handle.clone(),
cx.listener(|this, _, window, cx| {
this.toggle_search_option(
SearchOptions::INCLUDE_IGNORED,
window,
cx,
);
}),
),
),
.child(include)
.child(exclude),
)
.child(mode_column)
});
let mut key_context = KeyContext::default();
key_context.add("ProjectSearchBar");
if search
.replacement_editor
.focus_handle(cx)
@ -2315,7 +2192,9 @@ impl Render for ProjectSearchBar {
});
v_flex()
.gap_2()
.py(px(1.0))
.w_full()
.key_context(key_context)
.on_action(cx.listener(|this, _: &ToggleFocus, window, cx| {
this.move_focus_to_results(window, cx)
@ -2323,14 +2202,8 @@ impl Render for ProjectSearchBar {
.on_action(cx.listener(|this, _: &ToggleFilters, window, cx| {
this.toggle_filters(window, cx);
}))
.capture_action(cx.listener(|this, action, window, cx| {
this.tab(action, window, cx);
cx.stop_propagation();
}))
.capture_action(cx.listener(|this, action, window, cx| {
this.backtab(action, window, cx);
cx.stop_propagation();
}))
.capture_action(cx.listener(Self::tab))
.capture_action(cx.listener(Self::backtab))
.on_action(cx.listener(|this, action, window, cx| this.confirm(action, window, cx)))
.on_action(cx.listener(|this, action, window, cx| {
this.toggle_replace(action, window, cx);
@ -2362,8 +2235,6 @@ impl Render for ProjectSearchBar {
})
.on_action(cx.listener(Self::select_next_match))
.on_action(cx.listener(Self::select_prev_match))
.gap_2()
.w_full()
.child(search_line)
.children(query_error_line)
.children(replace_line)

View file

@ -1,8 +1,14 @@
use gpui::{Action, FocusHandle, IntoElement};
use editor::{Editor, EditorElement, EditorStyle};
use gpui::{Action, Entity, FocusHandle, Hsla, IntoElement, TextStyle};
use settings::Settings;
use theme::ThemeSettings;
use ui::{IconButton, IconButtonShape};
use ui::{Tooltip, prelude::*};
pub(super) fn render_nav_button(
use crate::ToggleReplace;
pub(super) fn render_action_button(
id_prefix: &'static str,
icon: ui::IconName,
active: bool,
tooltip: &'static str,
@ -10,7 +16,7 @@ pub(super) fn render_nav_button(
focus_handle: FocusHandle,
) -> impl IntoElement {
IconButton::new(
SharedString::from(format!("search-nav-button-{}", action.name())),
SharedString::from(format!("{id_prefix}-{}", action.name())),
icon,
)
.shape(IconButtonShape::Square)
@ -26,3 +32,74 @@ pub(super) fn render_nav_button(
.tooltip(move |window, cx| Tooltip::for_action_in(tooltip, action, &focus_handle, window, cx))
.disabled(!active)
}
pub(crate) fn input_base_styles(border_color: Hsla, map: impl FnOnce(Div) -> Div) -> Div {
h_flex()
.min_w_32()
.map(map)
.h_8()
.pl_2()
.pr_1()
.py_1()
.border_1()
.border_color(border_color)
.rounded_lg()
}
pub(crate) fn toggle_replace_button(
id: &'static str,
focus_handle: FocusHandle,
replace_enabled: bool,
on_click: impl Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static,
) -> IconButton {
IconButton::new(id, IconName::Replace)
.shape(IconButtonShape::Square)
.style(ButtonStyle::Subtle)
.when(replace_enabled, |button| button.style(ButtonStyle::Filled))
.on_click(on_click)
.toggle_state(replace_enabled)
.tooltip({
move |window, cx| {
Tooltip::for_action_in("Toggle Replace", &ToggleReplace, &focus_handle, window, cx)
}
})
}
pub(crate) fn render_text_input(
editor: &Entity<Editor>,
color_override: Option<Color>,
app: &App,
) -> impl IntoElement {
let (color, use_syntax) = if editor.read(app).read_only(app) {
(app.theme().colors().text_disabled, false)
} else {
match color_override {
Some(color_override) => (color_override.color(app), false),
None => (app.theme().colors().text, true),
}
};
let settings = ThemeSettings::get_global(app);
let text_style = TextStyle {
color,
font_family: settings.buffer_font.family.clone(),
font_features: settings.buffer_font.features.clone(),
font_fallbacks: settings.buffer_font.fallbacks.clone(),
font_size: rems(0.875).into(),
font_weight: settings.buffer_font.weight,
line_height: relative(1.3),
..TextStyle::default()
};
let mut editor_style = EditorStyle {
background: app.theme().colors().toolbar_background,
local_player: app.theme().players().local(),
text: text_style,
..EditorStyle::default()
};
if use_syntax {
editor_style.syntax = app.theme().syntax().clone();
}
EditorElement::new(editor, editor_style)
}

View file

@ -135,6 +135,15 @@ impl ProjectIndexDebugView {
let colors = cx.theme().colors();
let chunk = &state.chunks[ix];
let chunk_count = state.chunks.len();
let prev_button = Button::new(("prev", ix), "prev", cx)
.disabled(ix == 0)
.on_click(cx.listener(move |this, _, _, _| this.scroll_to_chunk(ix.saturating_sub(1))));
let next_button = Button::new(("next", ix), "next", cx)
.disabled(ix + 1 == chunk_count)
.on_click(cx.listener(move |this, _, _, _| this.scroll_to_chunk(ix + 1)));
div()
.text_ui(cx)
@ -146,26 +155,10 @@ impl ProjectIndexDebugView {
.child(format!(
"chunk {} of {}. length: {}",
ix + 1,
state.chunks.len(),
chunk_count,
chunk.len(),
))
.child(
h_flex()
.child(
Button::new(("prev", ix), "prev")
.disabled(ix == 0)
.on_click(cx.listener(move |this, _, _, _| {
this.scroll_to_chunk(ix.saturating_sub(1))
})),
)
.child(
Button::new(("next", ix), "next")
.disabled(ix + 1 == state.chunks.len())
.on_click(cx.listener(move |this, _, _, _| {
this.scroll_to_chunk(ix + 1)
})),
),
),
.child(h_flex().child(prev_button).child(next_button)),
)
.child(
div()

View file

@ -2469,7 +2469,7 @@ impl Render for KeybindingEditorModal {
.color(Color::Muted),
)
.child(
Button::new("show_matching", "View")
Button::new("show_matching", "View", cx)
.label_size(LabelSize::Small)
.icon(IconName::ArrowUpRight)
.icon_color(Color::Muted)
@ -2508,10 +2508,10 @@ impl Render for KeybindingEditorModal {
h_flex()
.gap_1()
.child(
Button::new("cancel", "Cancel")
Button::new("cancel", "Cancel", cx)
.on_click(cx.listener(|_, _, _, cx| cx.emit(DismissEvent))),
)
.child(Button::new("save-btn", "Save").on_click(cx.listener(
.child(Button::new("save-btn", "Save", cx).on_click(cx.listener(
|this, _event, _window, cx| {
this.save_or_display_error(cx);
},

View file

@ -1410,7 +1410,7 @@ impl Component for Table<3> {
"Project A".into_any_element(),
"High".into_any_element(),
"2023-12-31".into_any_element(),
Button::new("view_a", "View")
Button::new("view_a", "View", _cx)
.style(ButtonStyle::Filled)
.full_width()
.into_any_element(),
@ -1420,7 +1420,7 @@ impl Component for Table<3> {
"Project B".into_any_element(),
"Medium".into_any_element(),
"2024-03-15".into_any_element(),
Button::new("view_b", "View")
Button::new("view_b", "View", _cx)
.style(ButtonStyle::Filled)
.full_width()
.into_any_element(),
@ -1430,7 +1430,7 @@ impl Component for Table<3> {
"Project C".into_any_element(),
"Low".into_any_element(),
"2024-06-30".into_any_element(),
Button::new("view_c", "View")
Button::new("view_c", "View", _cx)
.style(ButtonStyle::Filled)
.full_width()
.into_any_element(),

View file

@ -668,7 +668,7 @@ impl PickerDelegate for TasksModalDelegate {
.map(|(label, action)| {
let keybind = KeyBinding::for_action(&*action, window, cx);
Button::new("edit-current-task", label)
Button::new("edit-current-task", label, cx)
.when_some(keybind, |this, keybind| this.key_binding(keybind))
.on_click(move |_, window, cx| {
window.dispatch_action(action.boxed_clone(), cx);
@ -691,7 +691,7 @@ impl PickerDelegate for TasksModalDelegate {
"Spawn Oneshot"
};
Button::new("spawn-onehshot", spawn_oneshot_label)
Button::new("spawn-onehshot", spawn_oneshot_label, cx)
.key_binding(keybind)
.on_click(move |_, window, cx| {
window.dispatch_action(action.boxed_clone(), cx)
@ -706,14 +706,14 @@ impl PickerDelegate for TasksModalDelegate {
} else {
"Spawn Without History"
};
Button::new("spawn", label).key_binding(keybind).on_click(
move |_, window, cx| {
Button::new("spawn", label, cx)
.key_binding(keybind)
.on_click(move |_, window, cx| {
window.dispatch_action(
menu::SecondaryConfirm.boxed_clone(),
cx,
)
},
)
})
},
),
)
@ -723,7 +723,7 @@ impl PickerDelegate for TasksModalDelegate {
let run_entry_label =
if is_recent_selected { "Rerun" } else { "Spawn" };
Button::new("spawn", run_entry_label)
Button::new("spawn", run_entry_label, cx)
.key_binding(keybind)
.on_click(|_, window, cx| {
window.dispatch_action(menu::Confirm.boxed_clone(), cx);

View file

@ -315,7 +315,7 @@ impl PickerDelegate for IconThemeSelectorDelegate {
.border_t_1()
.border_color(cx.theme().colors().border_variant)
.child(
Button::new("docs", "View Icon Theme Docs")
Button::new("docs", "View Icon Theme Docs", cx)
.icon(IconName::ArrowUpRight)
.icon_position(IconPosition::End)
.icon_size(IconSize::Small)
@ -325,7 +325,7 @@ impl PickerDelegate for IconThemeSelectorDelegate {
}),
)
.child(
Button::new("more-icon-themes", "Install Icon Themes").on_click(
Button::new("more-icon-themes", "Install Icon Themes", cx).on_click(
move |_event, window, cx| {
window.dispatch_action(
Box::new(Extensions {

View file

@ -373,7 +373,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
.border_t_1()
.border_color(cx.theme().colors().border_variant)
.child(
Button::new("docs", "View Theme Docs")
Button::new("docs", "View Theme Docs", cx)
.icon(IconName::ArrowUpRight)
.icon_position(IconPosition::End)
.icon_size(IconSize::Small)
@ -383,7 +383,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
})),
)
.child(
Button::new("more-themes", "Install Themes").on_click(cx.listener({
Button::new("more-themes", "Install Themes", cx).on_click(cx.listener({
move |_, _, window, cx| {
window.dispatch_action(
Box::new(Extensions {

View file

@ -163,7 +163,7 @@ impl ApplicationMenu {
)
}
fn render_standard_menu(&self, entry: &MenuEntry) -> impl IntoElement {
fn render_standard_menu(&self, entry: &MenuEntry, cx: &App) -> impl IntoElement {
let current_handle = entry.handle.clone();
let menu_name = entry.menu.name.clone();
@ -187,6 +187,7 @@ impl ApplicationMenu {
Button::new(
SharedString::from(format!("{}-menu-trigger", menu_name)),
menu_name.clone(),
cx,
)
.style(ButtonStyle::Subtle)
.label_size(LabelSize::Small),
@ -310,7 +311,7 @@ impl Render for ApplicationMenu {
this.children(
self.entries
.iter()
.map(|entry| self.render_standard_menu(entry)),
.map(|entry| self.render_standard_menu(entry, cx)),
)
})
}

View file

@ -366,6 +366,7 @@ impl TitleBar {
Button::new(
"toggle_sharing",
if is_shared { "Unshare" } else { "Share" },
cx,
)
.tooltip(Tooltip::text(if is_shared {
"Stop sharing project with call participants"

View file

@ -390,7 +390,7 @@ impl TitleBar {
if self.project.read(cx).is_disconnected(cx) {
return Some(
Button::new("disconnected", "Disconnected")
Button::new("disconnected", "Disconnected", cx)
.disabled(true)
.color(Color::Disabled)
.style(ButtonStyle::Subtle)
@ -407,7 +407,7 @@ impl TitleBar {
.participant_indices()
.get(&host_user.id)?;
Some(
Button::new("project_owner_trigger", host_user.github_login.clone())
Button::new("project_owner_trigger", host_user.github_login.clone(), cx)
.color(Color::Player(participant_index.0))
.style(ButtonStyle::Subtle)
.label_size(LabelSize::Small)
@ -445,7 +445,7 @@ impl TitleBar {
"Open recent project".to_string()
};
Button::new("project_name_trigger", name)
Button::new("project_name_trigger", name, cx)
.when(!is_project_selected, |b| b.color(Color::Muted))
.style(ButtonStyle::Subtle)
.label_size(LabelSize::Small)
@ -491,7 +491,7 @@ impl TitleBar {
}?;
Some(
Button::new("project_branch_trigger", branch_name)
Button::new("project_branch_trigger", branch_name, cx)
.color(Color::Muted)
.style(ButtonStyle::Subtle)
.label_size(LabelSize::Small)
@ -590,7 +590,7 @@ impl TitleBar {
};
Some(
Button::new("connection-status", label)
Button::new("connection-status", label, cx)
.label_size(LabelSize::Small)
.on_click(|_, window, cx| {
if let Some(auto_updater) = auto_update::AutoUpdater::get(cx) {
@ -608,9 +608,9 @@ impl TitleBar {
}
}
pub fn render_sign_in_button(&mut self, _: &mut Context<Self>) -> Button {
pub fn render_sign_in_button(&mut self, cx: &mut Context<Self>) -> Button {
let client = self.client.clone();
Button::new("sign_in", "Sign in")
Button::new("sign_in", "Sign in", cx)
.label_size(LabelSize::Small)
.on_click(move |_, window, cx| {
let client = client.clone();

View file

@ -232,7 +232,7 @@ impl Render for ActiveToolchain {
div().when_some(self.active_toolchain.as_ref(), |el, active_toolchain| {
let term = self.term.clone();
el.child(
Button::new("change-toolchain", active_toolchain.name.clone())
Button::new("change-toolchain", active_toolchain.name.clone(), cx)
.label_size(LabelSize::Small)
.on_click(cx.listener(|this, _, window, cx| {
if let Some(workspace) = this.workspace.upgrade() {

View file

@ -134,7 +134,7 @@ impl Component for Banner {
ComponentScope::DataDisplay
}
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
let severity_examples = vec![
single_example(
"Default",
@ -148,7 +148,7 @@ impl Component for Banner {
.severity(Severity::Info)
.child(Label::new("This is an informational message"))
.action_slot(
Button::new("learn-more", "Learn More")
Button::new("learn-more", "Learn More", cx)
.icon(IconName::ArrowUpRight)
.icon_size(IconSize::Small)
.icon_position(IconPosition::End),
@ -160,7 +160,7 @@ impl Component for Banner {
Banner::new()
.severity(Severity::Success)
.child(Label::new("Operation completed successfully"))
.action_slot(Button::new("dismiss", "Dismiss"))
.action_slot(Button::new("dismiss", "Dismiss", cx))
.into_any_element(),
),
single_example(
@ -168,7 +168,7 @@ impl Component for Banner {
Banner::new()
.severity(Severity::Warning)
.child(Label::new("Your settings file uses deprecated settings"))
.action_slot(Button::new("update", "Update Settings"))
.action_slot(Button::new("update", "Update Settings", cx))
.into_any_element(),
),
single_example(
@ -176,7 +176,7 @@ impl Component for Banner {
Banner::new()
.severity(Severity::Error)
.child(Label::new("Connection error: unable to connect to server"))
.action_slot(Button::new("reconnect", "Retry"))
.action_slot(Button::new("reconnect", "Retry", cx))
.into_any_element(),
),
];

View file

@ -1,5 +1,5 @@
use crate::component_prelude::*;
use gpui::{AnyElement, AnyView, DefiniteLength};
use gpui::{AnyElement, AnyView, DefiniteLength, FocusHandle, Focusable};
use ui_macros::RegisterComponent;
use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, IconName, IconSize, Label};
@ -79,6 +79,7 @@ use super::button_icon::ButtonIcon;
///
#[derive(IntoElement, Documented, RegisterComponent)]
pub struct Button {
focus_handle: FocusHandle,
base: ButtonLike,
label: SharedString,
label_color: Option<Color>,
@ -104,8 +105,9 @@ impl Button {
/// the button with the provided identifier and label text, setting all other
/// properties to their default values, which can be customized using the
/// builder pattern methods provided by this struct.
pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>, app: &App) -> Self {
Self {
focus_handle: app.focus_handle(),
base: ButtonLike::new(id),
label: label.into(),
label_color: None,
@ -216,6 +218,12 @@ impl Button {
}
}
impl Focusable for Button {
fn focus_handle(&self, _cx: &App) -> FocusHandle {
self.focus_handle.clone()
}
}
impl Toggleable for Button {
/// Sets the selected state of the button.
///
@ -483,7 +491,7 @@ impl Component for Button {
Some("A button triggers an event or action.")
}
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
Some(
v_flex()
.gap_6()
@ -493,29 +501,29 @@ impl Component for Button {
vec![
single_example(
"Default",
Button::new("default", "Default").into_any_element(),
Button::new("default", "Default", cx).into_any_element(),
),
single_example(
"Filled",
Button::new("filled", "Filled")
Button::new("filled", "Filled", cx)
.style(ButtonStyle::Filled)
.into_any_element(),
),
single_example(
"Subtle",
Button::new("outline", "Subtle")
Button::new("outlined", "Outlined", cx)
.style(ButtonStyle::Subtle)
.into_any_element(),
),
single_example(
"Tinted",
Button::new("tinted_accent_style", "Accent")
Button::new("tinted_accent_style", "Accent", cx)
.style(ButtonStyle::Tinted(TintColor::Accent))
.into_any_element(),
),
single_example(
"Transparent",
Button::new("transparent", "Transparent")
Button::new("transparent", "Transparent", cx)
.style(ButtonStyle::Transparent)
.into_any_element(),
),
@ -526,25 +534,25 @@ impl Component for Button {
vec![
single_example(
"Accent",
Button::new("tinted_accent", "Accent")
Button::new("color_accent", "Accent", cx)
.style(ButtonStyle::Tinted(TintColor::Accent))
.into_any_element(),
),
single_example(
"Error",
Button::new("tinted_negative", "Error")
Button::new("tinted_negative", "Error", cx)
.style(ButtonStyle::Tinted(TintColor::Error))
.into_any_element(),
),
single_example(
"Warning",
Button::new("tinted_warning", "Warning")
Button::new("tinted_warning", "Warning", cx)
.style(ButtonStyle::Tinted(TintColor::Warning))
.into_any_element(),
),
single_example(
"Success",
Button::new("tinted_positive", "Success")
Button::new("tinted_positive", "Success", cx)
.style(ButtonStyle::Tinted(TintColor::Success))
.into_any_element(),
),
@ -555,17 +563,17 @@ impl Component for Button {
vec![
single_example(
"Default",
Button::new("default_state", "Default").into_any_element(),
Button::new("default_state", "Default", cx).into_any_element(),
),
single_example(
"Disabled",
Button::new("disabled", "Disabled")
Button::new("disabled", "Disabled", cx)
.disabled(true)
.into_any_element(),
),
single_example(
"Selected",
Button::new("selected", "Selected")
Button::new("selected", "Selected", cx)
.toggle_state(true)
.into_any_element(),
),
@ -576,21 +584,21 @@ impl Component for Button {
vec![
single_example(
"Icon Start",
Button::new("icon_start", "Icon Start")
Button::new("icon-start", "Icon Start", cx)
.icon(IconName::Check)
.icon_position(IconPosition::Start)
.into_any_element(),
),
single_example(
"Icon End",
Button::new("icon_end", "Icon End")
Button::new("icon-end", "Icon End", cx)
.icon(IconName::Check)
.icon_position(IconPosition::End)
.into_any_element(),
),
single_example(
"Icon Color",
Button::new("icon_color", "Icon Color")
Button::new("icon_color", "Icon Color", cx)
.icon(IconName::Check)
.icon_color(Color::Accent)
.into_any_element(),

View file

@ -167,7 +167,7 @@ impl Component for Callout {
)
}
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
let callout_examples = vec![
single_example(
"Simple with Title Only",
@ -178,7 +178,9 @@ impl Component for Callout {
.size(IconSize::Small),
)
.title("System maintenance scheduled for tonight")
.primary_action(Button::new("got-it", "Got it").label_size(LabelSize::Small))
.primary_action(
Button::new("got-it", "Got it", cx).label_size(LabelSize::Small),
)
.into_any_element(),
)
.width(px(580.)),
@ -195,10 +197,10 @@ impl Component for Callout {
"We'll backup your current settings and update them to the new format.",
)
.primary_action(
Button::new("update", "Backup & Update").label_size(LabelSize::Small),
Button::new("update", "Backup & Update", cx).label_size(LabelSize::Small),
)
.secondary_action(
Button::new("dismiss", "Dismiss").label_size(LabelSize::Small),
Button::new("dismiss", "Dismiss", cx).label_size(LabelSize::Small),
)
.into_any_element(),
)
@ -214,10 +216,12 @@ impl Component for Callout {
.title("Thread reached the token limit")
.description("Start a new thread from a summary to continue the conversation.")
.primary_action(
Button::new("new-thread", "Start New Thread").label_size(LabelSize::Small),
Button::new("new-thread", "Start New Thread", cx)
.label_size(LabelSize::Small),
)
.secondary_action(
Button::new("view-summary", "View Summary").label_size(LabelSize::Small),
Button::new("view-summary", "View Summary", cx)
.label_size(LabelSize::Small),
)
.into_any_element(),
)
@ -233,10 +237,10 @@ impl Component for Callout {
.title("Upgrade to Pro")
.description("• Unlimited threads\n• Priority support\n• Advanced analytics")
.primary_action(
Button::new("upgrade", "Upgrade Now").label_size(LabelSize::Small),
Button::new("upgrade", "Upgrade Now", cx).label_size(LabelSize::Small),
)
.secondary_action(
Button::new("learn-more", "Learn More").label_size(LabelSize::Small),
Button::new("learn-more", "Learn More", cx).label_size(LabelSize::Small),
)
.into_any_element(),
)

View file

@ -811,7 +811,7 @@ impl ContextMenu {
ListSubHeader::new(header.clone())
.inset(true)
.end_slot(
Button::new(link_id, label.clone())
Button::new(link_id, label.clone(), cx)
.color(Color::Muted)
.label_size(LabelSize::Small)
.size(ButtonSize::None)

View file

@ -59,12 +59,17 @@ impl RenderOnce for AlertModal {
.items_center()
.gap_1()
.child(
Button::new(self.dismiss_label.clone(), self.dismiss_label.clone())
.color(Color::Muted),
Button::new(
self.dismiss_label.clone(),
self.dismiss_label.clone(),
cx,
)
.color(Color::Muted),
)
.child(Button::new(
self.primary_action.clone(),
self.primary_action.clone(),
cx,
)),
),
)

View file

@ -49,7 +49,7 @@ impl Component for SettingsContainer {
Some("A container for organizing and displaying settings in a structured manner.")
}
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
Some(
v_flex()
.gap_6()
@ -78,7 +78,7 @@ impl Component for SettingsContainer {
SettingsContainer::new()
.child(Label::new("Text Setting"))
.child(Checkbox::new("checkbox", ToggleState::Unselected))
.child(Button::new("button", "Click me"))
.child(Button::new("button", "Click me", cx))
.into_any_element(),
)],
),

View file

@ -165,7 +165,7 @@ impl Component for TabBar {
Some("A horizontal bar containing tabs for navigation between different views or sections.")
}
fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
fn preview(_window: &mut Window, cx: &mut App) -> Option<AnyElement> {
Some(
v_flex()
.gap_6()
@ -192,11 +192,11 @@ impl Component for TabBar {
vec![single_example(
"Full TabBar",
TabBar::new("full_tab_bar")
.start_child(Button::new("start_button", "Start"))
.start_child(Button::new("start_button", "Start", cx))
.child(Tab::new("tab1"))
.child(Tab::new("tab2"))
.child(Tab::new("tab3"))
.end_child(Button::new("end_button", "End"))
.end_child(Button::new("end_button", "End", cx))
.into_any_element(),
)],
),

Some files were not shown because too many files have changed in this diff Show more