use ratatui::layout::Position;
use tui_pane::FrameworkHit;
use tui_pane::FrameworkOverlayId;
use tui_pane::HitTestRegistry;
use tui_pane::Hittable;
use tui_pane::InputContext;
use tui_pane::ModalHit;
use tui_pane::ToastHit;
use tui_pane::Viewport;
use super::app::App;
use super::app::HoveredPaneRow;
use super::pane::DismissTarget;
use super::pane::HITTABLE_Z_ORDER;
use super::pane::HittableId;
use super::pane::HoverTarget;
use super::panes;
use super::panes::PaneId;
#[derive(Clone, Copy)]
pub(super) enum ClickMode {
FocusOnly,
Dispatch,
}
fn toggle_selected_project_expansion(app: &mut App) -> bool {
let selected = app.project_list.cursor();
let Some(row) = app.visible_rows().get(selected).copied() else {
return false;
};
let include_non_rust = app.config.include_non_rust().includes_non_rust();
app.project_list
.toggle_expand_for_row(row, include_non_rust)
}
pub(super) fn handle_click(app: &mut App, pos: Position, mode: ClickMode) -> bool {
let Some(hit) = hit_test_at(app, pos) else {
return false;
};
match hit {
HoverTarget::PaneRow { pane, row } => {
app.set_focus_to_pane(pane);
if pane == PaneId::ProjectList {
app.project_list.set_cursor(row);
if matches!(mode, ClickMode::Dispatch) {
toggle_selected_project_expansion(app);
}
} else {
set_pane_pos(app, pane, row);
}
if pane == PaneId::Targets {
panes::sync_running_targets_cursor(app);
if matches!(mode, ClickMode::Dispatch) {
panes::toggle_targets_tree_row(app);
}
}
true
},
HoverTarget::Dismiss(target) => {
if matches!(mode, ClickMode::FocusOnly) {
return true;
}
app.dismiss(target);
true
},
HoverTarget::ToastCard(id) => {
let active = app.framework.toasts.active_now();
if let Some(index) = active.iter().position(|toast| toast.id() == id) {
app.framework.toasts.viewport.set_pos(index);
app.set_focus_to_pane(PaneId::Toasts);
}
true
},
}
}
pub(super) fn hovered_pane_row_at(app: &App, pos: Position) -> Option<HoveredPaneRow> {
match hit_test_at(app, pos)? {
HoverTarget::PaneRow { pane, row } => Some(HoveredPaneRow { pane, row }),
HoverTarget::Dismiss(_) | HoverTarget::ToastCard(_) => None,
}
}
pub(super) fn hit_test_at(app: &App, pos: Position) -> Option<HoverTarget> {
tui_pane::dispatch_hit_test(app, pos)
}
impl HitTestRegistry for App {
type PaneId = HittableId;
type Target = HoverTarget;
fn z_order() -> &'static [HittableId] { &HITTABLE_Z_ORDER }
fn pane(&self, id: HittableId) -> Option<&dyn Hittable<HoverTarget>> {
Some(match id {
HittableId::ProjectList => &self.panes.project_list,
HittableId::Package => &self.panes.package,
HittableId::Lang => &self.panes.lang,
HittableId::Cpu => &self.panes.cpu,
HittableId::Git => &self.panes.git,
HittableId::Targets => &self.panes.targets,
HittableId::Lints => &self.lint,
HittableId::CiRuns => &self.ci,
HittableId::Output => &self.panes.output,
})
}
fn viewport_mut(&mut self, id: HittableId) -> Option<&mut Viewport> {
Some(match id {
HittableId::ProjectList => &mut self.panes.project_list.viewport,
HittableId::Package => &mut self.panes.package.viewport,
HittableId::Lang => &mut self.panes.lang.viewport,
HittableId::Cpu => &mut self.panes.cpu.viewport,
HittableId::Git => &mut self.panes.git.viewport,
HittableId::Targets => &mut self.panes.targets.viewport,
HittableId::Lints => &mut self.lint.viewport,
HittableId::CiRuns => &mut self.ci.viewport,
HittableId::Output => &mut self.panes.output.viewport,
})
}
}
impl InputContext for App {
fn framework_hit(&self, pos: Position) -> Option<FrameworkHit> {
self.framework.hit_test_at(pos)
}
fn app_modal_overlay_hit(&self, pos: Position) -> ModalHit<HoverTarget> {
if self.overlays.is_sccache_open() {
return self
.overlays
.sccache_pane
.hit_test_at(pos)
.map_or(ModalHit::MissedRow, ModalHit::Hit);
}
if !self.overlays.is_finder_open() {
return ModalHit::Closed;
}
self.overlays
.finder_pane
.hit_test_at(pos)
.map_or(ModalHit::MissedRow, ModalHit::Hit)
}
fn map_framework_hit(&self, hit: FrameworkHit) -> Option<HoverTarget> {
match hit {
FrameworkHit::Toast(ToastHit::Close(id)) => {
Some(HoverTarget::Dismiss(DismissTarget::Toast(id)))
},
FrameworkHit::Toast(ToastHit::Card(id)) => Some(HoverTarget::ToastCard(id)),
FrameworkHit::Overlay {
id: FrameworkOverlayId::Keymap,
row,
} => Some(HoverTarget::PaneRow {
pane: PaneId::Keymap,
row,
}),
FrameworkHit::Overlay {
id: FrameworkOverlayId::Settings,
row,
} => Some(HoverTarget::PaneRow {
pane: PaneId::Settings,
row,
}),
FrameworkHit::Overlay {
id: FrameworkOverlayId::GlobalShortcuts,
..
}
| FrameworkHit::ModalMissed => None,
}
}
}
pub(super) const fn set_pane_pos(app: &mut App, id: PaneId, row: usize) {
match id {
PaneId::ProjectList => {},
PaneId::Toasts => app.framework.toasts.viewport.set_pos(row),
PaneId::Keymap => app.framework.keymap_pane.viewport_mut().set_pos(row),
PaneId::Settings => app.framework.settings_pane.viewport_mut().set_pos(row),
PaneId::Sccache => app.overlays.sccache_pane.viewport_mut().set_pos(row),
_ => {
if let Some(viewport) = viewport_mut_for(app, id) {
viewport.set_pos(row);
}
},
}
}
pub(super) const fn viewport_mut_for(app: &mut App, id: PaneId) -> Option<&mut Viewport> {
let viewport = match id {
PaneId::Cpu => &mut app.panes.cpu.viewport,
PaneId::Lang => &mut app.panes.lang.viewport,
PaneId::Lints => &mut app.lint.viewport,
PaneId::CiRuns => &mut app.ci.viewport,
PaneId::Package => &mut app.panes.package.viewport,
PaneId::Git => &mut app.panes.git.viewport,
PaneId::Finder => &mut app.overlays.finder_pane.viewport,
PaneId::Sccache => &mut app.overlays.sccache_pane.viewport,
PaneId::Output => &mut app.panes.output.viewport,
PaneId::Targets => &mut app.panes.targets.viewport,
PaneId::ProjectList => &mut app.panes.project_list.viewport,
PaneId::Keymap | PaneId::Settings | PaneId::Toasts => return None,
};
Some(viewport)
}
const fn set_hovered(app: &mut App, pane: PaneId, row: Option<usize>) {
match pane {
PaneId::Toasts => app.framework.toasts.viewport.set_hovered(row),
PaneId::Keymap => app.framework.keymap_pane.viewport_mut().set_hovered(row),
PaneId::Settings => app.framework.settings_pane.viewport_mut().set_hovered(row),
PaneId::Sccache => app.overlays.sccache_pane.viewport_mut().set_hovered(row),
_ => {
if let Some(viewport) = viewport_mut_for(app, pane) {
viewport.set_hovered(row);
}
},
}
}
pub(super) fn apply_hovered_pane_row(app: &mut App) {
app.framework.clear_hover();
app.overlays.finder_pane.viewport.set_hovered(None);
app.overlays.sccache_pane.viewport.set_hovered(None);
app.panes.output.viewport.set_hovered(None);
tui_pane::clear_all_hover(app);
if let Some(hovered) = app.panes.hovered_row() {
set_hovered(app, hovered.pane, Some(hovered.row));
}
}