Add support for scrolling w/ mouse when explicitly enabled (#389)

Co-authored-by: Ulyssa <git@ulyssa.dev>
This commit is contained in:
Aleš Katona 2025-05-28 21:48:10 -07:00 committed by GitHub
parent 3296f58859
commit 82ed796a91
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 53 additions and 8 deletions

View file

@ -466,6 +466,12 @@ impl<'de> Deserialize<'de> for NotifyVia {
} }
} }
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
pub struct Mouse {
#[serde(default)]
pub enabled: bool,
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
pub struct Notifications { pub struct Notifications {
#[serde(default)] #[serde(default)]
@ -562,6 +568,7 @@ pub struct TunableValues {
pub message_user_color: bool, pub message_user_color: bool,
pub default_room: Option<String>, pub default_room: Option<String>,
pub open_command: Option<Vec<String>>, pub open_command: Option<Vec<String>>,
pub mouse: Mouse,
pub notifications: Notifications, pub notifications: Notifications,
pub image_preview: Option<ImagePreviewValues>, pub image_preview: Option<ImagePreviewValues>,
pub user_gutter_width: usize, pub user_gutter_width: usize,
@ -586,6 +593,7 @@ pub struct Tunables {
pub message_user_color: Option<bool>, pub message_user_color: Option<bool>,
pub default_room: Option<String>, pub default_room: Option<String>,
pub open_command: Option<Vec<String>>, pub open_command: Option<Vec<String>>,
pub mouse: Option<Mouse>,
pub notifications: Option<Notifications>, pub notifications: Option<Notifications>,
pub image_preview: Option<ImagePreview>, pub image_preview: Option<ImagePreview>,
pub user_gutter_width: Option<usize>, pub user_gutter_width: Option<usize>,
@ -614,6 +622,7 @@ impl Tunables {
message_user_color: self.message_user_color.or(other.message_user_color), message_user_color: self.message_user_color.or(other.message_user_color),
default_room: self.default_room.or(other.default_room), default_room: self.default_room.or(other.default_room),
open_command: self.open_command.or(other.open_command), open_command: self.open_command.or(other.open_command),
mouse: self.mouse.or(other.mouse),
notifications: self.notifications.or(other.notifications), notifications: self.notifications.or(other.notifications),
image_preview: self.image_preview.or(other.image_preview), image_preview: self.image_preview.or(other.image_preview),
user_gutter_width: self.user_gutter_width.or(other.user_gutter_width), user_gutter_width: self.user_gutter_width.or(other.user_gutter_width),
@ -640,6 +649,7 @@ impl Tunables {
message_user_color: self.message_user_color.unwrap_or(false), message_user_color: self.message_user_color.unwrap_or(false),
default_room: self.default_room, default_room: self.default_room,
open_command: self.open_command, open_command: self.open_command,
mouse: self.mouse.unwrap_or_default(),
notifications: self.notifications.unwrap_or_default(), notifications: self.notifications.unwrap_or_default(),
image_preview: self.image_preview.map(ImagePreview::values), image_preview: self.image_preview.map(ImagePreview::values),
user_gutter_width: self.user_gutter_width.unwrap_or(30), user_gutter_width: self.user_gutter_width.unwrap_or(30),

View file

@ -44,11 +44,14 @@ use modalkit::crossterm::{
read, read,
DisableBracketedPaste, DisableBracketedPaste,
DisableFocusChange, DisableFocusChange,
DisableMouseCapture,
EnableBracketedPaste, EnableBracketedPaste,
EnableFocusChange, EnableFocusChange,
EnableMouseCapture,
Event, Event,
KeyEventKind, KeyEventKind,
KeyboardEnhancementFlags, KeyboardEnhancementFlags,
MouseEventKind,
PopKeyboardEnhancementFlags, PopKeyboardEnhancementFlags,
PushKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
}, },
@ -364,8 +367,30 @@ impl Application {
return Ok(ke.into()); return Ok(ke.into());
}, },
Event::Mouse(_) => { Event::Mouse(me) => {
// Do nothing for now. let dir = match me.kind {
MouseEventKind::ScrollUp => MoveDir2D::Up,
MouseEventKind::ScrollDown => MoveDir2D::Down,
MouseEventKind::ScrollLeft => MoveDir2D::Left,
MouseEventKind::ScrollRight => MoveDir2D::Right,
_ => continue,
};
let size = ScrollSize::Cell;
let style = ScrollStyle::Direction2D(dir, size, 1.into());
let ctx = ProgramContext::default();
let mut store = self.store.lock().await;
match self.screen.scroll(&style, &ctx, store.deref_mut()) {
Ok(None) => {},
Ok(Some(info)) => {
drop(store);
self.handle_info(info);
},
Err(e) => {
self.screen.push_error(e);
},
}
}, },
Event::FocusGained => { Event::FocusGained => {
let mut store = self.store.lock().await; let mut store = self.store.lock().await;
@ -932,8 +957,8 @@ async fn login_normal(
} }
/// Set up the terminal for drawing the TUI, and getting additional info. /// Set up the terminal for drawing the TUI, and getting additional info.
fn setup_tty(title: &str, enable_enhanced_keys: bool) -> std::io::Result<()> { fn setup_tty(settings: &ApplicationSettings, enable_enhanced_keys: bool) -> std::io::Result<()> {
let title = format!("iamb ({})", title); let title = format!("iamb ({})", settings.profile.user_id.as_str());
// Enable raw mode and enter the alternate screen. // Enable raw mode and enter the alternate screen.
crossterm::terminal::enable_raw_mode()?; crossterm::terminal::enable_raw_mode()?;
@ -947,15 +972,23 @@ fn setup_tty(title: &str, enable_enhanced_keys: bool) -> std::io::Result<()> {
)?; )?;
} }
if settings.tunables.mouse.enabled {
crossterm::execute!(stdout(), EnableMouseCapture)?;
}
crossterm::execute!(stdout(), EnableBracketedPaste, EnableFocusChange, SetTitle(title)) crossterm::execute!(stdout(), EnableBracketedPaste, EnableFocusChange, SetTitle(title))
} }
// Do our best to reverse what we did in setup_tty() when we exit or crash. // Do our best to reverse what we did in setup_tty() when we exit or crash.
fn restore_tty(enable_enhanced_keys: bool) { fn restore_tty(enable_enhanced_keys: bool, enable_mouse: bool) {
if enable_enhanced_keys { if enable_enhanced_keys {
let _ = crossterm::queue!(stdout(), PopKeyboardEnhancementFlags); let _ = crossterm::queue!(stdout(), PopKeyboardEnhancementFlags);
} }
if enable_mouse {
let _ = crossterm::queue!(stdout(), DisableMouseCapture);
}
let _ = crossterm::execute!( let _ = crossterm::execute!(
stdout(), stdout(),
DisableBracketedPaste, DisableBracketedPaste,
@ -1009,11 +1042,12 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> {
false false
}, },
}; };
setup_tty(settings.profile.user_id.as_str(), enable_enhanced_keys)?; setup_tty(&settings, enable_enhanced_keys)?;
let orig_hook = std::panic::take_hook(); let orig_hook = std::panic::take_hook();
let enable_mouse = settings.tunables.mouse.enabled;
std::panic::set_hook(Box::new(move |panic_info| { std::panic::set_hook(Box::new(move |panic_info| {
restore_tty(enable_enhanced_keys); restore_tty(enable_enhanced_keys, enable_mouse);
orig_hook(panic_info); orig_hook(panic_info);
process::exit(1); process::exit(1);
})); }));
@ -1023,7 +1057,7 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> {
application.run().await?; application.run().await?;
// Clean up the terminal on exit. // Clean up the terminal on exit.
restore_tty(enable_enhanced_keys); restore_tty(enable_enhanced_keys, enable_mouse);
Ok(()) Ok(())
} }

View file

@ -189,6 +189,7 @@ pub fn mock_tunables() -> TunableValues {
external_edit_file_suffix: String::from(".md"), external_edit_file_suffix: String::from(".md"),
username_display: UserDisplayStyle::Username, username_display: UserDisplayStyle::Username,
message_user_color: false, message_user_color: false,
mouse: Default::default(),
notifications: Notifications { notifications: Notifications {
enabled: false, enabled: false,
via: NotifyVia::default(), via: NotifyVia::default(),