use crossterm::event::{MouseEvent, MouseEventKind};
use tokio::sync::mpsc;
use crate::events::utils::refresh_install_details;
use crate::logic::move_sel_cached;
use crate::state::{AppState, PackageItem};
const fn is_in_rect(mx: u16, my: u16, rect: Option<(u16, u16, u16, u16)>) -> bool {
let Some((x, y, w, h)) = rect else {
return false;
};
mx >= x && mx < x + w && my >= y && my < y + h
}
fn handle_results_pane(
m: MouseEvent,
mx: u16,
my: u16,
is_left_down: bool,
app: &mut AppState,
details_tx: &mpsc::UnboundedSender<PackageItem>,
comments_tx: &mpsc::UnboundedSender<String>,
) -> bool {
if !is_in_rect(mx, my, app.results_rect) {
return false;
}
let (_, y, _, _) = app
.results_rect
.expect("results_rect should be Some when is_in_rect returns true");
let row = my.saturating_sub(y.saturating_add(1)) as usize;
if matches!(app.app_mode, crate::state::types::AppMode::News) {
let offset = app.news_list_state.offset();
let idx = offset + row;
if idx < app.news_results.len() && is_left_down {
app.news_selected = idx;
app.news_list_state.select(Some(idx));
crate::events::utils::update_news_url(app);
}
match m.kind {
MouseEventKind::ScrollUp => {
crate::events::utils::move_news_selection(app, -1);
true
}
MouseEventKind::ScrollDown => {
crate::events::utils::move_news_selection(app, 1);
true
}
_ => is_left_down,
}
} else {
if is_left_down {
let offset = app.list_state.offset();
let idx = offset + row;
if idx < app.results.len() {
app.selected = idx;
app.list_state.select(Some(idx));
let selected_name = app.results[app.selected].name.clone();
crate::logic::clear_stale_pkgbuild_checks_for_selection(
app,
selected_name.as_str(),
);
}
}
match m.kind {
MouseEventKind::ScrollUp => {
move_sel_cached(app, -1, details_tx, comments_tx);
true
}
MouseEventKind::ScrollDown => {
move_sel_cached(app, 1, details_tx, comments_tx);
true
}
_ => is_left_down,
}
}
}
fn handle_recent_pane(
m: MouseEvent,
mx: u16,
my: u16,
app: &mut AppState,
preview_tx: &mpsc::UnboundedSender<PackageItem>,
) -> bool {
if !is_in_rect(mx, my, app.recent_rect) {
return false;
}
let inds = crate::ui::helpers::filtered_recent_indices(app);
if inds.is_empty() {
return false;
}
match m.kind {
MouseEventKind::ScrollUp => {
if let Some(sel) = app.history_state.selected() {
let new = sel.saturating_sub(1);
app.history_state.select(Some(new));
crate::ui::helpers::trigger_recent_preview(app, preview_tx);
true
} else {
false
}
}
MouseEventKind::ScrollDown => {
let sel = app.history_state.selected().unwrap_or(0);
let max = inds.len().saturating_sub(1);
let new = std::cmp::min(sel.saturating_add(1), max);
app.history_state.select(Some(new));
crate::ui::helpers::trigger_recent_preview(app, preview_tx);
true
}
_ => false,
}
}
fn handle_install_click(
mx: u16,
my: u16,
app: &mut AppState,
details_tx: &mpsc::UnboundedSender<PackageItem>,
) -> Option<bool> {
if !is_in_rect(mx, my, app.install_rect) {
return None;
}
app.focus = crate::state::Focus::Install;
let (_, y, _, _) = app
.install_rect
.expect("install_rect should be Some when is_in_rect returns true");
let row = my.saturating_sub(y) as usize;
if app.installed_only_mode {
app.right_pane_focus = crate::state::RightPaneFocus::Remove;
let max = app.remove_list.len().saturating_sub(1);
if !app.remove_list.is_empty() {
let idx = std::cmp::min(row, max);
app.remove_state.select(Some(idx));
crate::events::utils::refresh_remove_details(app, details_tx);
}
} else {
app.right_pane_focus = crate::state::RightPaneFocus::Install;
let inds = crate::ui::helpers::filtered_install_indices(app);
if !inds.is_empty() {
let max = inds.len().saturating_sub(1);
let vis_idx = std::cmp::min(row, max);
app.install_state.select(Some(vis_idx));
crate::events::utils::refresh_install_details(app, details_tx);
}
}
Some(false)
}
fn handle_install_scroll(
m: MouseEvent,
mx: u16,
my: u16,
app: &mut AppState,
details_tx: &mpsc::UnboundedSender<PackageItem>,
) -> bool {
if !is_in_rect(mx, my, app.install_rect) {
return false;
}
if app.installed_only_mode {
let len = app.remove_list.len();
if len > 0 {
match m.kind {
MouseEventKind::ScrollUp => {
if let Some(sel) = app.remove_state.selected() {
let new = sel.saturating_sub(1);
app.remove_state.select(Some(new));
crate::events::utils::refresh_remove_details(app, details_tx);
true
} else {
false
}
}
MouseEventKind::ScrollDown => {
let sel = app.remove_state.selected().unwrap_or(0);
let max = len.saturating_sub(1);
let new = std::cmp::min(sel.saturating_add(1), max);
app.remove_state.select(Some(new));
crate::events::utils::refresh_remove_details(app, details_tx);
true
}
_ => false,
}
} else {
false
}
} else {
let inds = crate::ui::helpers::filtered_install_indices(app);
if inds.is_empty() {
false
} else {
match m.kind {
MouseEventKind::ScrollUp => {
if let Some(sel) = app.install_state.selected() {
let new = sel.saturating_sub(1);
app.install_state.select(Some(new));
refresh_install_details(app, details_tx);
true
} else {
false
}
}
MouseEventKind::ScrollDown => {
let sel = app.install_state.selected().unwrap_or(0);
let max = inds.len().saturating_sub(1);
let new = std::cmp::min(sel.saturating_add(1), max);
app.install_state.select(Some(new));
refresh_install_details(app, details_tx);
true
}
_ => false,
}
}
}
}
fn handle_downgrade_click(
mx: u16,
my: u16,
app: &mut AppState,
details_tx: &mpsc::UnboundedSender<PackageItem>,
) -> Option<bool> {
if !app.installed_only_mode || !is_in_rect(mx, my, app.downgrade_rect) {
return None;
}
app.focus = crate::state::Focus::Install;
app.right_pane_focus = crate::state::RightPaneFocus::Downgrade;
let (_, y, _, _) = app
.downgrade_rect
.expect("downgrade_rect should be Some when is_in_rect returns true");
let row = my.saturating_sub(y) as usize;
let max = app.downgrade_list.len().saturating_sub(1);
if !app.downgrade_list.is_empty() {
let idx = std::cmp::min(row, max);
app.downgrade_state.select(Some(idx));
crate::events::utils::refresh_downgrade_details(app, details_tx);
}
Some(false)
}
fn handle_downgrade_scroll(
m: MouseEvent,
mx: u16,
my: u16,
app: &mut AppState,
details_tx: &mpsc::UnboundedSender<PackageItem>,
) -> bool {
if !app.installed_only_mode || !is_in_rect(mx, my, app.downgrade_rect) {
return false;
}
let len = app.downgrade_list.len();
if len > 0 {
match m.kind {
MouseEventKind::ScrollUp => {
if let Some(sel) = app.downgrade_state.selected() {
let new = sel.saturating_sub(1);
app.downgrade_state.select(Some(new));
crate::events::utils::refresh_downgrade_details(app, details_tx);
true
} else {
false
}
}
MouseEventKind::ScrollDown => {
let sel = app.downgrade_state.selected().unwrap_or(0);
let max = len.saturating_sub(1);
let new = std::cmp::min(sel.saturating_add(1), max);
app.downgrade_state.select(Some(new));
crate::events::utils::refresh_downgrade_details(app, details_tx);
true
}
_ => false,
}
} else {
false
}
}
#[allow(clippy::missing_const_for_fn)]
fn handle_pkgbuild_scroll(m: MouseEvent, mx: u16, my: u16, app: &mut AppState) -> bool {
if !is_in_rect(mx, my, app.pkgb_rect) {
return false;
}
match m.kind {
MouseEventKind::ScrollUp => {
app.pkgb_scroll = app.pkgb_scroll.saturating_sub(1);
true
}
MouseEventKind::ScrollDown => {
app.pkgb_scroll = app.pkgb_scroll.saturating_add(1);
true
}
_ => false,
}
}
#[allow(clippy::missing_const_for_fn)]
fn handle_comments_scroll(m: MouseEvent, mx: u16, my: u16, app: &mut AppState) -> bool {
if !is_in_rect(mx, my, app.comments_rect) {
return false;
}
match m.kind {
MouseEventKind::ScrollUp => {
app.comments_scroll = app.comments_scroll.saturating_sub(1);
true
}
MouseEventKind::ScrollDown => {
app.comments_scroll = app.comments_scroll.saturating_add(1);
true
}
_ => false,
}
}
#[allow(clippy::too_many_arguments)]
pub(super) fn handle_panes_mouse(
m: MouseEvent,
mx: u16,
my: u16,
is_left_down: bool,
app: &mut AppState,
details_tx: &mpsc::UnboundedSender<PackageItem>,
preview_tx: &mpsc::UnboundedSender<PackageItem>,
comments_tx: &mpsc::UnboundedSender<String>,
) -> Option<bool> {
if is_left_down {
if let Some(result) = handle_install_click(mx, my, app, details_tx) {
return Some(result);
}
if let Some(result) = handle_downgrade_click(mx, my, app, details_tx) {
return Some(result);
}
}
handle_results_pane(m, mx, my, is_left_down, app, details_tx, comments_tx);
handle_recent_pane(m, mx, my, app, preview_tx);
handle_install_scroll(m, mx, my, app, details_tx);
handle_downgrade_scroll(m, mx, my, app, details_tx);
handle_pkgbuild_scroll(m, mx, my, app);
handle_comments_scroll(m, mx, my, app);
None
}