mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-21 12:59:41 +00:00
Merge e583fc7864
into fa69885b6c
This commit is contained in:
commit
85b4a171a7
|
@ -3,7 +3,7 @@ use std::borrow::Cow;
|
|||
use crate::{float::FloatContent, hint::Shortcut};
|
||||
|
||||
use ratatui::{
|
||||
crossterm::event::{KeyCode, KeyEvent, MouseEvent, MouseEventKind},
|
||||
crossterm::event::{KeyCode, KeyEvent, MouseButton, MouseEvent, MouseEventKind},
|
||||
layout::Alignment,
|
||||
prelude::*,
|
||||
widgets::{Block, Borders, Clear, List},
|
||||
|
@ -87,16 +87,25 @@ impl FloatContent for ConfirmPrompt {
|
|||
|
||||
fn handle_mouse_event(&mut self, event: &MouseEvent) -> bool {
|
||||
match event.kind {
|
||||
MouseEventKind::Down(MouseButton::Left) => {
|
||||
self.status = ConfirmStatus::Confirm;
|
||||
true
|
||||
}
|
||||
MouseEventKind::Down(MouseButton::Right) => {
|
||||
self.status = ConfirmStatus::Abort;
|
||||
false
|
||||
}
|
||||
MouseEventKind::ScrollDown => {
|
||||
self.scroll_down();
|
||||
false
|
||||
}
|
||||
MouseEventKind::ScrollUp => {
|
||||
self.scroll_up();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
||||
use KeyCode::*;
|
||||
|
|
|
@ -4,7 +4,7 @@ use ratatui::{
|
|||
Frame,
|
||||
};
|
||||
|
||||
use crate::hint::Shortcut;
|
||||
use crate::{event::MouseButton, event::MouseEventKind, hint::Shortcut};
|
||||
|
||||
pub trait FloatContent {
|
||||
fn draw(&mut self, frame: &mut Frame, area: Rect);
|
||||
|
@ -54,8 +54,14 @@ impl<Content: FloatContent + ?Sized> Float<Content> {
|
|||
self.content.draw(frame, popup_area);
|
||||
}
|
||||
|
||||
pub fn handle_mouse_event(&mut self, event: &MouseEvent) {
|
||||
pub fn handle_mouse_event(&mut self, event: &MouseEvent) -> bool {
|
||||
match event.kind {
|
||||
MouseEventKind::Down(MouseButton::Right) => {
|
||||
self.content.handle_mouse_event(event);
|
||||
true
|
||||
}
|
||||
_ => self.content.handle_mouse_event(event),
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the floating window is finished.
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{float::FloatContent, hint::Shortcut};
|
|||
use linutil_core::Command;
|
||||
|
||||
use ratatui::{
|
||||
crossterm::event::{KeyCode, KeyEvent, MouseEvent, MouseEventKind},
|
||||
crossterm::event::{KeyCode, KeyEvent, MouseButton, MouseEvent, MouseEventKind},
|
||||
layout::Rect,
|
||||
style::{Style, Stylize},
|
||||
text::Line,
|
||||
|
@ -33,6 +33,8 @@ pub struct FloatingText {
|
|||
mode_title: String,
|
||||
wrap_words: bool,
|
||||
frame_height: usize,
|
||||
drag_start_y: Option<u16>,
|
||||
drag_start_scroll: Option<usize>,
|
||||
}
|
||||
|
||||
macro_rules! style {
|
||||
|
@ -141,6 +143,8 @@ impl FloatingText {
|
|||
h_scroll: 0,
|
||||
wrap_words,
|
||||
frame_height: 0,
|
||||
drag_start_y: None,
|
||||
drag_start_scroll: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,6 +169,8 @@ impl FloatingText {
|
|||
v_scroll: 0,
|
||||
wrap_words: false,
|
||||
frame_height: 0,
|
||||
drag_start_y: None,
|
||||
drag_start_scroll: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -206,6 +212,19 @@ impl FloatingText {
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_drag(&mut self, current_y: u16) {
|
||||
if let (Some(start_y), Some(start_scroll)) = (self.drag_start_y, self.drag_start_scroll) {
|
||||
let delta = start_y as i32 - current_y as i32;
|
||||
let new_scroll = start_scroll as i32 + delta;
|
||||
|
||||
let max_scroll = self
|
||||
.wrapped_lines
|
||||
.len()
|
||||
.saturating_sub(self.frame_height.saturating_sub(2));
|
||||
self.v_scroll = new_scroll.clamp(0, max_scroll as i32) as usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FloatContent for FloatingText {
|
||||
|
@ -285,6 +304,17 @@ impl FloatContent for FloatingText {
|
|||
|
||||
fn handle_mouse_event(&mut self, event: &MouseEvent) -> bool {
|
||||
match event.kind {
|
||||
MouseEventKind::Down(MouseButton::Left) => {
|
||||
self.drag_start_y = Some(event.row);
|
||||
self.drag_start_scroll = Some(self.v_scroll);
|
||||
}
|
||||
MouseEventKind::Up(MouseButton::Left) => {
|
||||
self.drag_start_y = None;
|
||||
self.drag_start_scroll = None;
|
||||
}
|
||||
MouseEventKind::Drag(MouseButton::Left) => {
|
||||
self.handle_drag(event.row);
|
||||
}
|
||||
MouseEventKind::ScrollDown => self.scroll_down(),
|
||||
MouseEventKind::ScrollUp => self.scroll_up(),
|
||||
MouseEventKind::ScrollLeft => self.scroll_left(),
|
||||
|
|
|
@ -113,7 +113,7 @@ impl FloatContent for RunningCommand {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
true
|
||||
false
|
||||
}
|
||||
/// Handle key events of the running command "window". Returns true when the "window" should be
|
||||
/// closed
|
||||
|
|
141
tui/src/state.rs
141
tui/src/state.rs
|
@ -13,7 +13,9 @@ use linutil_core::{ego_tree::NodeId, Config, ListNode, TabList};
|
|||
#[cfg(feature = "tips")]
|
||||
use rand::Rng;
|
||||
use ratatui::{
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseEvent, MouseEventKind},
|
||||
crossterm::event::{
|
||||
KeyCode, KeyEvent, KeyEventKind, KeyModifiers, MouseButton, MouseEvent, MouseEventKind,
|
||||
},
|
||||
layout::{Alignment, Constraint, Direction, Flex, Layout, Position, Rect},
|
||||
style::{Style, Stylize},
|
||||
text::{Line, Span, Text},
|
||||
|
@ -481,7 +483,7 @@ impl AppState {
|
|||
|
||||
match &mut self.focus {
|
||||
Focus::FloatingWindow(float) => float.draw(frame, chunks[1]),
|
||||
Focus::ConfirmationPrompt(prompt) => prompt.draw(frame, chunks[1]),
|
||||
Focus::ConfirmationPrompt(confirm) => confirm.draw(frame, chunks[1]),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -493,51 +495,138 @@ impl AppState {
|
|||
return true;
|
||||
}
|
||||
|
||||
if matches!(self.focus, Focus::TabList | Focus::List) {
|
||||
match &mut self.focus {
|
||||
Focus::FloatingWindow(float) => {
|
||||
if float.handle_mouse_event(event) {
|
||||
self.focus = Focus::List;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Focus::ConfirmationPrompt(confirm) => {
|
||||
if confirm.handle_mouse_event(event) {
|
||||
match confirm.content.status {
|
||||
ConfirmStatus::Abort => {
|
||||
self.focus = Focus::List;
|
||||
if !self.multi_select {
|
||||
self.selected_commands.clear()
|
||||
} else if let Some(node) = self.get_selected_node() {
|
||||
if !node.multi_select {
|
||||
self.selected_commands.retain(|cmd| cmd.name != node.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
ConfirmStatus::Confirm => self.handle_confirm_command(),
|
||||
ConfirmStatus::None => {}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if matches!(self.focus, Focus::TabList | Focus::List | Focus::Search) {
|
||||
let position = Position::new(event.column, event.row);
|
||||
let mouse_in_tab_list = self.areas.as_ref().unwrap().tab_list.contains(position);
|
||||
let mouse_in_list = self.areas.as_ref().unwrap().list.contains(position);
|
||||
|
||||
let mouse_in_search = if let Some(areas) = &self.areas {
|
||||
position.y >= areas.list.y
|
||||
&& position.y < areas.list.y + 3
|
||||
&& position.x >= areas.list.x
|
||||
&& position.x < areas.list.x + areas.list.width
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
match event.kind {
|
||||
MouseEventKind::Moved => {
|
||||
if mouse_in_list {
|
||||
self.focus = Focus::List
|
||||
if mouse_in_search {
|
||||
if !matches!(self.focus, Focus::Search) {
|
||||
self.focus = Focus::Search;
|
||||
self.filter.activate_search();
|
||||
}
|
||||
} else if mouse_in_list {
|
||||
if matches!(self.focus, Focus::Search) {
|
||||
self.exit_search();
|
||||
}
|
||||
self.focus = Focus::List;
|
||||
if let Some(areas) = &self.areas {
|
||||
let list_start = areas.list.y + 4;
|
||||
let relative_y = position.y.saturating_sub(list_start);
|
||||
let list_len = self.filter.item_list().len();
|
||||
let adjusted_len = if self.at_root() {
|
||||
list_len
|
||||
} else {
|
||||
list_len + 1
|
||||
};
|
||||
if relative_y < adjusted_len as u16 {
|
||||
self.selection.select(Some(relative_y as usize));
|
||||
}
|
||||
}
|
||||
} else if mouse_in_tab_list {
|
||||
self.focus = Focus::TabList
|
||||
if matches!(self.focus, Focus::Search) {
|
||||
self.exit_search();
|
||||
}
|
||||
self.focus = Focus::TabList;
|
||||
if let Some(areas) = &self.areas {
|
||||
let relative_y = position.y.saturating_sub(areas.tab_list.y + 1);
|
||||
if relative_y < self.tabs.len() as u16 {
|
||||
self.current_tab.select(Some(relative_y as usize));
|
||||
self.refresh_tab();
|
||||
}
|
||||
}
|
||||
MouseEventKind::ScrollDown => {
|
||||
}
|
||||
}
|
||||
MouseEventKind::Down(button) => match button {
|
||||
MouseButton::Left => {
|
||||
if mouse_in_search {
|
||||
self.enter_search();
|
||||
} else if mouse_in_list {
|
||||
if matches!(self.focus, Focus::Search) {
|
||||
self.exit_search();
|
||||
}
|
||||
self.handle_enter();
|
||||
} else if mouse_in_tab_list {
|
||||
if matches!(self.focus, Focus::Search) {
|
||||
self.exit_search();
|
||||
}
|
||||
self.focus = Focus::TabList;
|
||||
}
|
||||
}
|
||||
MouseButton::Right if mouse_in_list => {
|
||||
if matches!(self.focus, Focus::Search) {
|
||||
self.exit_search();
|
||||
}
|
||||
self.enable_preview();
|
||||
}
|
||||
MouseButton::Middle if mouse_in_list => {
|
||||
if matches!(self.focus, Focus::Search) {
|
||||
self.exit_search();
|
||||
}
|
||||
self.enable_description();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
MouseEventKind::ScrollDown | MouseEventKind::ScrollUp => {
|
||||
if matches!(self.focus, Focus::Search) {
|
||||
self.exit_search();
|
||||
}
|
||||
if mouse_in_tab_list {
|
||||
if self.current_tab.selected().unwrap() != self.tabs.len() - 1 {
|
||||
self.current_tab.select_next();
|
||||
}
|
||||
self.refresh_tab();
|
||||
} else if mouse_in_list {
|
||||
self.selection.select_next()
|
||||
if event.kind == MouseEventKind::ScrollDown {
|
||||
self.scroll_down();
|
||||
} else {
|
||||
self.scroll_up();
|
||||
}
|
||||
}
|
||||
MouseEventKind::ScrollUp => {
|
||||
if mouse_in_tab_list {
|
||||
if self.current_tab.selected().unwrap() != 0 {
|
||||
self.current_tab.select_previous();
|
||||
}
|
||||
self.refresh_tab();
|
||||
} else if mouse_in_list {
|
||||
self.selection.select_previous()
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
match &mut self.focus {
|
||||
Focus::FloatingWindow(float) => {
|
||||
float.content.handle_mouse_event(event);
|
||||
}
|
||||
Focus::ConfirmationPrompt(confirm) => {
|
||||
confirm.content.handle_mouse_event(event);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user