input: Disable context menu item for disabled input. (#1280)

This commit is contained in:
Jason Lee 2025-09-25 16:36:57 +08:00 committed by GitHub
parent 16105da122
commit a6a14fd57b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 70 additions and 67 deletions

View file

@ -17,24 +17,25 @@ struct Info(usize);
actions!(menu_story, [Copy, Paste, Cut, SearchAll, ToggleCheck]);
const CONTEXT: &str = "menu_story";
pub fn init(cx: &mut App) {
cx.bind_keys([
#[cfg(target_os = "macos")]
KeyBinding::new("cmd-c", Copy, None),
KeyBinding::new("cmd-c", Copy, Some(CONTEXT)),
#[cfg(not(target_os = "macos"))]
KeyBinding::new("ctrl-c", Copy, None),
KeyBinding::new("ctrl-c", Copy, Some(CONTEXT)),
#[cfg(target_os = "macos")]
KeyBinding::new("cmd-v", Paste, None),
KeyBinding::new("cmd-v", Paste, Some(CONTEXT)),
#[cfg(not(target_os = "macos"))]
KeyBinding::new("ctrl-v", Paste, None),
KeyBinding::new("ctrl-v", Paste, Some(CONTEXT)),
#[cfg(target_os = "macos")]
KeyBinding::new("cmd-x", Cut, None),
KeyBinding::new("cmd-x", Cut, Some(CONTEXT)),
#[cfg(not(target_os = "macos"))]
KeyBinding::new("ctrl-x", Cut, None),
KeyBinding::new("ctrl-x", Cut, Some(CONTEXT)),
#[cfg(target_os = "macos")]
KeyBinding::new("cmd-shift-f", SearchAll, None),
KeyBinding::new("cmd-shift-f", SearchAll, Some(CONTEXT)),
#[cfg(not(target_os = "macos"))]
KeyBinding::new("ctrl-shift-f", SearchAll, None),
KeyBinding::new("ctrl-shift-f", SearchAll, Some(CONTEXT)),
])
}
@ -116,6 +117,7 @@ impl Render for MenuStory {
let checked = self.checked;
v_flex()
.key_context(CONTEXT)
.track_focus(&self.focus_handle)
.on_action(cx.listener(Self::on_copy))
.on_action(cx.listener(Self::on_cut))

View file

@ -18,25 +18,25 @@ use serde::Deserialize;
struct Info(usize);
actions!(popover_story, [Copy, Paste, Cut, SearchAll, ToggleCheck]);
const CONTEXT: &str = "popover-story";
pub fn init(cx: &mut App) {
cx.bind_keys([
#[cfg(target_os = "macos")]
KeyBinding::new("cmd-c", Copy, None),
KeyBinding::new("cmd-c", Copy, Some(CONTEXT)),
#[cfg(not(target_os = "macos"))]
KeyBinding::new("ctrl-c", Copy, None),
KeyBinding::new("ctrl-c", Copy, Some(CONTEXT)),
#[cfg(target_os = "macos")]
KeyBinding::new("cmd-v", Paste, None),
KeyBinding::new("cmd-v", Paste, Some(CONTEXT)),
#[cfg(not(target_os = "macos"))]
KeyBinding::new("ctrl-v", Paste, None),
KeyBinding::new("ctrl-v", Paste, Some(CONTEXT)),
#[cfg(target_os = "macos")]
KeyBinding::new("cmd-x", Cut, None),
KeyBinding::new("cmd-x", Cut, Some(CONTEXT)),
#[cfg(not(target_os = "macos"))]
KeyBinding::new("ctrl-x", Cut, None),
KeyBinding::new("ctrl-x", Cut, Some(CONTEXT)),
#[cfg(target_os = "macos")]
KeyBinding::new("cmd-shift-f", SearchAll, None),
KeyBinding::new("cmd-shift-f", SearchAll, Some(CONTEXT)),
#[cfg(not(target_os = "macos"))]
KeyBinding::new("ctrl-shift-f", SearchAll, None),
KeyBinding::new("ctrl-shift-f", SearchAll, Some(CONTEXT)),
])
}
@ -159,6 +159,7 @@ impl Render for PopoverStory {
let form = self.form.clone();
v_flex()
.key_context(CONTEXT)
.track_focus(&self.focus_handle)
.on_action(cx.listener(Self::on_copy))
.on_action(cx.listener(Self::on_cut))

View file

@ -1,7 +1,7 @@
use gpui::{
anchored, deferred, div, prelude::FluentBuilder as _, px, App, AppContext as _, Context,
DismissEvent, Entity, IntoElement, MouseDownEvent, ParentElement as _, Pixels, Point, Render,
Styled, Subscription, Window,
Corner, DismissEvent, Entity, IntoElement, MouseDownEvent, ParentElement as _, Pixels, Point,
Render, Styled, Subscription, Window,
};
use rust_i18n::t;
@ -40,11 +40,13 @@ impl InputState {
self.handle_hover_definition(offset, window, cx);
}
let has_goto_definition = self.lsp.definition_provider.is_some();
let has_code_action = !self.lsp.code_action_providers.is_empty();
let is_selected = !self.selected_range.is_empty();
let has_paste = cx.read_from_clipboard().is_some();
let is_enable = !self.disabled;
let has_goto_definition = is_enable && self.lsp.definition_provider.is_some();
let has_code_action = is_enable && !self.lsp.code_action_providers.is_empty();
let is_selected = is_enable && !self.selected_range.is_empty();
let has_paste = is_enable && cx.read_from_clipboard().is_some();
let action_context = self.focus_handle.clone();
self.mouse_context_menu.update(cx, |this, cx| {
this.mouse_position = event.position;
this.menu.update(cx, |menu, cx| {
@ -62,13 +64,18 @@ impl InputState {
)
.separator()
})
.menu_with_enable(t!("Input.Cut"), Box::new(input::Cut), is_selected)
.menu_with_enable(
t!("Input.Cut"),
Box::new(input::Cut),
is_enable && is_selected,
)
.menu_with_enable(t!("Input.Copy"), Box::new(input::Copy), is_selected)
.menu_with_enable(t!("Input.Paste"), Box::new(input::Paste), has_paste)
.separator()
.menu(t!("Input.Select All"), Box::new(input::SelectAll));
menu.menu_items = new_menu.menu_items;
menu.action_context = Some(action_context);
cx.notify();
});
this.open = true;
@ -123,13 +130,11 @@ impl Render for MouseContextMenu {
return div().into_any_element();
}
let pos = self.mouse_position;
deferred(
anchored()
.snap_to_window_with_margin(px(8.))
.anchor(gpui::Corner::TopLeft)
.position(pos)
.anchor(Corner::TopLeft)
.position(self.mouse_position)
.child(
div()
.font_family(".SystemUIFont")

View file

@ -45,18 +45,18 @@ impl Kbd {
window: &Window,
) -> Option<Self> {
let key_context = context.and_then(|context| KeyContext::parse(context).ok());
let bindings = match key_context {
Some(context) => window.bindings_for_action_in_context(action, context),
None => window.bindings_for_action(action),
};
bindings.first().and_then(|binding| {
if let Some(key) = binding.keystrokes().first() {
Some(Self::new(key.as_keystroke().clone()))
} else {
None
let binding = match key_context {
Some(context) => {
window.highest_precedence_binding_for_action_in_context(action, context)
}
})
None => window.highest_precedence_binding_for_action(action),
}?;
if let Some(key) = binding.keystrokes().first() {
Some(Self::new(key.as_keystroke().clone()))
} else {
None
}
}
/// Return the Platform specific keybinding string by KeyStroke
@ -208,6 +208,8 @@ impl RenderOnce for Kbd {
.rounded_sm()
.line_height(relative(1.))
.text_xs()
.whitespace_normal()
.flex_shrink_0()
.refine_style(&self.style)
.child(Self::format(&self.stroke))
.into_any_element()

View file

@ -13,8 +13,7 @@ use gpui::{
InteractiveElement, IntoElement, KeyBinding, ParentElement, Pixels, Render, ScrollHandle,
SharedString, StatefulInteractiveElement, Styled, WeakEntity, Window,
};
use gpui::{AsKeystroke, Half, MouseDownEvent, Subscription};
use std::ops::Deref;
use gpui::{Half, MouseDownEvent, Subscription};
use std::rc::Rc;
const CONTEXT: &str = "PopupMenu";
@ -109,10 +108,10 @@ impl PopupMenuItem {
}
pub struct PopupMenu {
/// The parent menu of this menu, if this is a submenu
parent_menu: Option<WeakEntity<Self>>,
focus_handle: FocusHandle,
pub(crate) menu_items: Vec<PopupMenuItem>,
/// The focus handle of Entity to handle actions.
pub(crate) action_context: Option<FocusHandle>,
has_icon: bool,
selected_index: Option<usize>,
min_width: Option<Pixels>,
@ -121,12 +120,13 @@ pub struct PopupMenu {
bounds: Bounds<Pixels>,
size: Size,
/// The parent menu of this menu, if this is a submenu
parent_menu: Option<WeakEntity<Self>>,
scrollable: bool,
external_link_icon: bool,
scroll_handle: ScrollHandle,
scroll_state: ScrollbarState,
previous_focus_handle: Option<FocusHandle>,
_subscriptions: Vec<Subscription>,
}
@ -134,7 +134,7 @@ impl PopupMenu {
pub(crate) fn new(cx: &mut App) -> Self {
Self {
focus_handle: cx.focus_handle(),
previous_focus_handle: None,
action_context: None,
parent_menu: None,
menu_items: Vec::new(),
selected_index: None,
@ -159,7 +159,7 @@ impl PopupMenu {
) -> Entity<Self> {
cx.new(|cx| {
let mut menu = Self::new(cx);
menu.previous_focus_handle = window.focused(cx);
menu.action_context = window.focused(cx);
f(menu, window, cx)
})
}
@ -725,8 +725,8 @@ impl PopupMenu {
cx.emit(DismissEvent);
// Focus back to the previous focused handle.
if let Some(previous_focus_handle) = self.previous_focus_handle.as_ref() {
window.focus(previous_focus_handle);
if let Some(action_context) = self.action_context.as_ref() {
window.focus(action_context);
}
let Some(parent_menu) = self.parent_menu.clone() else {
@ -743,22 +743,15 @@ impl PopupMenu {
fn render_key_binding(
action: Option<Box<dyn Action>>,
window: &mut Window,
cx: &mut Context<Self>,
_: &mut Context<Self>,
) -> Option<impl IntoElement> {
if let Some(action) = action {
if let Some(key_binding) = window.bindings_for_action(action.deref()).first() {
let el = div().text_color(cx.theme().muted_foreground).children(
key_binding
.keystrokes()
.into_iter()
.map(|key| Kbd::format(key.as_keystroke())),
);
return Some(el);
}
}
return None;
let action = action?;
Kbd::binding_for_action(action.as_ref(), None, window).map(|this| {
this.p_0()
.flex_nowrap()
.border_0()
.bg(gpui::transparent_white())
})
}
fn render_icon(

View file

@ -253,7 +253,7 @@
"list.active.border": "#36A3D9",
"list.even.background": "#191F2A99",
"muted.background": "#1F2430",
"muted.foreground": "#8a8986ff",
"muted.foreground": "#6a6967",
"panel.background": "#1F2126",
"popover.background": "#1F2127",
"popover.foreground": "#B3B1AD",
@ -263,10 +263,10 @@
"primary.hover.background": "#3DAEE9",
"scrollbar.background": "#0A0E1400",
"scrollbar.thumb.background": "#bfbdb64c",
"secondary.active.background": "#191d24",
"secondary.background": "#1f2127",
"secondary.active.background": "#2D2F34",
"secondary.background": "#2D2F34",
"secondary.foreground": "#B3B1AD",
"secondary.hover.background": "#191d2499",
"secondary.hover.background": "#2D2F3499",
"tab.active.foreground": "#B3B1AD",
"tab.background": "#1F2127",
"tab.foreground": "#82807d",