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)]
pub struct Notifications {
#[serde(default)]
@ -562,6 +568,7 @@ pub struct TunableValues {
pub message_user_color: bool,
pub default_room: Option<String>,
pub open_command: Option<Vec<String>>,
pub mouse: Mouse,
pub notifications: Notifications,
pub image_preview: Option<ImagePreviewValues>,
pub user_gutter_width: usize,
@ -586,6 +593,7 @@ pub struct Tunables {
pub message_user_color: Option<bool>,
pub default_room: Option<String>,
pub open_command: Option<Vec<String>>,
pub mouse: Option<Mouse>,
pub notifications: Option<Notifications>,
pub image_preview: Option<ImagePreview>,
pub user_gutter_width: Option<usize>,
@ -614,6 +622,7 @@ impl Tunables {
message_user_color: self.message_user_color.or(other.message_user_color),
default_room: self.default_room.or(other.default_room),
open_command: self.open_command.or(other.open_command),
mouse: self.mouse.or(other.mouse),
notifications: self.notifications.or(other.notifications),
image_preview: self.image_preview.or(other.image_preview),
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),
default_room: self.default_room,
open_command: self.open_command,
mouse: self.mouse.unwrap_or_default(),
notifications: self.notifications.unwrap_or_default(),
image_preview: self.image_preview.map(ImagePreview::values),
user_gutter_width: self.user_gutter_width.unwrap_or(30),

View file

@ -44,11 +44,14 @@ use modalkit::crossterm::{
read,
DisableBracketedPaste,
DisableFocusChange,
DisableMouseCapture,
EnableBracketedPaste,
EnableFocusChange,
EnableMouseCapture,
Event,
KeyEventKind,
KeyboardEnhancementFlags,
MouseEventKind,
PopKeyboardEnhancementFlags,
PushKeyboardEnhancementFlags,
},
@ -364,8 +367,30 @@ impl Application {
return Ok(ke.into());
},
Event::Mouse(_) => {
// Do nothing for now.
Event::Mouse(me) => {
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 => {
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.
fn setup_tty(title: &str, enable_enhanced_keys: bool) -> std::io::Result<()> {
let title = format!("iamb ({})", title);
fn setup_tty(settings: &ApplicationSettings, enable_enhanced_keys: bool) -> std::io::Result<()> {
let title = format!("iamb ({})", settings.profile.user_id.as_str());
// Enable raw mode and enter the alternate screen.
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))
}
// 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 {
let _ = crossterm::queue!(stdout(), PopKeyboardEnhancementFlags);
}
if enable_mouse {
let _ = crossterm::queue!(stdout(), DisableMouseCapture);
}
let _ = crossterm::execute!(
stdout(),
DisableBracketedPaste,
@ -1009,11 +1042,12 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> {
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 enable_mouse = settings.tunables.mouse.enabled;
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);
process::exit(1);
}));
@ -1023,7 +1057,7 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> {
application.run().await?;
// Clean up the terminal on exit.
restore_tty(enable_enhanced_keys);
restore_tty(enable_enhanced_keys, enable_mouse);
Ok(())
}

View file

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