Skip to content

Commit

Permalink
feat: clipboard api (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
lemarier committed Jun 21, 2021
1 parent 52ebebb commit cf22c90
Show file tree
Hide file tree
Showing 14 changed files with 423 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changes/clipboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
tao: patch
---

Add `clipboard` api exposing `read_text` and `write_text`.
20 changes: 20 additions & 0 deletions examples/custom_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use simple_logger::SimpleLogger;
#[cfg(target_os = "macos")]
use tao::platform::macos::{CustomMenuItemExtMacOS, NativeImage};
use tao::{
clipboard::Clipboard,
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
menu::{MenuBar as Menu, MenuItem, MenuItemAttributes, MenuType},
Expand All @@ -15,6 +16,9 @@ fn main() {
SimpleLogger::new().init().unwrap();
let event_loop = EventLoop::new();

// create clipboard instance
let mut cliboard = Clipboard::new();

// create main menubar menu
let mut menu_bar_menu = Menu::new();

Expand All @@ -36,6 +40,10 @@ fn main() {
// to works correctly
first_menu.add_native_item(MenuItem::Copy);

// Create custom Copy menu with our clipboard object
let custom_insert_clipboard = first_menu.add_item(MenuItemAttributes::new("Insert clipboard"));
let custom_read_clipboard = first_menu.add_item(MenuItemAttributes::new("Read clipboard"));

// add `my_sub_menu` children of `first_menu` with `Sub menu` title
first_menu.add_submenu("Sub menu", true, my_sub_menu);

Expand Down Expand Up @@ -96,6 +104,18 @@ fn main() {
menu_bar_menu.add_submenu("My app", true, my_app_menu);
window.set_menu(Some(menu_bar_menu))
}
Event::MenuEvent {
menu_id,
origin: MenuType::MenuBar,
} if menu_id == custom_insert_clipboard.clone().id() => {
cliboard.write_text("This is injected from tao!!!")
}
Event::MenuEvent {
menu_id,
origin: MenuType::MenuBar,
} if menu_id == custom_read_clipboard.clone().id() => {
println!("Clipboard content: {:?}", cliboard.read_text());
}
_ => (),
}
});
Expand Down
90 changes: 90 additions & 0 deletions src/clipboard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0

//! The `Clipboard` struct and associated types.
//!
//! ## Platform-specific
//!
//! - **Android / iOS:** Unsupported
//!
//! ```rust,ignore
//! let mut cliboard = Clipboard::new();
//! cliboard.write_text("This is injected from tao!!!")
//! let content = cliboard.read_text();
//! ```
//!

use crate::platform_impl::Clipboard as ClipboardPlatform;

#[derive(Debug, Clone, Default)]
/// Object that allows you to access the `Clipboard` instance.
pub struct Clipboard(ClipboardPlatform);

impl Clipboard {
/// Creates a new `Clipboard` instance.
///
/// ## Platform-specific
///
/// - **Android / iOS:** Unsupported
pub fn new() -> Self {
Self::default()
}

/// Writes the text into the clipboard as plain text.
///
/// ## Platform-specific
///
/// - **Android / iOS:** Unsupported
pub fn write_text(&mut self, s: impl AsRef<str>) {
self.0.write_text(s);
}

/// The content in the clipboard as plain text.
///
/// ## Platform-specific
///
/// - **Android / iOS:** Unsupported
pub fn read_text(&self) -> Option<String> {
self.0.read_text()
}
}

/// Identifier of a clipboard format.
pub(crate) type FormatId = &'static str;

/// Object that allows you to access the `ClipboardFormat`.
#[derive(Debug, Clone)]
pub(crate) struct ClipboardFormat {
pub(crate) identifier: FormatId,
pub(crate) data: Vec<u8>,
}

// todo add more formats
impl ClipboardFormat {
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub const TEXT: &'static str = "public.utf8-plain-text";
#[cfg(any(target_os = "windows", target_os = "android"))]
pub const TEXT: &'static str = "text/plain";
#[cfg(target_os = "linux")]
pub const TEXT: &'static str = "UTF8_STRING";
}

impl ClipboardFormat {
pub fn new(identifier: FormatId, data: impl Into<Vec<u8>>) -> Self {
let data = data.into();
ClipboardFormat { identifier, data }
}
}

impl From<String> for ClipboardFormat {
fn from(src: String) -> ClipboardFormat {
let data = src.into_bytes();
ClipboardFormat::new(ClipboardFormat::TEXT, data)
}
}

impl From<&str> for ClipboardFormat {
fn from(src: &str) -> ClipboardFormat {
src.to_string().into()
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ extern crate bitflags;
#[macro_use]
extern crate objc;

pub mod clipboard;
pub mod dpi;
#[macro_use]
pub mod error;
Expand Down
11 changes: 11 additions & 0 deletions src/platform_impl/android/clipboard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0

#[derive(Debug, Clone, Default)]
pub struct Clipboard;
impl Clipboard {
pub(crate) fn write_text(&mut self, s: impl AsRef<str>) {}
pub(crate) fn read_text(&self) -> Option<String> {
None
}
}
3 changes: 3 additions & 0 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ use std::{
time::{Duration, Instant},
};

mod clipboard;
pub use clipboard::Clipboard;

lazy_static! {
static ref CONFIG: RwLock<Configuration> = RwLock::new(Configuration::new());
}
Expand Down
11 changes: 11 additions & 0 deletions src/platform_impl/ios/clipboard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0

#[derive(Debug, Clone, Default)]
pub struct Clipboard;
impl Clipboard {
pub(crate) fn write_text(&mut self, s: impl AsRef<str>) {}
pub(crate) fn read_text(&self) -> Option<String> {
None
}
}
2 changes: 2 additions & 0 deletions src/platform_impl/ios/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ macro_rules! assert_main_thread {
}

mod app_state;
mod clipboard;
mod event_loop;
mod ffi;
mod monitor;
Expand All @@ -82,6 +83,7 @@ use crate::menu::{CustomMenuItem, MenuId, MenuItem, MenuType};
use std::fmt;

pub use self::{
clipboard::Clipboard,
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
monitor::{MonitorHandle, VideoMode},
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
Expand Down
49 changes: 49 additions & 0 deletions src/platform_impl/linux/clipboard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0

use gdk::Atom;
use gtk::{TargetEntry, TargetFlags};

#[derive(Debug, Clone, Default)]
pub struct Clipboard;

const CLIPBOARD_TARGETS: [&str; 5] = [
"UTF8_STRING",
"TEXT",
"STRING",
"text/plain;charset=utf-8",
"text/plain",
];

impl Clipboard {
pub(crate) fn write_text(&mut self, string: impl AsRef<str>) {
let string = string.as_ref().to_string();

let display = gdk::Display::get_default().unwrap();
let clipboard = gtk::Clipboard::get_default(&display).unwrap();

let targets: Vec<TargetEntry> = CLIPBOARD_TARGETS
.iter()
.enumerate()
.map(|(i, target)| TargetEntry::new(target, TargetFlags::all(), i as u32))
.collect();

clipboard.set_with_data(&targets, move |_, selection, _| {
selection.set(&selection.get_target(), 8 as i32, string.as_bytes());
});
}

pub(crate) fn read_text(&self) -> Option<String> {
let display = gdk::Display::get_default().unwrap();
let clipboard = gtk::Clipboard::get_default(&display).unwrap();

for target in &CLIPBOARD_TARGETS {
let atom = Atom::intern(target);
if let Some(selection) = clipboard.wait_for_contents(&atom) {
return String::from_utf8(selection.get_data()).ok();
}
}

None
}
}
6 changes: 5 additions & 1 deletion src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@
target_os = "openbsd"
))]

mod clipboard;
mod event_loop;
mod menu;
mod monitor;
#[cfg(feature = "tray")]
mod system_tray;
mod window;

pub use self::menu::{Menu, MenuItemAttributes};
#[cfg(feature = "tray")]
pub use self::system_tray::{SystemTray, SystemTrayBuilder};
pub use self::{
clipboard::Clipboard,
menu::{Menu, MenuItemAttributes},
};
pub use event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
pub use monitor::{MonitorHandle, VideoMode};
pub use window::{
Expand Down
41 changes: 41 additions & 0 deletions src/platform_impl/macos/clipboard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0

use cocoa::{
appkit::NSPasteboardTypeString,
base::{id, nil, BOOL, YES},
foundation::{NSInteger, NSString},
};
use objc::{class, msg_send, sel, sel_impl};

#[derive(Debug, Clone, Default)]
pub struct Clipboard;

impl Clipboard {
pub(crate) fn write_text(&mut self, s: impl AsRef<str>) {
let s = s.as_ref();
unsafe {
let nsstring = NSString::alloc(nil).init_str(s);
let pasteboard: id = msg_send![class!(NSPasteboard), generalPasteboard];
let _: NSInteger = msg_send![pasteboard, clearContents];
let result: BOOL = msg_send![pasteboard, setString: nsstring forType: NSPasteboardTypeString];
if result != YES {
println!("failed to set clipboard");
}
}
}

pub(crate) fn read_text(&self) -> Option<String> {
unsafe {
let pasteboard: id = msg_send![class!(NSPasteboard), generalPasteboard];
let contents: id = msg_send![pasteboard, stringForType: NSPasteboardTypeString];
if contents.is_null() {
None
} else {
let slice = std::slice::from_raw_parts(contents.UTF8String() as *const _, contents.len());
let result = std::str::from_utf8_unchecked(slice);
Some(result.to_string())
}
}
}
}
2 changes: 2 additions & 0 deletions src/platform_impl/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
mod app;
mod app_delegate;
mod app_state;
mod clipboard;
mod event;
mod event_loop;
mod ffi;
Expand All @@ -26,6 +27,7 @@ pub use self::system_tray::{SystemTray, SystemTrayBuilder};

pub use self::{
app_delegate::{get_aux_state_mut, AuxDelegateState},
clipboard::Clipboard,
event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy},
menu::{Menu, MenuItemAttributes},
monitor::{MonitorHandle, VideoMode},
Expand Down
Loading

0 comments on commit cf22c90

Please sign in to comment.