Skip to content

Commit

Permalink
chore: Update text to reflect requested changes
Browse files Browse the repository at this point in the history
  • Loading branch information
mmstick committed Dec 12, 2022
1 parent 3650f7c commit ca157d8
Showing 1 changed file with 32 additions and 78 deletions.
110 changes: 32 additions & 78 deletions text/0012-event-container.md → text/0012-mouse-listener.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Event Container
# Mouse Listener

## Summary

A programmable container that intercepts a variety of input events.
A programmable container that intercepts a variety of mouse input events.

## Motivation

Expand All @@ -16,14 +16,14 @@ One such use case for the event container is a file icon in a file manager. The

```rs
for (entity, metadata) in self.files.iter() {
let file = file_icon(metadata)
let file = mouse_listener(file_icon(metadata))
.on_press(Message::Drag(entity))
.on_release(Message::Drop)
.on_right_release(Message::Context(entity))
.on_middle_release(Message::OpenFile(entity))
.on_mouse_enter(Message::IconEnter(entity))
.on_mouse_exit(Message::IconExit(entity));

icon_widgets.push(file.into());
}
```
Expand All @@ -34,7 +34,7 @@ A tabbed interface may also want to provide widgets that are draggable with cont

```rs
for (entity, metadata) in self.tabs.iter() {
let tab = tab(metadata)
let tab = mouse_listener(tab(metadata))
.on_press(Message::TabDrag(entity))
.on_release(Message::TabDrop)
.on_right_release(Message::TabContext(entity))
Expand All @@ -51,109 +51,63 @@ for (entity, metadata) in self.tabs.iter() {
Another use case for this widget is a headerbar, which is a container placed at the top of the window to serve as a replacement for a window title bar when server-side decorations are disabled. If any part of the container is clicked that does not intercept that click event, then the container itself will receive the click and make it possible to initiate a window drag.

```rs
let headerbar = event_container(vec![left, center, right])
let headerbar = mouse_listener(row(vec![left, center, right]))
.on_press(Message::WindowDrag)
.on_release(Message::WindowMaximize)
.on_right_release(Message::WindowContext);
```

## Implementation strategy

Implementation is very similar to the container widget, but with a handful of possible events that can be emitted:
The implementation is simple, and only requires thinly wrapping any existing element to intercept mouse events over it.

```rs
struct EventMessages<Message> {
pub struct MouseListener<'a, Message, Renderer> {
content: Element<'a, Message, Renderer>,

/// Sets the message to emit on a left mouse button press.
on_press: Option<Message>,

/// Sets the message to emit on a left mouse button release.
on_release: Option<Message>,

/// Sets the message to emit on a right mouse button press.
on_right_press: Option<Message>,

/// Sets the message to emit on a right mouse button release.
on_right_release: Option<Message>,
}
```

With an update method for publishing these messages on certain input events:
/// Sets the message to emit on a middle mouse button press.
on_middle_press: Option<Message>,

```rs
fn update<Message: Clone>(
event: Event,
layout: Layout<'_>,
cursor_position: Point,
shell: &mut Shell<'_, Message>,
messages: &EventMessages<Message>,
) -> event::Status {
let contains_bound = || layout.bounds().contains(cursor_position);

if let Some(message) = messages.on_press.clone() {
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
| Event::Touch(touch::Event::FingerPressed { .. }) = event
{
return if contains_bound() {
shell.publish(message);
event::Status::Captured
} else {
event::Status::Ignored
};
}
}

if let Some(message) = messages.on_release.clone() {
if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
| Event::Touch(touch::Event::FingerLifted { .. }) = event
{
return if contains_bound() {
shell.publish(message);
event::Status::Captured
} else {
event::Status::Ignored
};
}
}

if let Some(message) = messages.on_right_press.clone() {
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) =
event
{
return if contains_bound() {
shell.publish(message);
event::Status::Captured
} else {
event::Status::Ignored
};
}
}

if let Some(message) = messages.on_right_release.clone() {
if let Event::Mouse(mouse::Event::ButtonReleased(
mouse::Button::Right,
)) = event
{
return if contains_bound() {
shell.publish(message);
event::Status::Captured
} else {
event::Status::Ignored
};
}
}

event::Status::Ignored
/// Sets the message to emit on a middle mouse button release.
on_middle_release: Option<Message>,

/// Sets the message to emit when the mouse enters the widget.
on_mouse_enter: Option<Message>,

/// Sets the messsage to emit when the mouse exits the widget.
on_mouse_exit: Option<Message>,
}
```

The update method will emit these messages when their criteria is met.

## Drawbacks

There's some duplicated code between the container widget and event_container widget.
N/A

## Rationale and alternatives

There doesn't seem to be any alternative approach besides enforcing application authors to bring their own event containers when needing to create a more sophisticated evented widget.
There doesn't seem to be any alternative approach besides enforcing application authors to bring their own mouse-event containers when needing to create a more sophisticated evented widget.

## Prior Art

Similar to `gtk::EventBox` from GTK, which is essentially a `gtk::Box` but with events that can be intercepted and programmed with callbacks. The implementation here is much more ergonomic in comparison.

## Unresolved questions

Perhaps there's a way to handle events in a more flexible way that isn't soo cumbersome, since directly matching input events is rather tedious.
None

## Future Possibilities

Expand Down

0 comments on commit ca157d8

Please sign in to comment.