fix: capture mouse motion and mouse interactions in overlay
This commit is contained in:
parent
1810bedfa5
commit
1970499459
4 changed files with 417 additions and 407 deletions
|
|
@ -20,7 +20,7 @@ use iced::clipboard::mime::AllowedMimeTypes;
|
|||
use iced::touch::Finger;
|
||||
use iced::{
|
||||
Alignment, Background, Color, Event, Length, Padding, Rectangle, Size, Task, Vector, alignment,
|
||||
event, keyboard, mouse, touch, window,
|
||||
keyboard, mouse, touch, window,
|
||||
};
|
||||
use iced_core::mouse::ScrollDelta;
|
||||
use iced_core::text::{self, Ellipsize, LineHeight, Renderer as TextRenderer, Shaping, Wrapping};
|
||||
|
|
@ -36,7 +36,6 @@ use std::collections::HashSet;
|
|||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
thread_local! {
|
||||
|
|
@ -609,27 +608,26 @@ where
|
|||
.text
|
||||
.get(button)
|
||||
.zip(state.paragraphs.entry(button))
|
||||
&& !text.is_empty()
|
||||
{
|
||||
if !text.is_empty() {
|
||||
icon_spacing = f32::from(self.button_spacing);
|
||||
let paragraph = entry.or_insert_with(|| {
|
||||
crate::Plain::new(Text {
|
||||
content: text.to_string(), // TODO should we just use String at this point?
|
||||
size: iced::Pixels(self.font_size),
|
||||
bounds: Size::INFINITE,
|
||||
font,
|
||||
align_x: text::Alignment::Left,
|
||||
align_y: alignment::Vertical::Center,
|
||||
shaping: Shaping::Advanced,
|
||||
wrapping: Wrapping::default(),
|
||||
ellipsize: Ellipsize::default(),
|
||||
line_height: self.line_height,
|
||||
})
|
||||
});
|
||||
icon_spacing = f32::from(self.button_spacing);
|
||||
let paragraph = entry.or_insert_with(|| {
|
||||
crate::Plain::new(Text {
|
||||
content: text.to_string(), // TODO should we just use String at this point?
|
||||
size: iced::Pixels(self.font_size),
|
||||
bounds: Size::INFINITE,
|
||||
font,
|
||||
align_x: text::Alignment::Left,
|
||||
align_y: alignment::Vertical::Center,
|
||||
shaping: Shaping::Advanced,
|
||||
wrapping: Wrapping::default(),
|
||||
ellipsize: Ellipsize::default(),
|
||||
line_height: self.line_height,
|
||||
})
|
||||
});
|
||||
|
||||
let size = paragraph.min_bounds();
|
||||
width += size.width;
|
||||
}
|
||||
let size = paragraph.min_bounds();
|
||||
width += size.width;
|
||||
}
|
||||
|
||||
// Add indent to measurement if found.
|
||||
|
|
@ -895,10 +893,10 @@ where
|
|||
}
|
||||
|
||||
// Unfocus if another segmented control was focused.
|
||||
if let Some(f) = state.focused.as_ref() {
|
||||
if f.updated_at != LAST_FOCUS_UPDATE.with(|f| f.get()) {
|
||||
state.unfocus();
|
||||
}
|
||||
if let Some(f) = state.focused.as_ref()
|
||||
&& f.updated_at != LAST_FOCUS_UPDATE.with(|f| f.get())
|
||||
{
|
||||
state.unfocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1162,6 +1160,9 @@ where
|
|||
None::<fn(_, _) -> Message>,
|
||||
on_drop,
|
||||
);
|
||||
if matches!(ret, iced::event::Status::Captured) {
|
||||
shell.capture_event();
|
||||
}
|
||||
if let Some(msg) = maybe_msg {
|
||||
log::trace!(
|
||||
target: TAB_REORDER_LOG_TARGET,
|
||||
|
|
@ -1200,9 +1201,8 @@ where
|
|||
}
|
||||
|
||||
Event::Touch(touch::Event::FingerLifted { id, .. }) => {
|
||||
state.fingers_pressed.remove(&id);
|
||||
state.fingers_pressed.remove(id);
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
|
@ -1301,27 +1301,26 @@ where
|
|||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
||||
)
|
||||
&& !over_close_button
|
||||
&& let Some(position) = cursor_position.position()
|
||||
{
|
||||
if let Some(position) = cursor_position.position() {
|
||||
state.tab_drag_candidate = Some(TabDragCandidate {
|
||||
entity: key,
|
||||
bounds,
|
||||
origin: position,
|
||||
});
|
||||
if let Some(tab_drag) = self.tab_drag.as_ref() {
|
||||
log::trace!(
|
||||
target: TAB_REORDER_LOG_TARGET,
|
||||
"tab drag candidate entity={:?} origin=({:.2},{:.2}) bounds=({:.2},{:.2},{:.2},{:.2}) threshold={}",
|
||||
key,
|
||||
position.x,
|
||||
position.y,
|
||||
bounds.x,
|
||||
bounds.y,
|
||||
bounds.width,
|
||||
bounds.height,
|
||||
tab_drag.threshold
|
||||
);
|
||||
}
|
||||
state.tab_drag_candidate = Some(TabDragCandidate {
|
||||
entity: key,
|
||||
bounds,
|
||||
origin: position,
|
||||
});
|
||||
if let Some(tab_drag) = self.tab_drag.as_ref() {
|
||||
log::trace!(
|
||||
target: TAB_REORDER_LOG_TARGET,
|
||||
"tab drag candidate entity={:?} origin=({:.2},{:.2}) bounds=({:.2},{:.2},{:.2},{:.2}) threshold={}",
|
||||
key,
|
||||
position.x,
|
||||
position.y,
|
||||
bounds.x,
|
||||
bounds.y,
|
||||
bounds.width,
|
||||
bounds.height,
|
||||
tab_drag.threshold
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1330,40 +1329,35 @@ where
|
|||
}
|
||||
|
||||
if let Some(on_activate) = self.on_activate.as_ref() {
|
||||
if is_pressed(&event) {
|
||||
if is_pressed(event) {
|
||||
state.pressed_item = Some(Item::Tab(key));
|
||||
} else if is_lifted(&event) {
|
||||
if self.button_is_pressed(state, key) {
|
||||
shell.publish(on_activate(key));
|
||||
state.set_focused();
|
||||
state.focused_item = Item::Tab(key);
|
||||
state.pressed_item = None;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
} else if is_lifted(&event) && self.button_is_pressed(state, key) {
|
||||
shell.publish(on_activate(key));
|
||||
state.set_focused();
|
||||
state.focused_item = Item::Tab(key);
|
||||
state.pressed_item = None;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Present a context menu on a right click event.
|
||||
if self.context_menu.is_some() {
|
||||
if let Some(on_context) = self.on_context.as_ref() {
|
||||
if right_button_released(&event)
|
||||
|| (touch_lifted(&event) && fingers_pressed == 2)
|
||||
{
|
||||
state.show_context = Some(key);
|
||||
state.context_cursor =
|
||||
cursor_position.position().unwrap_or_default();
|
||||
if self.context_menu.is_some()
|
||||
&& let Some(on_context) = self.on_context.as_ref()
|
||||
&& (right_button_released(&event)
|
||||
|| (touch_lifted(&event) && fingers_pressed == 2))
|
||||
{
|
||||
state.show_context = Some(key);
|
||||
state.context_cursor = cursor_position.position().unwrap_or_default();
|
||||
|
||||
state.menu_state.inner.with_data_mut(|data| {
|
||||
data.open = true;
|
||||
data.view_cursor = cursor_position;
|
||||
});
|
||||
state.menu_state.inner.with_data_mut(|data| {
|
||||
data.open = true;
|
||||
data.view_cursor = cursor_position;
|
||||
});
|
||||
|
||||
shell.publish(on_context(key));
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
shell.publish(on_context(key));
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
|
||||
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle)) =
|
||||
|
|
@ -1385,57 +1379,56 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
if self.scrollable_focus {
|
||||
if let Some(on_activate) = self.on_activate.as_ref() {
|
||||
if let Event::Mouse(mouse::Event::WheelScrolled { delta }) = event {
|
||||
let current = Instant::now();
|
||||
if self.scrollable_focus
|
||||
&& let Some(on_activate) = self.on_activate.as_ref()
|
||||
&& let Event::Mouse(mouse::Event::WheelScrolled { delta }) = event
|
||||
{
|
||||
let current = Instant::now();
|
||||
|
||||
// Permit successive scroll wheel events only after a given delay.
|
||||
if state.wheel_timestamp.is_none_or(|previous| {
|
||||
current.duration_since(previous) > Duration::from_millis(250)
|
||||
}) {
|
||||
state.wheel_timestamp = Some(current);
|
||||
// Permit successive scroll wheel events only after a given delay.
|
||||
if state.wheel_timestamp.is_none_or(|previous| {
|
||||
current.duration_since(previous) > Duration::from_millis(250)
|
||||
}) {
|
||||
state.wheel_timestamp = Some(current);
|
||||
|
||||
match delta {
|
||||
ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => {
|
||||
let mut activate_key = None;
|
||||
match delta {
|
||||
ScrollDelta::Lines { y, .. } | ScrollDelta::Pixels { y, .. } => {
|
||||
let mut activate_key = None;
|
||||
|
||||
if *y < 0.0 {
|
||||
let mut prev_key = Entity::null();
|
||||
if *y < 0.0 {
|
||||
let mut prev_key = Entity::null();
|
||||
|
||||
for key in self.model.order.iter().copied() {
|
||||
if self.model.is_active(key) && !prev_key.is_null() {
|
||||
activate_key = Some(prev_key);
|
||||
}
|
||||
for key in self.model.order.iter().copied() {
|
||||
if self.model.is_active(key) && !prev_key.is_null() {
|
||||
activate_key = Some(prev_key);
|
||||
}
|
||||
|
||||
if self.model.is_enabled(key) {
|
||||
prev_key = key;
|
||||
}
|
||||
}
|
||||
} else if *y > 0.0 {
|
||||
let mut buttons = self.model.order.iter().copied();
|
||||
while let Some(key) = buttons.next() {
|
||||
if self.model.is_active(key) {
|
||||
for key in buttons {
|
||||
if self.model.is_enabled(key) {
|
||||
prev_key = key;
|
||||
}
|
||||
}
|
||||
} else if *y > 0.0 {
|
||||
let mut buttons = self.model.order.iter().copied();
|
||||
while let Some(key) = buttons.next() {
|
||||
if self.model.is_active(key) {
|
||||
for key in buttons {
|
||||
if self.model.is_enabled(key) {
|
||||
activate_key = Some(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
activate_key = Some(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(key) = activate_key {
|
||||
shell.publish(on_activate(key));
|
||||
state.set_focused();
|
||||
state.focused_item = Item::Tab(key);
|
||||
shell.capture_event();
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(key) = activate_key {
|
||||
shell.publish(on_activate(key));
|
||||
state.set_focused();
|
||||
state.focused_item = Item::Tab(key);
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1460,31 +1453,27 @@ where
|
|||
|
||||
if let (Some(tab_drag), Some(candidate)) =
|
||||
(self.tab_drag.as_ref(), state.tab_drag_candidate)
|
||||
&& let Event::Mouse(mouse::Event::CursorMoved { .. }) = event
|
||||
&& let Some(position) = cursor_position.position()
|
||||
&& position.distance(candidate.origin) >= tab_drag.threshold
|
||||
&& let Some(candidate) = state.tab_drag_candidate.take()
|
||||
{
|
||||
if let Event::Mouse(mouse::Event::CursorMoved { .. }) = event {
|
||||
if let Some(position) = cursor_position.position() {
|
||||
if position.distance(candidate.origin) >= tab_drag.threshold {
|
||||
if let Some(candidate) = state.tab_drag_candidate.take() {
|
||||
log::trace!(
|
||||
target: TAB_REORDER_LOG_TARGET,
|
||||
"tab drag threshold met entity={:?} distance={:.2} threshold={}",
|
||||
candidate.entity,
|
||||
position.distance(candidate.origin),
|
||||
tab_drag.threshold
|
||||
);
|
||||
if self.start_tab_drag(
|
||||
state,
|
||||
candidate.entity,
|
||||
candidate.bounds,
|
||||
position,
|
||||
clipboard,
|
||||
) {
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log::trace!(
|
||||
target: TAB_REORDER_LOG_TARGET,
|
||||
"tab drag threshold met entity={:?} distance={:.2} threshold={}",
|
||||
candidate.entity,
|
||||
position.distance(candidate.origin),
|
||||
tab_drag.threshold
|
||||
);
|
||||
if self.start_tab_drag(
|
||||
state,
|
||||
candidate.entity,
|
||||
candidate.bounds,
|
||||
position,
|
||||
clipboard,
|
||||
) {
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1504,55 +1493,53 @@ where
|
|||
{
|
||||
state.focused_visible = true;
|
||||
return if *modifiers == keyboard::Modifiers::SHIFT {
|
||||
self.focus_previous(state, shell)
|
||||
self.focus_previous(state, shell);
|
||||
} else if modifiers.is_empty() {
|
||||
self.focus_next(state, shell)
|
||||
self.focus_next(state, shell);
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(on_activate) = self.on_activate.as_ref() {
|
||||
if let Event::Keyboard(keyboard::Event::KeyReleased {
|
||||
if let Some(on_activate) = self.on_activate.as_ref()
|
||||
&& let Event::Keyboard(keyboard::Event::KeyReleased {
|
||||
key: keyboard::Key::Named(keyboard::key::Named::Enter),
|
||||
..
|
||||
}) = event
|
||||
{
|
||||
match state.focused_item {
|
||||
Item::Tab(entity) => {
|
||||
shell.publish(on_activate(entity));
|
||||
}
|
||||
|
||||
Item::PrevButton => {
|
||||
if self.prev_tab_sensitive(state) {
|
||||
state.buttons_offset -= 1;
|
||||
|
||||
// If the change would cause it to be insensitive, focus the first tab.
|
||||
if !self.prev_tab_sensitive(state) {
|
||||
if let Some(first) = self.first_tab(state) {
|
||||
state.focused_item = Item::Tab(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item::NextButton => {
|
||||
if self.next_tab_sensitive(state) {
|
||||
state.buttons_offset += 1;
|
||||
|
||||
// If the change would cause it to be insensitive, focus the last tab.
|
||||
if !self.next_tab_sensitive(state) {
|
||||
if let Some(last) = self.last_tab(state) {
|
||||
state.focused_item = Item::Tab(last);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item::None | Item::Set => (),
|
||||
{
|
||||
match state.focused_item {
|
||||
Item::Tab(entity) => {
|
||||
shell.publish(on_activate(entity));
|
||||
}
|
||||
|
||||
shell.capture_event();
|
||||
return;
|
||||
Item::PrevButton => {
|
||||
if self.prev_tab_sensitive(state) {
|
||||
state.buttons_offset -= 1;
|
||||
|
||||
// If the change would cause it to be insensitive, focus the first tab.
|
||||
if !self.prev_tab_sensitive(state)
|
||||
&& let Some(first) = self.first_tab(state)
|
||||
{
|
||||
state.focused_item = Item::Tab(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item::NextButton => {
|
||||
if self.next_tab_sensitive(state) {
|
||||
state.buttons_offset += 1;
|
||||
|
||||
// If the change would cause it to be insensitive, focus the last tab.
|
||||
if !self.next_tab_sensitive(state)
|
||||
&& let Some(last) = self.last_tab(state)
|
||||
{
|
||||
state.focused_item = Item::Tab(last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item::None | Item::Set => (),
|
||||
}
|
||||
|
||||
shell.capture_event();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1794,22 +1781,22 @@ where
|
|||
let original_bounds = bounds;
|
||||
let center_y = bounds.center_y();
|
||||
|
||||
if show_drop_hint_marker {
|
||||
if matches!(
|
||||
if show_drop_hint_marker
|
||||
&& matches!(
|
||||
drop_hint_marker,
|
||||
Some(DropHint {
|
||||
entity,
|
||||
side: DropSide::Before
|
||||
}) if entity == key
|
||||
) {
|
||||
draw_drop_indicator(
|
||||
renderer,
|
||||
original_bounds,
|
||||
DropSide::Before,
|
||||
Self::VERTICAL,
|
||||
appearance.active.text_color,
|
||||
);
|
||||
}
|
||||
)
|
||||
{
|
||||
draw_drop_indicator(
|
||||
renderer,
|
||||
original_bounds,
|
||||
DropSide::Before,
|
||||
Self::VERTICAL,
|
||||
appearance.active.text_color,
|
||||
);
|
||||
}
|
||||
|
||||
let menu_open = || {
|
||||
|
|
@ -1882,41 +1869,41 @@ where
|
|||
let mut indent_padding = 0.0;
|
||||
|
||||
// Adjust bounds by indent
|
||||
if let Some(indent) = self.model.indent(key) {
|
||||
if indent > 0 {
|
||||
let adjustment = f32::from(indent) * f32::from(self.indent_spacing);
|
||||
bounds.x += adjustment;
|
||||
bounds.width -= adjustment;
|
||||
if let Some(indent) = self.model.indent(key)
|
||||
&& indent > 0
|
||||
{
|
||||
let adjustment = f32::from(indent) * f32::from(self.indent_spacing);
|
||||
bounds.x += adjustment;
|
||||
bounds.width -= adjustment;
|
||||
|
||||
// Draw indent line
|
||||
if let crate::theme::SegmentedButton::FileNav = self.style {
|
||||
if indent > 1 {
|
||||
indent_padding = 7.0;
|
||||
// Draw indent line
|
||||
if let crate::theme::SegmentedButton::FileNav = self.style
|
||||
&& indent > 1
|
||||
{
|
||||
indent_padding = 7.0;
|
||||
|
||||
for level in 1..indent {
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds: Rectangle {
|
||||
x: (level as f32)
|
||||
.mul_add(-(self.indent_spacing as f32), bounds.x)
|
||||
+ indent_padding,
|
||||
width: 1.0,
|
||||
..bounds
|
||||
},
|
||||
border: Border {
|
||||
radius: rad_0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
shadow: Shadow::default(),
|
||||
snap: true,
|
||||
},
|
||||
divider_background,
|
||||
);
|
||||
}
|
||||
|
||||
indent_padding += 4.0;
|
||||
}
|
||||
for level in 1..indent {
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds: Rectangle {
|
||||
x: (level as f32)
|
||||
.mul_add(-(self.indent_spacing as f32), bounds.x)
|
||||
+ indent_padding,
|
||||
width: 1.0,
|
||||
..bounds
|
||||
},
|
||||
border: Border {
|
||||
radius: rad_0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
shadow: Shadow::default(),
|
||||
snap: true,
|
||||
},
|
||||
divider_background,
|
||||
);
|
||||
}
|
||||
|
||||
indent_padding += 4.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1990,40 +1977,35 @@ where
|
|||
bounds.x += offset;
|
||||
} else {
|
||||
// Draw the selection indicator if widget is a segmented selection, and the item is selected.
|
||||
if key_is_active {
|
||||
if let crate::theme::SegmentedButton::Control = self.style {
|
||||
let mut image_bounds = bounds;
|
||||
image_bounds.y = center_y - 8.0;
|
||||
if key_is_active && let crate::theme::SegmentedButton::Control = self.style {
|
||||
let mut image_bounds = bounds;
|
||||
image_bounds.y = center_y - 8.0;
|
||||
|
||||
draw_icon::<Message>(
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
cursor,
|
||||
viewport,
|
||||
status_appearance.text_color,
|
||||
Rectangle {
|
||||
width: 16.0,
|
||||
height: 16.0,
|
||||
..image_bounds
|
||||
},
|
||||
crate::widget::icon(
|
||||
match crate::widget::common::object_select().data() {
|
||||
crate::iced_core::svg::Data::Bytes(bytes) => {
|
||||
crate::widget::icon::from_svg_bytes(bytes.as_ref())
|
||||
.symbolic(true)
|
||||
}
|
||||
crate::iced_core::svg::Data::Path(path) => {
|
||||
crate::widget::icon::from_path(path.clone())
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
draw_icon::<Message>(
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
cursor,
|
||||
viewport,
|
||||
status_appearance.text_color,
|
||||
Rectangle {
|
||||
width: 16.0,
|
||||
height: 16.0,
|
||||
..image_bounds
|
||||
},
|
||||
crate::widget::icon(match crate::widget::common::object_select().data() {
|
||||
crate::iced_core::svg::Data::Bytes(bytes) => {
|
||||
crate::widget::icon::from_svg_bytes(bytes.as_ref()).symbolic(true)
|
||||
}
|
||||
crate::iced_core::svg::Data::Path(path) => {
|
||||
crate::widget::icon::from_path(path.clone())
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
let offset = 16.0 + f32::from(self.button_spacing);
|
||||
let offset = 16.0 + f32::from(self.button_spacing);
|
||||
|
||||
bounds.x += offset;
|
||||
}
|
||||
bounds.x += offset;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue