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
16 changes: 14 additions & 2 deletions core/src/clipboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct Clipboard {
/// The read requests the runtime must fulfill.
pub reads: Vec<Kind>,
pub reads: Vec<(ClipboardKind, Kind)>,
/// The content that must be written to the clipboard by the runtime,
/// if any.
pub write: Option<Content>,
pub write: Option<(ClipboardKind, Content)>,
}

impl Clipboard {
Expand All @@ -34,6 +34,18 @@ impl Default for Clipboard {
}
}

/// The kind of [`Clipboard`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClipboardKind {
/// The standard clipboard.
Standard,

/// The primary clipboard.
///
/// Normally only present in X11 and Wayland.
Primary,
}

/// A clipboard event.
#[derive(Debug, Clone, PartialEq)]
pub enum Event {
Expand Down
5 changes: 5 additions & 0 deletions core/src/mouse/click.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ impl Click {
self.kind
}

/// Returns the [`Button`] of [`Click`].
pub fn button(&self) -> Button {
self.button
}

/// Returns the position of the [`Click`].
pub fn position(&self) -> Point {
self.position
Expand Down
22 changes: 20 additions & 2 deletions core/src/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,32 @@ impl<'a, Message> Shell<'a, Message> {
///
/// The runtime will produce a [`clipboard::Event::Read`] when the contents have been read.
pub fn read_clipboard(&mut self, kind: clipboard::Kind) {
self.clipboard.reads.push(kind);
self.clipboard
.reads
.push((clipboard::ClipboardKind::Standard, kind));
}

/// Requests the runtime to read the primary clipboard contents expecting the given [`clipboard::Kind`].
///
/// The runtime will produce a [`clipboard::Event::Read`] when the contents have been read.
pub fn read_clipboard_primary(&mut self, kind: clipboard::Kind) {
self.clipboard
.reads
.push((clipboard::ClipboardKind::Primary, kind));
}

/// Requests the runtime to write the given [`clipboard::Content`] to the clipboard.
///
/// The runtime will produce a [`clipboard::Event::Written`] when the contents have been written.
pub fn write_clipboard(&mut self, content: clipboard::Content) {
self.clipboard.write = Some(content);
self.clipboard.write = Some((clipboard::ClipboardKind::Standard, content));
}

/// Requests the runtime to write the given [`clipboard::Content`] to the primary clipboard.
///
/// The runtime will produce a [`clipboard::Event::Written`] when the contents have been written.
pub fn write_clipboard_primary(&mut self, content: clipboard::Content) {
self.clipboard.write = Some((clipboard::ClipboardKind::Primary, content));
}

/// Returns the [`Clipboard`] requests of the [`Shell`], mutably.
Expand Down
54 changes: 50 additions & 4 deletions runtime/src/clipboard.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Access the clipboard.
use crate::core::clipboard::{Content, Error, Kind};
use crate::core::clipboard::{ClipboardKind, Content, Error, Kind};
use crate::futures::futures::channel::oneshot;
use crate::task::{self, Task};

Expand All @@ -13,6 +13,8 @@ use std::sync::Arc;
pub enum Action {
/// Read the clipboard and produce `T` with the result.
Read {
/// The kind of clipboard to read from.
clipboard_kind: ClipboardKind,
/// The [`Kind`] of [`Content`] to read.
kind: Kind,
/// The channel to send the read contents.
Expand All @@ -21,6 +23,9 @@ pub enum Action {

/// Write the given contents to the clipboard.
Write {
/// The kind of clipboard to write to.
clipboard_kind: ClipboardKind,

/// The [`Content`] to be written.
content: Content,

Expand All @@ -31,14 +36,33 @@ pub enum Action {

/// Read the given [`Kind`] of [`Content`] from the clipboard.
pub fn read(kind: Kind) -> Task<Result<Arc<Content>, Error>> {
task::oneshot(|channel| crate::Action::Clipboard(Action::Read { kind, channel }))
.map(|result| result.map(Arc::new))
task::oneshot(|channel| {
crate::Action::Clipboard(Action::Read {
clipboard_kind: ClipboardKind::Standard,
kind,
channel,
})
})
.map(|result| result.map(Arc::new))
}

/// Read the given [`Kind`] of [`Content`] from the primary clipboard.
pub fn read_primary(kind: Kind) -> Task<Result<Arc<Content>, Error>> {
task::oneshot(|channel| {
crate::Action::Clipboard(Action::Read {
clipboard_kind: ClipboardKind::Primary,
kind,
channel,
})
})
.map(|result| result.map(Arc::new))
}

/// Read the current text contents of the clipboard.
pub fn read_text() -> Task<Result<Arc<String>, Error>> {
task::oneshot(|channel| {
crate::Action::Clipboard(Action::Read {
clipboard_kind: ClipboardKind::Standard,
kind: Kind::Text,
channel,
})
Expand All @@ -56,6 +80,7 @@ pub fn read_text() -> Task<Result<Arc<String>, Error>> {
pub fn read_html() -> Task<Result<Arc<String>, Error>> {
task::oneshot(|channel| {
crate::Action::Clipboard(Action::Read {
clipboard_kind: ClipboardKind::Standard,
kind: Kind::Html,
channel,
})
Expand All @@ -73,6 +98,7 @@ pub fn read_html() -> Task<Result<Arc<String>, Error>> {
pub fn read_files() -> Task<Result<Arc<[PathBuf]>, Error>> {
task::oneshot(|channel| {
crate::Action::Clipboard(Action::Read {
clipboard_kind: ClipboardKind::Standard,
kind: Kind::Files,
channel,
})
Expand All @@ -91,6 +117,7 @@ pub fn read_files() -> Task<Result<Arc<[PathBuf]>, Error>> {
pub fn read_image() -> Task<Result<crate::core::clipboard::Image, Error>> {
task::oneshot(|channel| {
crate::Action::Clipboard(Action::Read {
clipboard_kind: ClipboardKind::Standard,
kind: Kind::Image,
channel,
})
Expand All @@ -108,5 +135,24 @@ pub fn read_image() -> Task<Result<crate::core::clipboard::Image, Error>> {
pub fn write(content: impl Into<Content>) -> Task<Result<(), Error>> {
let content = content.into();

task::oneshot(|channel| crate::Action::Clipboard(Action::Write { content, channel }))
task::oneshot(|channel| {
crate::Action::Clipboard(Action::Write {
clipboard_kind: ClipboardKind::Standard,
content,
channel,
})
})
}

/// Write the given [`Content`] to the primary clipboard.
pub fn write_primary(content: impl Into<Content>) -> Task<Result<(), Error>> {
let content = content.into();

task::oneshot(|channel| {
crate::Action::Clipboard(Action::Write {
clipboard_kind: ClipboardKind::Primary,
content,
channel,
})
})
}
6 changes: 4 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,8 +559,10 @@ pub mod task {

pub mod clipboard {
//! Access the clipboard.
pub use crate::core::clipboard::{Content, Error, Kind};
pub use crate::runtime::clipboard::{read, read_files, read_html, read_text, write};
pub use crate::core::clipboard::{ClipboardKind, Content, Error, Kind};
pub use crate::runtime::clipboard::{
read, read_files, read_html, read_primary, read_text, write, write_primary,
};

#[cfg(feature = "image")]
pub use crate::core::clipboard::Image;
Expand Down
49 changes: 35 additions & 14 deletions widget/src/text_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -707,24 +707,42 @@ where
) {
match update {
Update::Click(click) => {
let action = match click.kind() {
mouse::click::Kind::Single => Action::Click(click.position()),
mouse::click::Kind::Double => Action::SelectWord,
mouse::click::Kind::Triple => Action::SelectLine,
};

state.focus = Some(Focus::now());
state.last_click = Some(click);
state.drag_click = Some(click.kind());

shell.publish(on_edit(action));
match click.button() {
mouse::Button::Left => {
let action = match click.kind() {
mouse::click::Kind::Single => Action::Click(click.position()),
mouse::click::Kind::Double => Action::SelectWord,
mouse::click::Kind::Triple => Action::SelectLine,
};

state.drag_click = Some(click.kind());

shell.publish(on_edit(action));
}
mouse::Button::Middle if cfg!(target_os = "linux") => {
shell.publish(on_edit(Action::Click(click.position())));

shell.read_clipboard_primary(clipboard::Kind::Text);
}
_ => (),
}

shell.capture_event();
}
Update::Drag(position) => {
shell.publish(on_edit(Action::Drag(position)));
}
Update::Release => {
state.drag_click = None;

if cfg!(target_os = "linux") && state.focus.is_some() {
shell.write_clipboard_primary(clipboard::Content::Text(
self.content.selection().unwrap_or_default(),
));
}
}
Update::Scroll(lines) => {
let bounds = self.content.0.borrow().editor.bounds();
Expand Down Expand Up @@ -1213,16 +1231,16 @@ impl<Message> Update<Message> {

match event {
Event::Mouse(event) => match event {
mouse::Event::ButtonPressed(mouse::Button::Left) => {
mouse::Event::ButtonPressed(button)
if matches!(button, mouse::Button::Left)
|| (cfg!(target_os = "linux")
&& matches!(button, mouse::Button::Middle)) =>
{
if let Some(cursor_position) = cursor.position_in(bounds) {
let cursor_position =
cursor_position - Vector::new(padding.left, padding.top);

let click = mouse::Click::new(
cursor_position,
mouse::Button::Left,
state.last_click,
);
let click = mouse::Click::new(cursor_position, *button, state.last_click);

Some(Update::Click(click))
} else if state.focus.is_some() {
Expand Down Expand Up @@ -1302,6 +1320,9 @@ impl<Message> Update<Message> {
}
.map(Self::Binding)
}
Event::Keyboard(keyboard::Event::KeyReleased { .. }) if cfg!(target_os = "linux") => {
Some(Update::Release)
}
_ => None,
}
}
Expand Down
Loading