mirror of
https://github.com/openai/codex.git
synced 2026-04-24 14:45:27 +00:00
focus changing
This commit is contained in:
@@ -124,6 +124,9 @@ impl App {
|
||||
TuiEvent::Key(key_event) => {
|
||||
self.handle_key_event(tui, key_event).await;
|
||||
}
|
||||
TuiEvent::FocusChanged(focused) => {
|
||||
self.chat_widget.set_input_focus(focused);
|
||||
}
|
||||
TuiEvent::Paste(pasted) => {
|
||||
// Many terminals convert newlines to \r when pasting (e.g., iTerm2),
|
||||
// but tui-textarea expects \n. Normalize CR to LF.
|
||||
|
||||
@@ -1236,30 +1236,35 @@ impl WidgetRef for ChatComposer {
|
||||
ActivePopup::None => {
|
||||
let bottom_line_rect = popup_rect;
|
||||
let key_hint_style = Style::default().fg(Color::Cyan);
|
||||
let mut hint = if self.ctrl_c_quit_hint {
|
||||
vec![
|
||||
Span::from(" "),
|
||||
"Ctrl+C again".set_style(key_hint_style),
|
||||
Span::from(" to quit"),
|
||||
]
|
||||
} else {
|
||||
let newline_hint_key = if self.use_shift_enter_hint {
|
||||
"Shift+⏎"
|
||||
|
||||
// Start with a visible focus state label.
|
||||
let mut hint: Vec<Span> = vec![
|
||||
Span::from(" "),
|
||||
if self.has_focus { "Focused".green().into() } else { "Unfocused".magenta().into() },
|
||||
Span::from(" "),
|
||||
];
|
||||
|
||||
// Append the usual key hints.
|
||||
hint.extend(
|
||||
if self.ctrl_c_quit_hint {
|
||||
vec![
|
||||
"Ctrl+C again".set_style(key_hint_style).into(),
|
||||
Span::from(" to quit"),
|
||||
]
|
||||
} else {
|
||||
"Ctrl+J"
|
||||
};
|
||||
vec![
|
||||
Span::from(" "),
|
||||
"⏎".set_style(key_hint_style),
|
||||
Span::from(" send "),
|
||||
newline_hint_key.set_style(key_hint_style),
|
||||
Span::from(" newline "),
|
||||
"Ctrl+T".set_style(key_hint_style),
|
||||
Span::from(" transcript "),
|
||||
"Ctrl+C".set_style(key_hint_style),
|
||||
Span::from(" quit"),
|
||||
]
|
||||
};
|
||||
let newline_hint_key = if self.use_shift_enter_hint { "Shift+⏎" } else { "Ctrl+J" };
|
||||
vec![
|
||||
"⏎".set_style(key_hint_style).into(),
|
||||
Span::from(" send "),
|
||||
newline_hint_key.set_style(key_hint_style).into(),
|
||||
Span::from(" newline "),
|
||||
"Ctrl+T".set_style(key_hint_style).into(),
|
||||
Span::from(" transcript "),
|
||||
"Ctrl+C".set_style(key_hint_style).into(),
|
||||
Span::from(" quit"),
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
if !self.ctrl_c_quit_hint && self.esc_backtrack_hint {
|
||||
hint.push(Span::from(" "));
|
||||
|
||||
@@ -99,6 +99,16 @@ impl BottomPane {
|
||||
}
|
||||
}
|
||||
|
||||
/// Update whether the bottom pane's composer has input focus.
|
||||
pub(crate) fn set_has_input_focus(&mut self, has_focus: bool) {
|
||||
self.has_input_focus = has_focus;
|
||||
// Use existing API to propagate focus to the composer without changing the
|
||||
// current Ctrl-C hint visibility.
|
||||
self.composer
|
||||
.set_ctrl_c_quit_hint(self.ctrl_c_quit_hint, self.has_input_focus);
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub fn desired_height(&self, width: u16) -> u16 {
|
||||
let top_margin = if self.active_view.is_some() { 0 } else { 1 };
|
||||
|
||||
|
||||
@@ -693,6 +693,12 @@ impl ChatWidget {
|
||||
.map_or(0, |c| c.desired_height(width))
|
||||
}
|
||||
|
||||
/// Update input focus state for the bottom pane/composer.
|
||||
pub(crate) fn set_input_focus(&mut self, has_focus: bool) {
|
||||
self.bottom_pane.set_has_input_focus(has_focus);
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub(crate) fn handle_key_event(&mut self, key_event: KeyEvent) {
|
||||
match key_event {
|
||||
KeyEvent {
|
||||
|
||||
@@ -18,7 +18,9 @@ use crossterm::SynchronizedUpdate;
|
||||
use crossterm::cursor;
|
||||
use crossterm::cursor::MoveTo;
|
||||
use crossterm::event::DisableBracketedPaste;
|
||||
use crossterm::event::DisableFocusChange;
|
||||
use crossterm::event::EnableBracketedPaste;
|
||||
use crossterm::event::EnableFocusChange;
|
||||
use crossterm::event::Event;
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
@@ -49,6 +51,8 @@ pub type Terminal = CustomTerminal<CrosstermBackend<Stdout>>;
|
||||
|
||||
pub fn set_modes() -> Result<()> {
|
||||
execute!(stdout(), EnableBracketedPaste)?;
|
||||
// Enable focus change reporting where supported; ignore errors on unsupported terminals.
|
||||
let _ = execute!(stdout(), EnableFocusChange);
|
||||
|
||||
enable_raw_mode()?;
|
||||
// Enable keyboard enhancement flags so modifiers for keys like Enter are disambiguated.
|
||||
@@ -116,6 +120,8 @@ pub fn restore() -> Result<()> {
|
||||
// Pop may fail on platforms that didn't support the push; ignore errors.
|
||||
let _ = execute!(stdout(), PopKeyboardEnhancementFlags);
|
||||
execute!(stdout(), DisableBracketedPaste)?;
|
||||
// Disable focus change reporting if it was enabled; ignore errors.
|
||||
let _ = execute!(stdout(), DisableFocusChange);
|
||||
disable_raw_mode()?;
|
||||
let _ = execute!(stdout(), crossterm::cursor::Show);
|
||||
Ok(())
|
||||
@@ -154,6 +160,8 @@ pub enum TuiEvent {
|
||||
Key(KeyEvent),
|
||||
Paste(String),
|
||||
Draw,
|
||||
/// Terminal focus changed: true when focused, false when unfocused.
|
||||
FocusChanged(bool),
|
||||
AttachImage {
|
||||
path: PathBuf,
|
||||
width: u32,
|
||||
@@ -323,12 +331,12 @@ impl Tui {
|
||||
}
|
||||
Err(_) => {
|
||||
// Fall back to normal key handling if no image is available.
|
||||
yield TuiEvent::Key(key_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
yield TuiEvent::Key(key_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crossterm::event::Event::Key(key_event) => {
|
||||
crossterm::event::Event::Key(key_event) => {
|
||||
#[cfg(unix)]
|
||||
if matches!(
|
||||
key_event,
|
||||
@@ -363,6 +371,12 @@ impl Tui {
|
||||
Event::Resize(_, _) => {
|
||||
yield TuiEvent::Draw;
|
||||
}
|
||||
Event::FocusGained => {
|
||||
yield TuiEvent::FocusChanged(true);
|
||||
}
|
||||
Event::FocusLost => {
|
||||
yield TuiEvent::FocusChanged(false);
|
||||
}
|
||||
Event::Paste(pasted) => {
|
||||
yield TuiEvent::Paste(pasted);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user