fix(input): drag threshold
This commit is contained in:
parent
4d4f754318
commit
dc4e0edd73
1 changed files with 102 additions and 74 deletions
|
|
@ -211,6 +211,7 @@ pub struct TextInput<'a, Message> {
|
|||
always_active: bool,
|
||||
/// The text input tracks and manages the input value in its state.
|
||||
manage_value: bool,
|
||||
drag_threshold: f32,
|
||||
}
|
||||
|
||||
impl<'a, Message> TextInput<'a, Message>
|
||||
|
|
@ -259,6 +260,7 @@ where
|
|||
helper_text: None,
|
||||
always_active: false,
|
||||
manage_value: false,
|
||||
drag_threshold: 20.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -557,6 +559,12 @@ where
|
|||
layout.children().next().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the drag threshold.
|
||||
pub fn drag_threshold(mut self, drag_threshold: f32) -> Self {
|
||||
self.drag_threshold = drag_threshold;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> Widget<Message, crate::Theme, crate::Renderer> for TextInput<'_, Message>
|
||||
|
|
@ -926,6 +934,7 @@ where
|
|||
line_height,
|
||||
layout,
|
||||
self.manage_value,
|
||||
self.drag_threshold,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1346,6 +1355,7 @@ pub fn update<'a, Message: Clone + 'static>(
|
|||
line_height: text::LineHeight,
|
||||
layout: Layout<'_>,
|
||||
manage_value: bool,
|
||||
drag_threshold: f32,
|
||||
) -> event::Status {
|
||||
let update_cache = |state, value| {
|
||||
replace_paragraph(
|
||||
|
|
@ -1424,11 +1434,6 @@ pub fn update<'a, Message: Clone + 'static>(
|
|||
) {
|
||||
#[cfg(feature = "wayland")]
|
||||
(None, click::Kind::Single, cursor::State::Selection { start, end }) => {
|
||||
// if something is already selected, we can start a drag and drop for a
|
||||
// single click that is on top of the selected text
|
||||
// is the click on selected text?
|
||||
|
||||
if on_input.is_some() || manage_value {
|
||||
let left = start.min(end);
|
||||
let right = end.max(start);
|
||||
|
||||
|
|
@ -1452,56 +1457,16 @@ pub fn update<'a, Message: Clone + 'static>(
|
|||
height: text_layout.bounds().height,
|
||||
};
|
||||
|
||||
if cursor.is_over(selection_bounds) {
|
||||
// XXX never start a dnd if the input is secure
|
||||
if is_secure {
|
||||
return event::Status::Ignored;
|
||||
if cursor.is_over(selection_bounds) && (on_input.is_some() || manage_value)
|
||||
{
|
||||
state.dragging_state = Some(DraggingState::PrepareDnd(cursor_position));
|
||||
return event::Status::Captured;
|
||||
}
|
||||
let input_text =
|
||||
state.selected_text(&value.to_string()).unwrap_or_default();
|
||||
state.dragging_state = Some(DraggingState::Dnd(
|
||||
DndAction::empty(),
|
||||
input_text.clone(),
|
||||
));
|
||||
let mut editor = Editor::new(unsecured_value, &mut state.cursor);
|
||||
editor.delete();
|
||||
|
||||
let contents = editor.contents();
|
||||
let unsecured_value = Value::new(&contents);
|
||||
state.tracked_value = unsecured_value.clone();
|
||||
if let Some(on_input) = on_input {
|
||||
let message = (on_input)(contents);
|
||||
shell.publish(message);
|
||||
}
|
||||
if let Some(on_start_dnd) = on_start_dnd_source {
|
||||
shell.publish(on_start_dnd(state.clone()));
|
||||
}
|
||||
let state_clone = state.clone();
|
||||
|
||||
iced_core::clipboard::start_dnd(
|
||||
clipboard,
|
||||
false,
|
||||
id.map(iced_core::clipboard::DndSource::Widget),
|
||||
Some(iced_core::clipboard::IconSurface::new(
|
||||
Element::from(
|
||||
TextInput::<'static, ()>::new("", input_text.clone())
|
||||
.dnd_icon(true),
|
||||
),
|
||||
iced_core::widget::tree::State::new(state_clone),
|
||||
Vector::ZERO,
|
||||
)),
|
||||
Box::new(TextInputString(input_text)),
|
||||
DndAction::Move,
|
||||
);
|
||||
|
||||
update_cache(state, &unsecured_value);
|
||||
} else {
|
||||
// clear selection and place cursor at click position
|
||||
update_cache(state, value);
|
||||
state.setting_selection(value, text_layout.bounds(), target);
|
||||
}
|
||||
} else {
|
||||
state.setting_selection(value, text_layout.bounds(), target);
|
||||
}
|
||||
state.dragging_state = None;
|
||||
return event::Status::Captured;
|
||||
}
|
||||
(None, click::Kind::Single, _) => {
|
||||
state.setting_selection(value, text_layout.bounds(), target);
|
||||
|
|
@ -1575,6 +1540,15 @@ pub fn update<'a, Message: Clone + 'static>(
|
|||
| Event::Touch(touch::Event::FingerLifted { .. } | touch::Event::FingerLost { .. }) => {
|
||||
cold();
|
||||
let state = state();
|
||||
#[cfg(feature = "wayland")]
|
||||
if matches!(state.dragging_state, Some(DraggingState::PrepareDnd(_))) {
|
||||
// clear selection and place cursor at click position
|
||||
update_cache(state, value);
|
||||
if let Some(position) = cursor.position_over(layout.bounds()) {
|
||||
let target = position.x - text_layout.bounds().x;
|
||||
state.setting_selection(value, text_layout.bounds(), target);
|
||||
}
|
||||
}
|
||||
state.dragging_state = None;
|
||||
|
||||
return if cursor.is_over(layout.bounds()) {
|
||||
|
|
@ -1598,6 +1572,58 @@ pub fn update<'a, Message: Clone + 'static>(
|
|||
.cursor
|
||||
.select_range(state.cursor.start(value), position);
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
#[cfg(feature = "wayland")]
|
||||
if let Some(DraggingState::PrepareDnd(start_position)) = state.dragging_state {
|
||||
let distance = ((position.x - start_position.x).powi(2)
|
||||
+ (position.y - start_position.y).powi(2))
|
||||
.sqrt();
|
||||
|
||||
if distance >= drag_threshold {
|
||||
if is_secure {
|
||||
return event::Status::Ignored;
|
||||
}
|
||||
|
||||
let input_text = state.selected_text(&value.to_string()).unwrap_or_default();
|
||||
state.dragging_state =
|
||||
Some(DraggingState::Dnd(DndAction::empty(), input_text.clone()));
|
||||
let mut editor = Editor::new(unsecured_value, &mut state.cursor);
|
||||
editor.delete();
|
||||
|
||||
let contents = editor.contents();
|
||||
let unsecured_value = Value::new(&contents);
|
||||
state.tracked_value = unsecured_value.clone();
|
||||
if let Some(on_input) = on_input {
|
||||
let message = (on_input)(contents);
|
||||
shell.publish(message);
|
||||
}
|
||||
if let Some(on_start_dnd) = on_start_dnd_source {
|
||||
shell.publish(on_start_dnd(state.clone()));
|
||||
}
|
||||
let state_clone = state.clone();
|
||||
|
||||
iced_core::clipboard::start_dnd(
|
||||
clipboard,
|
||||
false,
|
||||
id.map(iced_core::clipboard::DndSource::Widget),
|
||||
Some(iced_core::clipboard::IconSurface::new(
|
||||
Element::from(
|
||||
TextInput::<'static, ()>::new("", input_text.clone())
|
||||
.dnd_icon(true),
|
||||
),
|
||||
iced_core::widget::tree::State::new(state_clone),
|
||||
Vector::ZERO,
|
||||
)),
|
||||
Box::new(TextInputString(input_text)),
|
||||
DndAction::Move,
|
||||
);
|
||||
|
||||
update_cache(state, &unsecured_value);
|
||||
} else {
|
||||
state.dragging_state = Some(DraggingState::PrepareDnd(start_position));
|
||||
}
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
}
|
||||
|
|
@ -2519,10 +2545,12 @@ impl AsMimeTypes for TextInputString {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) enum DraggingState {
|
||||
Selection,
|
||||
#[cfg(feature = "wayland")]
|
||||
PrepareDnd(Point),
|
||||
#[cfg(feature = "wayland")]
|
||||
Dnd(DndAction, String),
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue