chore: move each view used in BottomPane into its own file (#928)

`BottomPane` was getting a bit unwieldy because it maintained a
`PaneState` enum with three variants and many of its methods had `match`
statements to handle each variant. To replace the enum, this PR:

* Introduces a `trait BottomPaneView` that has two implementations:
`StatusIndicatorView` and `ApprovalModalView`.
* Migrates `PaneState::TextInput` into its own struct, `ChatComposer`,
that does **not** implement `BottomPaneView`.
* Updates `BottomPane` so it has `composer: ChatComposer` and
`active_view: Option<Box<dyn BottomPaneView<'a> + 'a>>`. The idea is
that `active_view` takes priority and is displayed when it is `Some`;
otherwise, `ChatComposer` is displayed.
* While methods of `BottomPane` often have to check whether
`active_view` is present to decide which component to delegate to, the
code is more straightforward than before and introducing new
implementations of `BottomPaneView` should be less painful.

Because we want to retain the `TextArea` owned by `ChatComposer` even
when another view is displayed, to keep the ownership logic simple, it
seemed best to keep `ChatComposer` distinct from `BottomPaneView`.
This commit is contained in:
Michael Bolin
2025-05-14 10:13:29 -07:00
committed by GitHub
parent 399e819c9b
commit 0402aef126
8 changed files with 490 additions and 358 deletions

View File

@@ -0,0 +1,56 @@
use crossterm::event::KeyEvent;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use std::sync::mpsc::SendError;
use crate::app_event::AppEvent;
use crate::user_approval_widget::ApprovalRequest;
use super::BottomPane;
/// Type to use for a method that may require a redraw of the UI.
pub(crate) enum ConditionalUpdate {
NeedsRedraw,
NoRedraw,
}
/// Trait implemented by every view that can be shown in the bottom pane.
pub(crate) trait BottomPaneView<'a> {
/// Handle a key event while the view is active. A redraw is always
/// scheduled after this call.
fn handle_key_event(
&mut self,
pane: &mut BottomPane<'a>,
key_event: KeyEvent,
) -> Result<(), SendError<AppEvent>>;
/// Return `true` if the view has finished and should be removed.
fn is_complete(&self) -> bool {
false
}
/// Height required to render the view.
fn calculate_required_height(&self, area: &Rect) -> u16;
/// Render the view: this will be displayed in place of the composer.
fn render(&self, area: Rect, buf: &mut Buffer);
/// Update the status indicator text.
fn update_status_text(&mut self, _text: String) -> ConditionalUpdate {
ConditionalUpdate::NoRedraw
}
/// Called when task completes to check if the view should be hidden.
fn should_hide_when_task_is_done(&mut self) -> bool {
false
}
/// Try to handle approval request; return the original value if not
/// consumed.
fn try_consume_approval_request(
&mut self,
request: ApprovalRequest,
) -> Option<ApprovalRequest> {
Some(request)
}
}