diff --git a/src/dialogs.rs b/src/dialogs.rs index 5105a8c..73f142a 100644 --- a/src/dialogs.rs +++ b/src/dialogs.rs @@ -31,7 +31,7 @@ impl<'a> FileDialog<'a> { pub fn fwd(&mut self, steps: usize) { let i = match self.state.selected() { - Some(i) => std::cmp::min(i.saturating_add(steps), self.dirs.len() - 1), + Some(i) => std::cmp::min(i.saturating_add(steps), self.dirs.len().saturating_sub(1)), None => 0, }; self.state.select(Some(i)); diff --git a/src/main.rs b/src/main.rs index 8ab9664..d0772de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -793,8 +793,8 @@ fn run( let mut app = None; if file_dialog.error_message.is_some() { - if let Event::Input(event) = next_event() { - match event.code { + match next_event() { + Event::Input(event) => match event.code { KeyCode::Enter | KeyCode::Esc => { file_dialog.clear_error(); } @@ -805,61 +805,77 @@ fn run( break; } _ => {} + }, + Event::Update => { + let now = Instant::now(); + if next_repo_refresh.get() <= now { + next_repo_refresh.set(now + repo_refresh_interval); + } } } - } else if let Event::Input(event) = next_event() { - match event.code { - KeyCode::Char('q') => { - disable_raw_mode()?; - execute!(terminal.backend_mut(), LeaveAlternateScreen)?; - terminal.show_cursor()?; - break; - } - KeyCode::Char('o') if event.modifiers.contains(KeyModifiers::CONTROL) => { - if let Some(prev_app) = file_dialog.previous_app.take() { - app = Some(prev_app); - } else { - file_dialog.set_error("No repository to return to.\nSelect a Git rrpository or quit with Q.".to_string()) + } else { + match next_event() { + Event::Input(event) => match event.code { + KeyCode::Char('q') => { + disable_raw_mode()?; + execute!(terminal.backend_mut(), LeaveAlternateScreen)?; + terminal.show_cursor()?; + break; } - } - KeyCode::Esc => { - if let Some(prev_app) = file_dialog.previous_app.take() { - app = Some(prev_app); - } else { - file_dialog.set_error("No repository to return to.\nSelect a Git rrpository or quit with Q.".to_string()) + KeyCode::Char('o') if event.modifiers.contains(KeyModifiers::CONTROL) => { + if let Some(prev_app) = file_dialog.previous_app.take() { + app = Some(prev_app); + } else { + file_dialog.set_error("No repository to return to.\nSelect a Git rrpository or quit with Q.".to_string()) + } } - } - KeyCode::Up => file_dialog.on_up(event.modifiers.contains(KeyModifiers::SHIFT)), - KeyCode::Down => { - file_dialog.on_down(event.modifiers.contains(KeyModifiers::SHIFT)) - } - KeyCode::Left => file_dialog.on_left()?, - KeyCode::Right => file_dialog.on_right()?, - KeyCode::Enter => { - file_dialog.on_enter(); - if let Some(path) = &file_dialog.selection { - match get_repo(path) { - Ok(repo) => { - if repo.is_shallow() { - file_dialog.set_error(format!("{} is a shallow clone. Shallow clones are not supported due to a missing feature in the underlying libgit2 library.", repo.path().parent().unwrap().display())); - } else { - app = Some(create_app( - repo, - &mut settings, - &app_settings, - model, - max_commits, - )?) + KeyCode::Esc => { + if let Some(prev_app) = file_dialog.previous_app.take() { + app = Some(prev_app); + } else { + file_dialog.set_error("No repository to return to.\nSelect a Git rrpository or quit with Q.".to_string()) + } + } + KeyCode::Up => { + file_dialog.on_up(event.modifiers.contains(KeyModifiers::SHIFT)) + } + KeyCode::Down => { + file_dialog.on_down(event.modifiers.contains(KeyModifiers::SHIFT)) + } + KeyCode::Left => file_dialog.on_left()?, + KeyCode::Right => file_dialog.on_right()?, + KeyCode::Enter => { + file_dialog.on_enter(); + if let Some(path) = &file_dialog.selection { + match get_repo(path) { + Ok(repo) => { + if repo.is_shallow() { + file_dialog.set_error(format!("{} is a shallow clone. Shallow clones are not supported due to a missing feature in the underlying libgit2 library.", repo.path().parent().unwrap().display())); + } else { + app = Some(create_app( + repo, + &mut settings, + &app_settings, + model, + max_commits, + )?) + } } - } - Err(_) => { - file_dialog.on_right()?; - } - }; + Err(_) => { + file_dialog.on_right()?; + } + }; + } + } + _ => {} + }, + Event::Update => { + let now = Instant::now(); + if next_repo_refresh.get() <= now { + next_repo_refresh.set(now + repo_refresh_interval); } } - _ => {} - }; + } } app }; diff --git a/src/widgets/graph_view.rs b/src/widgets/graph_view.rs index f701d6b..910467b 100644 --- a/src/widgets/graph_view.rs +++ b/src/widgets/graph_view.rs @@ -29,9 +29,12 @@ impl GraphViewState { pub fn move_selection(&mut self, steps: usize, down: bool) -> bool { let changed = if let Some(sel) = self.selected { let new_idx = if down { - std::cmp::min(sel.saturating_add(steps), self.indices.len() - 1) + std::cmp::min( + sel.saturating_add(steps), + self.indices.len().saturating_sub(1), + ) } else { - std::cmp::max(sel.saturating_sub(steps), 0) + sel.saturating_sub(steps) }; self.selected = Some(new_idx); new_idx != sel @@ -49,18 +52,24 @@ impl GraphViewState { pub fn move_secondary_selection(&mut self, steps: usize, down: bool) -> bool { let changed = if let Some(sel) = self.secondary_selected { let new_idx = if down { - std::cmp::min(sel.saturating_add(steps), self.indices.len() - 1) + std::cmp::min( + sel.saturating_add(steps), + self.indices.len().saturating_sub(1), + ) } else { - std::cmp::max(sel.saturating_sub(steps), 0) + sel.saturating_sub(steps) }; self.secondary_selected = Some(new_idx); new_idx != sel } else if !self.graph_lines.is_empty() { if let Some(sel) = self.selected { let new_idx = if down { - std::cmp::min(sel.saturating_add(steps), self.indices.len() - 1) + std::cmp::min( + sel.saturating_add(steps), + self.indices.len().saturating_sub(1), + ) } else { - std::cmp::max(sel.saturating_sub(steps), 0) + sel.saturating_sub(steps) }; self.secondary_selected = Some(new_idx); new_idx != sel @@ -131,7 +140,7 @@ impl StatefulWidget for GraphView<'_> { return; } - if state.graph_lines.is_empty() { + if state.graph_lines.is_empty() || state.indices.is_empty() { return; } let list_height = list_area.height as usize; @@ -144,18 +153,23 @@ impl StatefulWidget for GraphView<'_> { ); let mut end = start + height; - let selected_row = state.selected.map(|idx| state.indices[idx]); - let selected = selected_row.unwrap_or(0).min(state.graph_lines.len() - 1); + let max_graph_idx = state.graph_lines.len().saturating_sub(1); + let max_indices_idx = state.indices.len().saturating_sub(1); - let secondary_selected_row = state.secondary_selected.map(|idx| state.indices[idx]); - let secondary_selected = secondary_selected_row - .unwrap_or(0) - .min(state.graph_lines.len() - 1); + let selected_row = state + .selected + .and_then(|idx| state.indices.get(idx).copied()); + let selected = selected_row.unwrap_or(0).min(max_graph_idx); + + let secondary_selected_row = state + .secondary_selected + .and_then(|idx| state.indices.get(idx).copied()); + let secondary_selected = secondary_selected_row.unwrap_or(0).min(max_graph_idx); let selected_index = if state.secondary_changed { - state.secondary_selected.unwrap_or(0) + state.secondary_selected.unwrap_or(0).min(max_indices_idx) } else { - state.selected.unwrap_or(0) + state.selected.unwrap_or(0).min(max_indices_idx) }; let move_to_selected = if state.secondary_changed { secondary_selected @@ -163,12 +177,13 @@ impl StatefulWidget for GraphView<'_> { selected }; - let move_to_end = if selected_index >= state.indices.len() - 1 { - state.graph_lines.len() - 1 + let move_to_end = if selected_index >= max_indices_idx { + max_graph_idx } else { - (state.indices[selected_index + 1] - 1) + state.indices[selected_index + 1] + .saturating_sub(1) .max(move_to_selected + SCROLL_MARGIN) - .min(state.graph_lines.len() - 1) + .min(max_graph_idx) }; let move_to_start = move_to_selected.saturating_sub(SCROLL_MARGIN); diff --git a/src/widgets/models_view.rs b/src/widgets/models_view.rs index bf31979..848325d 100644 --- a/src/widgets/models_view.rs +++ b/src/widgets/models_view.rs @@ -17,7 +17,7 @@ impl ModelListState { pub fn fwd(&mut self, steps: usize) { let i = match self.state.selected() { - Some(i) => std::cmp::min(i.saturating_add(steps), self.models.len() - 1), + Some(i) => std::cmp::min(i.saturating_add(steps), self.models.len().saturating_sub(1)), None => 0, }; self.state.select(Some(i));