Merge branch 'master' into rescan
This commit is contained in:
+28
-7
@@ -5,7 +5,7 @@ use crate::{
|
||||
platform::{self, WindowOptions},
|
||||
presenter::Presenter,
|
||||
util::post_inc,
|
||||
AssetCache, AssetSource, FontCache, TextLayoutCache,
|
||||
AssetCache, AssetSource, ClipboardItem, FontCache, PathPromptOptions, TextLayoutCache,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_std::sync::Condvar;
|
||||
@@ -141,12 +141,13 @@ impl App {
|
||||
{
|
||||
let presenter = presenter.clone();
|
||||
let path = presenter.borrow().dispatch_path(ctx.as_ref());
|
||||
if ctx.dispatch_action_any(key_window_id, &path, command, arg.unwrap_or(&())) {
|
||||
return;
|
||||
}
|
||||
ctx.dispatch_action_any(key_window_id, &path, command, arg.unwrap_or(&()));
|
||||
} else {
|
||||
ctx.dispatch_global_action_any(command, arg.unwrap_or(&()));
|
||||
}
|
||||
} else {
|
||||
ctx.dispatch_global_action_any(command, arg.unwrap_or(&()));
|
||||
}
|
||||
ctx.dispatch_global_action_any(command, arg.unwrap_or(&()));
|
||||
}));
|
||||
|
||||
app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0));
|
||||
@@ -570,6 +571,22 @@ impl MutableAppContext {
|
||||
self.platform.set_menus(menus);
|
||||
}
|
||||
|
||||
pub fn prompt_for_paths<F>(&self, options: PathPromptOptions, done_fn: F)
|
||||
where
|
||||
F: 'static + FnOnce(Option<Vec<PathBuf>>, &mut MutableAppContext),
|
||||
{
|
||||
let app = self.weak_self.as_ref().unwrap().upgrade().unwrap();
|
||||
let foreground = self.foreground.clone();
|
||||
self.platform().prompt_for_paths(
|
||||
options,
|
||||
Box::new(move |paths| {
|
||||
foreground
|
||||
.spawn(async move { (done_fn)(paths, &mut *app.borrow_mut()) })
|
||||
.detach();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn dispatch_action<T: 'static + Any>(
|
||||
&mut self,
|
||||
window_id: usize,
|
||||
@@ -1213,8 +1230,12 @@ impl MutableAppContext {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy(&self, text: &str) {
|
||||
self.platform.copy(text);
|
||||
pub fn write_to_clipboard(&self, item: ClipboardItem) {
|
||||
self.platform.write_to_clipboard(item);
|
||||
}
|
||||
|
||||
pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
|
||||
self.platform.read_from_clipboard()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
use seahash::SeaHasher;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ClipboardItem {
|
||||
pub(crate) text: String,
|
||||
pub(crate) metadata: Option<String>,
|
||||
}
|
||||
|
||||
impl ClipboardItem {
|
||||
pub fn new(text: String) -> Self {
|
||||
Self {
|
||||
text,
|
||||
metadata: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_metadata<T: Serialize>(mut self, metadata: T) -> Self {
|
||||
self.metadata = Some(serde_json::to_string(&metadata).unwrap());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn text(&self) -> &String {
|
||||
&self.text
|
||||
}
|
||||
|
||||
pub fn metadata<T>(&self) -> Option<T>
|
||||
where
|
||||
T: for<'a> Deserialize<'a>,
|
||||
{
|
||||
self.metadata
|
||||
.as_ref()
|
||||
.and_then(|m| serde_json::from_str(m).ok())
|
||||
}
|
||||
|
||||
pub(crate) fn text_hash(text: &str) -> u64 {
|
||||
let mut hasher = SeaHasher::new();
|
||||
text.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
LayoutContext, PaintContext, SizeConstraint,
|
||||
};
|
||||
use json::ToJson;
|
||||
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
||||
use pathfinder_geometry::vector::Vector2F;
|
||||
use serde_json::json;
|
||||
|
||||
pub struct Align {
|
||||
@@ -19,8 +19,13 @@ impl Align {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn top_center(mut self) -> Self {
|
||||
self.alignment = vec2f(0.0, -1.0);
|
||||
pub fn top(mut self) -> Self {
|
||||
self.alignment.set_y(-1.0);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn right(mut self) -> Self {
|
||||
self.alignment.set_x(1.0);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,11 @@ impl ConstrainedBox {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_min_width(mut self, min_width: f32) -> Self {
|
||||
self.constraint.min.set_x(min_width);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_max_width(mut self, max_width: f32) -> Self {
|
||||
self.constraint.max.set_x(max_width);
|
||||
self
|
||||
@@ -33,6 +38,12 @@ impl ConstrainedBox {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_width(mut self, width: f32) -> Self {
|
||||
self.constraint.min.set_x(width);
|
||||
self.constraint.max.set_x(width);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_height(mut self, height: f32) -> Self {
|
||||
self.constraint.min.set_y(height);
|
||||
self.constraint.max.set_y(height);
|
||||
@@ -51,6 +62,7 @@ impl Element for ConstrainedBox {
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
constraint.min = constraint.min.max(self.constraint.min);
|
||||
constraint.max = constraint.max.min(self.constraint.max);
|
||||
constraint.max = constraint.max.max(constraint.min);
|
||||
let size = self.child.layout(constraint, ctx);
|
||||
(size, ())
|
||||
}
|
||||
@@ -91,6 +103,6 @@ impl Element for ConstrainedBox {
|
||||
_: &Self::PaintState,
|
||||
ctx: &DebugContext,
|
||||
) -> json::Value {
|
||||
json!({"type": "ConstrainedBox", "constraint": self.constraint.to_json(), "child": self.child.debug(ctx)})
|
||||
json!({"type": "ConstrainedBox", "set_constraint": self.constraint.to_json(), "child": self.child.debug(ctx)})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,18 @@ impl Container {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_horizontal_padding(mut self, padding: f32) -> Self {
|
||||
self.padding.left = padding;
|
||||
self.padding.right = padding;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_vertical_padding(mut self, padding: f32) -> Self {
|
||||
self.padding.top = padding;
|
||||
self.padding.bottom = padding;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_uniform_padding(mut self, padding: f32) -> Self {
|
||||
self.padding = Padding {
|
||||
top: padding,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::any::Any;
|
||||
use std::{any::Any, f32::INFINITY};
|
||||
|
||||
use crate::{
|
||||
json::{self, ToJson, Value},
|
||||
@@ -64,8 +64,16 @@ impl Element for Flex {
|
||||
if let Some(flex) = Self::child_flex(&child) {
|
||||
total_flex += flex;
|
||||
} else {
|
||||
let child_constraint =
|
||||
SizeConstraint::strict_along(cross_axis, constraint.max_along(cross_axis));
|
||||
let child_constraint = match self.axis {
|
||||
Axis::Horizontal => SizeConstraint::new(
|
||||
vec2f(0.0, constraint.min.y()),
|
||||
vec2f(INFINITY, constraint.max.y()),
|
||||
),
|
||||
Axis::Vertical => SizeConstraint::new(
|
||||
vec2f(constraint.min.x(), 0.0),
|
||||
vec2f(constraint.max.x(), INFINITY),
|
||||
),
|
||||
};
|
||||
let size = child.layout(child_constraint, ctx);
|
||||
fixed_space += size.along(self.axis);
|
||||
cross_axis_max = cross_axis_max.max(size.along(cross_axis));
|
||||
@@ -80,16 +88,20 @@ impl Element for Flex {
|
||||
let mut remaining_space = constraint.max_along(self.axis) - fixed_space;
|
||||
let mut remaining_flex = total_flex;
|
||||
for child in &mut self.children {
|
||||
let space_per_flex = remaining_space / remaining_flex;
|
||||
if let Some(flex) = Self::child_flex(&child) {
|
||||
let child_max = space_per_flex * flex;
|
||||
let child_max = if remaining_flex == 0.0 {
|
||||
remaining_space
|
||||
} else {
|
||||
let space_per_flex = remaining_space / remaining_flex;
|
||||
space_per_flex * flex
|
||||
};
|
||||
let child_constraint = match self.axis {
|
||||
Axis::Horizontal => SizeConstraint::new(
|
||||
vec2f(0.0, constraint.max.y()),
|
||||
vec2f(0.0, constraint.min.y()),
|
||||
vec2f(child_max, constraint.max.y()),
|
||||
),
|
||||
Axis::Vertical => SizeConstraint::new(
|
||||
vec2f(constraint.max.x(), 0.0),
|
||||
vec2f(constraint.min.x(), 0.0),
|
||||
vec2f(constraint.max.x(), child_max),
|
||||
),
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::{
|
||||
SizeConstraint,
|
||||
};
|
||||
use core::panic;
|
||||
use json::ToJson;
|
||||
use replace_with::replace_with_or_abort;
|
||||
use std::{any::Any, borrow::Cow};
|
||||
|
||||
@@ -90,11 +91,13 @@ pub enum Lifecycle<T: Element> {
|
||||
},
|
||||
PostLayout {
|
||||
element: T,
|
||||
constraint: SizeConstraint,
|
||||
size: Vector2F,
|
||||
layout: T::LayoutState,
|
||||
},
|
||||
PostPaint {
|
||||
element: T,
|
||||
constraint: SizeConstraint,
|
||||
bounds: RectF,
|
||||
layout: T::LayoutState,
|
||||
paint: T::PaintState,
|
||||
@@ -119,6 +122,7 @@ impl<T: Element> AnyElement for Lifecycle<T> {
|
||||
result = Some(size);
|
||||
Lifecycle::PostLayout {
|
||||
element,
|
||||
constraint,
|
||||
size,
|
||||
layout,
|
||||
}
|
||||
@@ -132,6 +136,7 @@ impl<T: Element> AnyElement for Lifecycle<T> {
|
||||
element,
|
||||
size,
|
||||
layout,
|
||||
..
|
||||
} = self
|
||||
{
|
||||
element.after_layout(*size, layout, ctx);
|
||||
@@ -144,6 +149,7 @@ impl<T: Element> AnyElement for Lifecycle<T> {
|
||||
replace_with_or_abort(self, |me| {
|
||||
if let Lifecycle::PostLayout {
|
||||
mut element,
|
||||
constraint,
|
||||
size,
|
||||
mut layout,
|
||||
} = me
|
||||
@@ -152,6 +158,7 @@ impl<T: Element> AnyElement for Lifecycle<T> {
|
||||
let paint = element.paint(bounds, &mut layout, ctx);
|
||||
Lifecycle::PostPaint {
|
||||
element,
|
||||
constraint,
|
||||
bounds,
|
||||
layout,
|
||||
paint,
|
||||
@@ -168,6 +175,7 @@ impl<T: Element> AnyElement for Lifecycle<T> {
|
||||
bounds,
|
||||
layout,
|
||||
paint,
|
||||
..
|
||||
} = self
|
||||
{
|
||||
element.dispatch_event(event, *bounds, layout, paint, ctx)
|
||||
@@ -196,10 +204,25 @@ impl<T: Element> AnyElement for Lifecycle<T> {
|
||||
match self {
|
||||
Lifecycle::PostPaint {
|
||||
element,
|
||||
constraint,
|
||||
bounds,
|
||||
layout,
|
||||
paint,
|
||||
} => element.debug(*bounds, layout, paint, ctx),
|
||||
} => {
|
||||
let mut value = element.debug(*bounds, layout, paint, ctx);
|
||||
if let json::Value::Object(map) = &mut value {
|
||||
let mut new_map: crate::json::Map<String, serde_json::Value> =
|
||||
Default::default();
|
||||
if let Some(typ) = map.remove("type") {
|
||||
new_map.insert("type".into(), typ);
|
||||
}
|
||||
new_map.insert("constraint".into(), constraint.to_json());
|
||||
new_map.append(map);
|
||||
json::Value::Object(new_map)
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
_ => panic!("invalid element lifecycle state"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ pub use assets::*;
|
||||
pub mod elements;
|
||||
pub mod font_cache;
|
||||
pub use font_cache::FontCache;
|
||||
mod clipboard;
|
||||
pub use clipboard::ClipboardItem;
|
||||
pub mod fonts;
|
||||
pub mod geometry;
|
||||
mod presenter;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::{BoolExt as _, Dispatcher, FontSystem, Window};
|
||||
use crate::{executor, keymap::Keystroke, platform, Event, Menu, MenuItem};
|
||||
use crate::{executor, keymap::Keystroke, platform, ClipboardItem, Event, Menu, MenuItem};
|
||||
use block::ConcreteBlock;
|
||||
use cocoa::{
|
||||
appkit::{
|
||||
NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
|
||||
@@ -20,12 +21,14 @@ use objc::{
|
||||
use ptr::null_mut;
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::RefCell,
|
||||
cell::{Cell, RefCell},
|
||||
convert::TryInto,
|
||||
ffi::{c_void, CStr},
|
||||
os::raw::c_char,
|
||||
path::PathBuf,
|
||||
ptr,
|
||||
rc::Rc,
|
||||
slice, str,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
@@ -77,6 +80,9 @@ pub struct MacPlatform {
|
||||
fonts: Arc<FontSystem>,
|
||||
callbacks: RefCell<Callbacks>,
|
||||
menu_item_actions: RefCell<Vec<(String, Option<Box<dyn Any>>)>>,
|
||||
pasteboard: id,
|
||||
text_hash_pasteboard_type: id,
|
||||
metadata_pasteboard_type: id,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -96,6 +102,9 @@ impl MacPlatform {
|
||||
fonts: Arc::new(FontSystem::new()),
|
||||
callbacks: Default::default(),
|
||||
menu_item_actions: Default::default(),
|
||||
pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
|
||||
text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
|
||||
metadata_pasteboard_type: unsafe { ns_string("zed-metadata") },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +185,18 @@ impl MacPlatform {
|
||||
|
||||
menu_bar
|
||||
}
|
||||
|
||||
unsafe fn read_from_pasteboard(&self, kind: id) -> Option<&[u8]> {
|
||||
let data = self.pasteboard.dataForType(kind);
|
||||
if data == nil {
|
||||
None
|
||||
} else {
|
||||
Some(slice::from_raw_parts(
|
||||
data.bytes() as *mut u8,
|
||||
data.length() as usize,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl platform::Platform for MacPlatform {
|
||||
@@ -247,31 +268,40 @@ impl platform::Platform for MacPlatform {
|
||||
fn prompt_for_paths(
|
||||
&self,
|
||||
options: platform::PathPromptOptions,
|
||||
) -> Option<Vec<std::path::PathBuf>> {
|
||||
done_fn: Box<dyn FnOnce(Option<Vec<std::path::PathBuf>>)>,
|
||||
) {
|
||||
unsafe {
|
||||
let panel = NSOpenPanel::openPanel(nil);
|
||||
panel.setCanChooseDirectories_(options.directories.to_objc());
|
||||
panel.setCanChooseFiles_(options.files.to_objc());
|
||||
panel.setAllowsMultipleSelection_(options.multiple.to_objc());
|
||||
panel.setResolvesAliases_(false.to_objc());
|
||||
let response = panel.runModal();
|
||||
if response == NSModalResponse::NSModalResponseOk {
|
||||
let mut result = Vec::new();
|
||||
let urls = panel.URLs();
|
||||
for i in 0..urls.count() {
|
||||
let url = urls.objectAtIndex(i);
|
||||
let string = url.absoluteString();
|
||||
let string = std::ffi::CStr::from_ptr(string.UTF8String())
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
if let Some(path) = string.strip_prefix("file://") {
|
||||
result.push(PathBuf::from(path));
|
||||
let done_fn = Cell::new(Some(done_fn));
|
||||
let block = ConcreteBlock::new(move |response: NSModalResponse| {
|
||||
let result = if response == NSModalResponse::NSModalResponseOk {
|
||||
let mut result = Vec::new();
|
||||
let urls = panel.URLs();
|
||||
for i in 0..urls.count() {
|
||||
let url = urls.objectAtIndex(i);
|
||||
let string = url.absoluteString();
|
||||
let string = std::ffi::CStr::from_ptr(string.UTF8String())
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
if let Some(path) = string.strip_prefix("file://") {
|
||||
result.push(PathBuf::from(path));
|
||||
}
|
||||
}
|
||||
Some(result)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(done_fn) = done_fn.take() {
|
||||
(done_fn)(result);
|
||||
}
|
||||
Some(result)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let block = block.copy();
|
||||
let _: () = msg_send![panel, beginWithCompletionHandler: block];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,16 +316,72 @@ impl platform::Platform for MacPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
fn copy(&self, text: &str) {
|
||||
fn write_to_clipboard(&self, item: ClipboardItem) {
|
||||
unsafe {
|
||||
let data = NSData::dataWithBytes_length_(
|
||||
self.pasteboard.clearContents();
|
||||
|
||||
let text_bytes = NSData::dataWithBytes_length_(
|
||||
nil,
|
||||
text.as_ptr() as *const c_void,
|
||||
text.len() as u64,
|
||||
item.text.as_ptr() as *const c_void,
|
||||
item.text.len() as u64,
|
||||
);
|
||||
let pasteboard = NSPasteboard::generalPasteboard(nil);
|
||||
pasteboard.clearContents();
|
||||
pasteboard.setData_forType(data, NSPasteboardTypeString);
|
||||
self.pasteboard
|
||||
.setData_forType(text_bytes, NSPasteboardTypeString);
|
||||
|
||||
if let Some(metadata) = item.metadata.as_ref() {
|
||||
let hash_bytes = ClipboardItem::text_hash(&item.text).to_be_bytes();
|
||||
let hash_bytes = NSData::dataWithBytes_length_(
|
||||
nil,
|
||||
hash_bytes.as_ptr() as *const c_void,
|
||||
hash_bytes.len() as u64,
|
||||
);
|
||||
self.pasteboard
|
||||
.setData_forType(hash_bytes, self.text_hash_pasteboard_type);
|
||||
|
||||
let metadata_bytes = NSData::dataWithBytes_length_(
|
||||
nil,
|
||||
metadata.as_ptr() as *const c_void,
|
||||
metadata.len() as u64,
|
||||
);
|
||||
self.pasteboard
|
||||
.setData_forType(metadata_bytes, self.metadata_pasteboard_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
|
||||
unsafe {
|
||||
if let Some(text_bytes) = self.read_from_pasteboard(NSPasteboardTypeString) {
|
||||
let text = String::from_utf8_lossy(&text_bytes).to_string();
|
||||
let hash_bytes = self
|
||||
.read_from_pasteboard(self.text_hash_pasteboard_type)
|
||||
.and_then(|bytes| bytes.try_into().ok())
|
||||
.map(u64::from_be_bytes);
|
||||
let metadata_bytes = self
|
||||
.read_from_pasteboard(self.metadata_pasteboard_type)
|
||||
.and_then(|bytes| String::from_utf8(bytes.to_vec()).ok());
|
||||
|
||||
if let Some((hash, metadata)) = hash_bytes.zip(metadata_bytes) {
|
||||
if hash == ClipboardItem::text_hash(&text) {
|
||||
Some(ClipboardItem {
|
||||
text,
|
||||
metadata: Some(metadata),
|
||||
})
|
||||
} else {
|
||||
Some(ClipboardItem {
|
||||
text,
|
||||
metadata: None,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Some(ClipboardItem {
|
||||
text,
|
||||
metadata: None,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,3 +478,46 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
|
||||
unsafe fn ns_string(string: &str) -> id {
|
||||
NSString::alloc(nil).init_str(string).autorelease()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::platform::Platform;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_clipboard() {
|
||||
let platform = build_platform();
|
||||
assert_eq!(platform.read_from_clipboard(), None);
|
||||
|
||||
let item = ClipboardItem::new("1".to_string());
|
||||
platform.write_to_clipboard(item.clone());
|
||||
assert_eq!(platform.read_from_clipboard(), Some(item));
|
||||
|
||||
let item = ClipboardItem::new("2".to_string()).with_metadata(vec![3, 4]);
|
||||
platform.write_to_clipboard(item.clone());
|
||||
assert_eq!(platform.read_from_clipboard(), Some(item));
|
||||
|
||||
let text_from_other_app = "text from other app";
|
||||
unsafe {
|
||||
let bytes = NSData::dataWithBytes_length_(
|
||||
nil,
|
||||
text_from_other_app.as_ptr() as *const c_void,
|
||||
text_from_other_app.len() as u64,
|
||||
);
|
||||
platform
|
||||
.pasteboard
|
||||
.setData_forType(bytes, NSPasteboardTypeString);
|
||||
}
|
||||
assert_eq!(
|
||||
platform.read_from_clipboard(),
|
||||
Some(ClipboardItem::new(text_from_other_app.to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
fn build_platform() -> MacPlatform {
|
||||
let mut platform = MacPlatform::new();
|
||||
platform.pasteboard = unsafe { NSPasteboard::pasteboardWithUniqueName(nil) };
|
||||
platform
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{
|
||||
vector::Vector2F,
|
||||
},
|
||||
text_layout::Line,
|
||||
Menu, Scene,
|
||||
ClipboardItem, Menu, Scene,
|
||||
};
|
||||
use async_task::Runnable;
|
||||
pub use event::Event;
|
||||
@@ -40,9 +40,14 @@ pub trait Platform {
|
||||
executor: Rc<executor::Foreground>,
|
||||
) -> Box<dyn Window>;
|
||||
fn key_window_id(&self) -> Option<usize>;
|
||||
fn prompt_for_paths(&self, options: PathPromptOptions) -> Option<Vec<PathBuf>>;
|
||||
fn prompt_for_paths(
|
||||
&self,
|
||||
options: PathPromptOptions,
|
||||
done_fn: Box<dyn FnOnce(Option<Vec<std::path::PathBuf>>)>,
|
||||
);
|
||||
fn quit(&self);
|
||||
fn copy(&self, text: &str);
|
||||
fn write_to_clipboard(&self, item: ClipboardItem);
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
|
||||
fn set_menus(&self, menus: Vec<Menu>);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use crate::ClipboardItem;
|
||||
use pathfinder_geometry::vector::Vector2F;
|
||||
use std::sync::Arc;
|
||||
use std::{any::Any, rc::Rc};
|
||||
use std::{any::Any, cell::RefCell, rc::Rc, sync::Arc};
|
||||
|
||||
struct Platform {
|
||||
dispatcher: Arc<dyn super::Dispatcher>,
|
||||
fonts: Arc<dyn super::FontSystem>,
|
||||
current_clipboard_item: RefCell<Option<ClipboardItem>>,
|
||||
}
|
||||
|
||||
struct Dispatcher;
|
||||
@@ -22,6 +23,7 @@ impl Platform {
|
||||
Self {
|
||||
dispatcher: Arc::new(Dispatcher),
|
||||
fonts: Arc::new(super::current::FontSystem::new()),
|
||||
current_clipboard_item: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,11 +70,20 @@ impl super::Platform for Platform {
|
||||
|
||||
fn quit(&self) {}
|
||||
|
||||
fn prompt_for_paths(&self, _: super::PathPromptOptions) -> Option<Vec<std::path::PathBuf>> {
|
||||
None
|
||||
fn prompt_for_paths(
|
||||
&self,
|
||||
_: super::PathPromptOptions,
|
||||
_: Box<dyn FnOnce(Option<Vec<std::path::PathBuf>>)>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn copy(&self, _: &str) {}
|
||||
fn write_to_clipboard(&self, item: ClipboardItem) {
|
||||
*self.current_clipboard_item.borrow_mut() = Some(item);
|
||||
}
|
||||
|
||||
fn read_from_clipboard(&self) -> Option<ClipboardItem> {
|
||||
self.current_clipboard_item.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
||||
Reference in New Issue
Block a user