Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 29 additions & 7 deletions packages/iocraft/src/components/text_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ impl TextInputHandle {
}
}

/// The action to take for a key event after `on_key_event` is called.
pub enum KeyEventAction {
/// Perform the default handling of the key event, which may update the value and/or cursor position.
Default,
/// Perform no further handling of the key event.
None,
}

/// The props which can be passed to the [`TextInput`] component.
#[non_exhaustive]
#[derive(Default, Props)]
Expand All @@ -89,6 +97,9 @@ pub struct TextInputProps {
/// The handler to invoke when the value changes.
pub on_change: HandlerMut<'static, String>,

/// If provided, this handler will be invoked for key events before the default handling of the input. The return value indicates whether the default handling should be performed or not. This can be used to implement custom key bindings.
pub on_key_event: Option<HandlerMut<'static, KeyEvent, KeyEventAction>>,

/// If true, the input will fill 100% of the height of its container and handle multiline input.
pub multiline: bool,

Expand Down Expand Up @@ -419,20 +430,31 @@ pub fn TextInput(mut hooks: Hooks, props: &mut TextInputProps) -> impl Into<AnyE
let mut value = props.value.clone();
let mut temp_cursor_offset = cursor_offset.get();
let mut on_change = props.on_change.take();
let mut on_key_event = props.on_key_event.take();
move |event| {
if !has_focus {
return;
}

match event {
TerminalEvent::Key(KeyEvent {
let key_event = match event {
TerminalEvent::Key(k) => k,
_ => return,
};

if let Some(on_key_event) = &mut on_key_event {
match on_key_event(key_event.clone()) {
KeyEventAction::Default => {}
KeyEventAction::None => return,
}
}

match key_event {
KeyEvent {
code,
kind,
modifiers,
..
}) if kind != KeyEventKind::Release
&& modifiers.contains(KeyModifiers::CONTROL) =>
{
} if kind != KeyEventKind::Release && modifiers.contains(KeyModifiers::CONTROL) => {
match code {
KeyCode::Char('a') => {
cursor_offset.set(buffer.row_start_offset(cursor_offset.get()));
Expand All @@ -445,12 +467,12 @@ pub fn TextInput(mut hooks: Hooks, props: &mut TextInputProps) -> impl Into<AnyE
_ => {}
}
}
TerminalEvent::Key(KeyEvent {
KeyEvent {
code,
kind,
modifiers,
..
}) if kind != KeyEventKind::Release
} if kind != KeyEventKind::Release
&& !modifiers.intersects(KeyModifiers::CONTROL | KeyModifiers::ALT) =>
{
let mut clear_vertical_movement_col_preference = true;
Expand Down
44 changes: 23 additions & 21 deletions packages/iocraft/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use std::{ops::DerefMut, sync::Arc};
///
/// Any function that takes a single argument and returns `()` can be converted into a `HandlerMut`,
/// and it can be invoked using function call syntax.
pub struct HandlerMut<'a, T>(bool, Box<dyn FnMut(T) + Send + Sync + 'a>);
pub struct HandlerMut<'a, T, R = ()>(bool, Box<dyn FnMut(T) -> R + Send + Sync + 'a>);

impl<T> HandlerMut<'_, T> {
impl<T, R: Default> HandlerMut<'_, T, R> {
/// Returns `true` if the handler was default-initialized.
pub fn is_default(&self) -> bool {
!self.0
Expand All @@ -19,30 +19,30 @@ impl<T> HandlerMut<'_, T> {
}
}

impl<T> Default for HandlerMut<'_, T> {
impl<T, R: Default> Default for HandlerMut<'_, T, R> {
fn default() -> Self {
Self(false, Box::new(|_| {}))
Self(false, Box::new(|_| Default::default()))
}
}

impl<'a, T, F> From<F> for HandlerMut<'a, T>
impl<'a, T, F, R> From<F> for HandlerMut<'a, T, R>
where
F: FnMut(T) + Send + Sync + 'a,
F: FnMut(T) -> R + Send + Sync + 'a,
{
fn from(f: F) -> Self {
Self(true, Box::new(f))
}
}

impl<'a, T: 'a> Deref for HandlerMut<'a, T> {
type Target = dyn FnMut(T) + Send + Sync + 'a;
impl<'a, T: 'a, R> Deref for HandlerMut<'a, T, R> {
type Target = dyn FnMut(T) -> R + Send + Sync + 'a;

fn deref(&self) -> &Self::Target {
self.1.as_ref()
}
}

impl<'a, T: 'a> DerefMut for HandlerMut<'a, T> {
impl<'a, T: 'a, R> DerefMut for HandlerMut<'a, T, R> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.1.as_mut()
}
Expand Down Expand Up @@ -78,48 +78,50 @@ impl<'a, T: 'a> DerefMut for HandlerMut<'a, T> {
/// # }
/// ```
#[derive(Clone)]
pub struct Handler<T>(bool, Arc<dyn Fn(T) + Send + Sync + 'static>);
pub struct Handler<T, R = ()>(bool, Arc<dyn Fn(T) -> R + Send + Sync + 'static>);

impl<T> Handler<T> {
impl<T, R> Handler<T, R> {
/// Returns `true` if the handler was default-initialized.
pub fn is_default(&self) -> bool {
!self.0
}
}

impl<T> Deref for Handler<T> {
type Target = dyn Fn(T) + Send + Sync + 'static;
impl<T, R> Deref for Handler<T, R> {
type Target = dyn Fn(T) -> R + Send + Sync + 'static;

fn deref(&self) -> &Self::Target {
self.1.as_ref()
}
}

impl<T> Default for Handler<T> {
impl<T, R: Default> Default for Handler<T, R> {
fn default() -> Self {
Self(false, Arc::new(|_| {}))
Self(false, Arc::new(|_| Default::default()))
}
}

impl<T, F> From<F> for Handler<T>
impl<T, F, R> From<F> for Handler<T, R>
where
F: Fn(T) + Send + Sync + 'static,
F: Fn(T) -> R + Send + Sync + 'static,
{
fn from(f: F) -> Self {
Self(true, Arc::new(f))
}
}

impl<T: Clone + Send + Sync + 'static> Handler<T> {
impl<T: Clone + Send + Sync + 'static, R: Clone + 'static> Handler<T, R> {
/// Creates a new `Handler` that uses a constant value for it's input.
pub fn bind(&self, value: T) -> Handler<()> {
pub fn bind(&self, value: T) -> Handler<(), R> {
let handler = self.clone();
Handler::from(move |_| handler(value.clone()))
}
}

impl<T: Clone + Send + Sync + 'static> From<Handler<T>> for HandlerMut<'static, T> {
fn from(handler: Handler<T>) -> Self {
impl<T: Clone + Send + Sync + 'static, R: 'static> From<Handler<T, R>>
for HandlerMut<'static, T, R>
{
fn from(handler: Handler<T, R>) -> Self {
Self::from(move |value| handler.1(value))
}
}
Expand Down
Loading