Merge branch 'chat'

This commit is contained in:
Antonio Scandurra
2021-09-07 19:21:03 +02:00
105 changed files with 11897 additions and 5996 deletions
+1
View File
@@ -24,5 +24,6 @@ pub enum Event {
},
MouseMoved {
position: Vector2F,
left_mouse_down: bool,
},
}
+3 -2
View File
@@ -6,8 +6,8 @@ use cocoa::appkit::{
NSUpArrowFunctionKey as ARROW_UP_KEY,
};
use cocoa::{
appkit::{NSEvent as _, NSEventModifierFlags, NSEventType},
base::{id, YES},
appkit::{NSEvent, NSEventModifierFlags, NSEventType},
base::{id, nil, YES},
foundation::NSString as _,
};
use std::{ffi::CStr, os::raw::c_char};
@@ -116,6 +116,7 @@ impl Event {
native_event.locationInWindow().x as f32,
window_height - native_event.locationInWindow().y as f32,
),
left_mouse_down: NSEvent::pressedMouseButtons(nil) & 1 != 0,
}),
_ => None,
}
+79 -48
View File
@@ -1,5 +1,4 @@
use crate::{
color::Color,
fonts::{FontId, GlyphId, Metrics, Properties},
geometry::{
rect::{RectF, RectI},
@@ -7,7 +6,7 @@ use crate::{
vector::{vec2f, vec2i, Vector2F},
},
platform,
text_layout::{Glyph, LineLayout, Run},
text_layout::{Glyph, LineLayout, Run, RunStyle},
};
use cocoa::appkit::{CGFloat, CGPoint};
use core_foundation::{
@@ -21,9 +20,12 @@ use core_graphics::{
base::CGGlyph, color_space::CGColorSpace, context::CGContext, geometry::CGAffineTransform,
};
use core_text::{line::CTLine, string_attributes::kCTFontAttributeName};
use font_kit::{canvas::RasterizationOptions, hinting::HintingOptions, source::SystemSource};
use font_kit::{
canvas::RasterizationOptions, handle::Handle, hinting::HintingOptions, source::SystemSource,
sources::mem::MemSource,
};
use parking_lot::RwLock;
use std::{cell::RefCell, char, cmp, convert::TryFrom, ffi::c_void};
use std::{cell::RefCell, char, cmp, convert::TryFrom, ffi::c_void, sync::Arc};
#[allow(non_upper_case_globals)]
const kCGImageAlphaOnly: u32 = 7;
@@ -31,20 +33,26 @@ const kCGImageAlphaOnly: u32 = 7;
pub struct FontSystem(RwLock<FontSystemState>);
struct FontSystemState {
source: SystemSource,
memory_source: MemSource,
system_source: SystemSource,
fonts: Vec<font_kit::font::Font>,
}
impl FontSystem {
pub fn new() -> Self {
Self(RwLock::new(FontSystemState {
source: SystemSource::new(),
memory_source: MemSource::empty(),
system_source: SystemSource::new(),
fonts: Vec::new(),
}))
}
}
impl platform::FontSystem for FontSystem {
fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> anyhow::Result<()> {
self.0.write().add_fonts(fonts)
}
fn load_family(&self, name: &str) -> anyhow::Result<Vec<FontId>> {
self.0.write().load_family(name)
}
@@ -78,12 +86,7 @@ impl platform::FontSystem for FontSystem {
.rasterize_glyph(font_id, font_size, glyph_id, subpixel_shift, scale_factor)
}
fn layout_line(
&self,
text: &str,
font_size: f32,
runs: &[(usize, FontId, Color)],
) -> LineLayout {
fn layout_line(&self, text: &str, font_size: f32, runs: &[(usize, RunStyle)]) -> LineLayout {
self.0.read().layout_line(text, font_size, runs)
}
@@ -93,9 +96,23 @@ impl platform::FontSystem for FontSystem {
}
impl FontSystemState {
fn add_fonts(&mut self, fonts: &[Arc<Vec<u8>>]) -> anyhow::Result<()> {
self.memory_source.add_fonts(
fonts
.iter()
.map(|bytes| Handle::from_memory(bytes.clone(), 0)),
)?;
Ok(())
}
fn load_family(&mut self, name: &str) -> anyhow::Result<Vec<FontId>> {
let mut font_ids = Vec::new();
for font in self.source.select_family_by_name(name)?.fonts() {
let family = self
.memory_source
.select_family_by_name(name)
.or_else(|_| self.system_source.select_family_by_name(name))?;
for font in family.fonts() {
let font = font.load()?;
font_ids.push(FontId(self.fonts.len()));
self.fonts.push(font);
@@ -187,12 +204,7 @@ impl FontSystemState {
}
}
fn layout_line(
&self,
text: &str,
font_size: f32,
runs: &[(usize, FontId, Color)],
) -> LineLayout {
fn layout_line(&self, text: &str, font_size: f32, runs: &[(usize, RunStyle)]) -> LineLayout {
let font_id_attr_name = CFString::from_static_string("zed_font_id");
// Construct the attributed string, converting UTF8 ranges to UTF16 ranges.
@@ -204,20 +216,20 @@ impl FontSystemState {
let last_run: RefCell<Option<(usize, FontId)>> = Default::default();
let font_runs = runs
.iter()
.filter_map(|(len, font_id, _)| {
.filter_map(|(len, style)| {
let mut last_run = last_run.borrow_mut();
if let Some((last_len, last_font_id)) = last_run.as_mut() {
if font_id == last_font_id {
if style.font_id == *last_font_id {
*last_len += *len;
None
} else {
let result = (*last_len, *last_font_id);
*last_len = *len;
*last_font_id = *font_id;
*last_font_id = style.font_id;
Some(result)
}
} else {
*last_run = Some((*len, *font_id));
*last_run = Some((*len, style.font_id));
None
}
})
@@ -392,9 +404,8 @@ extern "C" {
#[cfg(test)]
mod tests {
use crate::MutableAppContext;
use super::*;
use crate::MutableAppContext;
use font_kit::properties::{Style, Weight};
use platform::FontSystem as _;
@@ -403,13 +414,25 @@ mod tests {
// This is failing intermittently on CI and we don't have time to figure it out
let fonts = FontSystem::new();
let menlo = fonts.load_family("Menlo").unwrap();
let menlo_regular = fonts.select_font(&menlo, &Properties::new()).unwrap();
let menlo_italic = fonts
.select_font(&menlo, &Properties::new().style(Style::Italic))
.unwrap();
let menlo_bold = fonts
.select_font(&menlo, &Properties::new().weight(Weight::BOLD))
.unwrap();
let menlo_regular = RunStyle {
font_id: fonts.select_font(&menlo, &Properties::new()).unwrap(),
color: Default::default(),
underline: false,
};
let menlo_italic = RunStyle {
font_id: fonts
.select_font(&menlo, &Properties::new().style(Style::Italic))
.unwrap(),
color: Default::default(),
underline: false,
};
let menlo_bold = RunStyle {
font_id: fonts
.select_font(&menlo, &Properties::new().weight(Weight::BOLD))
.unwrap(),
color: Default::default(),
underline: false,
};
assert_ne!(menlo_regular, menlo_italic);
assert_ne!(menlo_regular, menlo_bold);
assert_ne!(menlo_italic, menlo_bold);
@@ -417,18 +440,14 @@ mod tests {
let line = fonts.layout_line(
"hello world",
16.0,
&[
(2, menlo_bold, Default::default()),
(4, menlo_italic, Default::default()),
(5, menlo_regular, Default::default()),
],
&[(2, menlo_bold), (4, menlo_italic), (5, menlo_regular)],
);
assert_eq!(line.runs.len(), 3);
assert_eq!(line.runs[0].font_id, menlo_bold);
assert_eq!(line.runs[0].font_id, menlo_bold.font_id);
assert_eq!(line.runs[0].glyphs.len(), 2);
assert_eq!(line.runs[1].font_id, menlo_italic);
assert_eq!(line.runs[1].font_id, menlo_italic.font_id);
assert_eq!(line.runs[1].glyphs.len(), 4);
assert_eq!(line.runs[2].font_id, menlo_regular);
assert_eq!(line.runs[2].font_id, menlo_regular.font_id);
assert_eq!(line.runs[2].glyphs.len(), 5);
}
@@ -436,18 +455,26 @@ mod tests {
fn test_glyph_offsets() -> anyhow::Result<()> {
let fonts = FontSystem::new();
let zapfino = fonts.load_family("Zapfino")?;
let zapfino_regular = fonts.select_font(&zapfino, &Properties::new())?;
let zapfino_regular = RunStyle {
font_id: fonts.select_font(&zapfino, &Properties::new())?,
color: Default::default(),
underline: false,
};
let menlo = fonts.load_family("Menlo")?;
let menlo_regular = fonts.select_font(&menlo, &Properties::new())?;
let menlo_regular = RunStyle {
font_id: fonts.select_font(&menlo, &Properties::new())?,
color: Default::default(),
underline: false,
};
let text = "This is, m𐍈re 𐍈r less, Zapfino!𐍈";
let line = fonts.layout_line(
text,
16.0,
&[
(9, zapfino_regular, Color::default()),
(13, menlo_regular, Color::default()),
(text.len() - 22, zapfino_regular, Color::default()),
(9, zapfino_regular),
(13, menlo_regular),
(text.len() - 22, zapfino_regular),
],
);
assert_eq!(
@@ -513,15 +540,19 @@ mod tests {
fn test_layout_line_bom_char() {
let fonts = FontSystem::new();
let font_ids = fonts.load_family("Helvetica").unwrap();
let font_id = fonts.select_font(&font_ids, &Default::default()).unwrap();
let style = RunStyle {
font_id: fonts.select_font(&font_ids, &Default::default()).unwrap(),
color: Default::default(),
underline: false,
};
let line = "\u{feff}";
let layout = fonts.layout_line(line, 16., &[(line.len(), font_id, Default::default())]);
let layout = fonts.layout_line(line, 16., &[(line.len(), style)]);
assert_eq!(layout.len, line.len());
assert!(layout.runs.is_empty());
let line = "a\u{feff}b";
let layout = fonts.layout_line(line, 16., &[(line.len(), font_id, Default::default())]);
let layout = fonts.layout_line(line, 16., &[(line.len(), style)]);
assert_eq!(layout.len, line.len());
assert_eq!(layout.runs.len(), 1);
assert_eq!(layout.runs[0].glyphs.len(), 2);
+45 -20
View File
@@ -1,5 +1,11 @@
use super::{BoolExt as _, Dispatcher, FontSystem, Window};
use crate::{executor, keymap::Keystroke, platform, ClipboardItem, Event, Menu, MenuItem};
use crate::{
executor,
keymap::Keystroke,
platform::{self, CursorStyle},
AnyAction, ClipboardItem, Event, Menu, MenuItem,
};
use anyhow::{anyhow, Result};
use block::ConcreteBlock;
use cocoa::{
appkit::{
@@ -27,7 +33,6 @@ use objc::{
};
use ptr::null_mut;
use std::{
any::Any,
cell::{Cell, RefCell},
convert::TryInto,
ffi::{c_void, CStr},
@@ -38,6 +43,7 @@ use std::{
slice, str,
sync::Arc,
};
use time::UtcOffset;
const MAC_PLATFORM_IVAR: &'static str = "platform";
static mut APP_CLASS: *const Class = ptr::null();
@@ -90,10 +96,10 @@ pub struct MacForegroundPlatformState {
become_active: Option<Box<dyn FnMut()>>,
resign_active: Option<Box<dyn FnMut()>>,
event: Option<Box<dyn FnMut(crate::Event) -> bool>>,
menu_command: Option<Box<dyn FnMut(&str, Option<&dyn Any>)>>,
menu_command: Option<Box<dyn FnMut(&dyn AnyAction)>>,
open_files: Option<Box<dyn FnMut(Vec<PathBuf>)>>,
finish_launching: Option<Box<dyn FnOnce() -> ()>>,
menu_actions: Vec<(String, Option<Box<dyn Any>>)>,
menu_actions: Vec<Box<dyn AnyAction>>,
}
impl MacForegroundPlatform {
@@ -121,7 +127,6 @@ impl MacForegroundPlatform {
name,
keystroke,
action,
arg,
} => {
if let Some(keystroke) = keystroke {
let keystroke = Keystroke::parse(keystroke).unwrap_or_else(|err| {
@@ -162,7 +167,7 @@ impl MacForegroundPlatform {
let tag = state.menu_actions.len() as NSInteger;
let _: () = msg_send![item, setTag: tag];
state.menu_actions.push((action.to_string(), arg));
state.menu_actions.push(action);
}
}
@@ -215,7 +220,7 @@ impl platform::ForegroundPlatform for MacForegroundPlatform {
}
}
fn on_menu_command(&self, callback: Box<dyn FnMut(&str, Option<&dyn Any>)>) {
fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn AnyAction)>) {
self.0.borrow_mut().menu_command = Some(callback);
}
@@ -465,7 +470,7 @@ impl platform::Platform for MacPlatform {
}
}
fn write_credentials(&self, url: &str, username: &str, password: &[u8]) {
fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> {
let url = CFString::from(url);
let username = CFString::from(username);
let password = CFData::from_buffer(password);
@@ -498,12 +503,13 @@ impl platform::Platform for MacPlatform {
}
if status != errSecSuccess {
panic!("{} password failed: {}", verb, status);
return Err(anyhow!("{} password failed: {}", verb, status));
}
}
Ok(())
}
fn read_credentials(&self, url: &str) -> Option<(String, Vec<u8>)> {
fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> {
let url = CFString::from(url);
let cf_true = CFBoolean::true_value().as_CFTypeRef();
@@ -521,27 +527,46 @@ impl platform::Platform for MacPlatform {
let status = SecItemCopyMatching(attrs.as_concrete_TypeRef(), &mut result);
match status {
security::errSecSuccess => {}
security::errSecItemNotFound | security::errSecUserCanceled => return None,
_ => panic!("reading password failed: {}", status),
security::errSecItemNotFound | security::errSecUserCanceled => return Ok(None),
_ => return Err(anyhow!("reading password failed: {}", status)),
}
let result = CFType::wrap_under_create_rule(result)
.downcast::<CFDictionary>()
.expect("keychain item was not a dictionary");
.ok_or_else(|| anyhow!("keychain item was not a dictionary"))?;
let username = result
.find(kSecAttrAccount as *const _)
.expect("account was missing from keychain item");
.ok_or_else(|| anyhow!("account was missing from keychain item"))?;
let username = CFType::wrap_under_get_rule(*username)
.downcast::<CFString>()
.expect("account was not a string");
.ok_or_else(|| anyhow!("account was not a string"))?;
let password = result
.find(kSecValueData as *const _)
.expect("password was missing from keychain item");
.ok_or_else(|| anyhow!("password was missing from keychain item"))?;
let password = CFType::wrap_under_get_rule(*password)
.downcast::<CFData>()
.expect("password was not a string");
.ok_or_else(|| anyhow!("password was not a string"))?;
Some((username.to_string(), password.bytes().to_vec()))
Ok(Some((username.to_string(), password.bytes().to_vec())))
}
}
fn set_cursor_style(&self, style: CursorStyle) {
unsafe {
let cursor: id = match style {
CursorStyle::Arrow => msg_send![class!(NSCursor), arrowCursor],
CursorStyle::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor],
CursorStyle::PointingHand => msg_send![class!(NSCursor), pointingHandCursor],
};
let _: () = msg_send![cursor, set];
}
}
fn local_timezone(&self) -> UtcOffset {
unsafe {
let local_timezone: id = msg_send![class!(NSTimeZone), localTimeZone];
let seconds_from_gmt: NSInteger = msg_send![local_timezone, secondsFromGMT];
UtcOffset::from_whole_seconds(seconds_from_gmt.try_into().unwrap()).unwrap()
}
}
}
@@ -623,8 +648,8 @@ extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) {
if let Some(mut callback) = platform.menu_command.take() {
let tag: NSInteger = msg_send![item, tag];
let index = tag as usize;
if let Some((action, arg)) = platform.menu_actions.get(index) {
callback(action, arg.as_ref().map(Box::as_ref));
if let Some(action) = platform.menu_actions.get(index) {
callback(action.as_ref());
}
platform.menu_command = Some(callback);
}
+60 -33
View File
@@ -6,7 +6,7 @@ use crate::{
vector::{vec2f, vec2i, Vector2F},
},
platform,
scene::Layer,
scene::{Glyph, Icon, Layer, Quad, Shadow},
Scene,
};
use cocoa::foundation::NSUInteger;
@@ -142,7 +142,7 @@ impl Renderer {
let mut sprites = Vec::new();
let mut vertices = Vec::<shaders::GPUIPathVertex>::new();
let mut current_atlas_id = None;
for (layer_id, layer) in scene.layers().iter().enumerate() {
for (layer_id, layer) in scene.layers().enumerate() {
for path in layer.paths() {
let origin = path.bounds.origin() * scene.scale_factor();
let size = (path.bounds.size() * scene.scale_factor()).ceil();
@@ -283,12 +283,24 @@ impl Renderer {
zfar: 1.0,
});
let scale_factor = scene.scale_factor();
let mut path_sprites = path_sprites.into_iter().peekable();
for (layer_id, layer) in scene.layers().iter().enumerate() {
for (layer_id, layer) in scene.layers().enumerate() {
self.clip(scene, layer, drawable_size, command_encoder);
self.render_shadows(scene, layer, offset, drawable_size, command_encoder);
self.render_quads(scene, layer, offset, drawable_size, command_encoder);
self.render_shadows(
layer.shadows(),
scale_factor,
offset,
drawable_size,
command_encoder,
);
self.render_quads(
layer.quads(),
scale_factor,
offset,
drawable_size,
command_encoder,
);
self.render_path_sprites(
layer_id,
&mut path_sprites,
@@ -296,7 +308,21 @@ impl Renderer {
drawable_size,
command_encoder,
);
self.render_sprites(scene, layer, offset, drawable_size, command_encoder);
self.render_sprites(
layer.glyphs(),
layer.icons(),
scale_factor,
offset,
drawable_size,
command_encoder,
);
self.render_quads(
layer.underlines(),
scale_factor,
offset,
drawable_size,
command_encoder,
);
}
command_encoder.end_encoding();
@@ -324,18 +350,18 @@ impl Renderer {
fn render_shadows(
&mut self,
scene: &Scene,
layer: &Layer,
shadows: &[Shadow],
scale_factor: f32,
offset: &mut usize,
drawable_size: Vector2F,
command_encoder: &metal::RenderCommandEncoderRef,
) {
if layer.shadows().is_empty() {
if shadows.is_empty() {
return;
}
align_offset(offset);
let next_offset = *offset + layer.shadows().len() * mem::size_of::<shaders::GPUIShadow>();
let next_offset = *offset + shadows.len() * mem::size_of::<shaders::GPUIShadow>();
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
@@ -365,12 +391,12 @@ impl Renderer {
(self.instances.contents() as *mut u8).offset(*offset as isize)
as *mut shaders::GPUIShadow
};
for (ix, shadow) in layer.shadows().iter().enumerate() {
let shape_bounds = shadow.bounds * scene.scale_factor();
for (ix, shadow) in shadows.iter().enumerate() {
let shape_bounds = shadow.bounds * scale_factor;
let shader_shadow = shaders::GPUIShadow {
origin: shape_bounds.origin().to_float2(),
size: shape_bounds.size().to_float2(),
corner_radius: shadow.corner_radius * scene.scale_factor(),
corner_radius: shadow.corner_radius * scale_factor,
sigma: shadow.sigma,
color: shadow.color.to_uchar4(),
};
@@ -383,24 +409,24 @@ impl Renderer {
metal::MTLPrimitiveType::Triangle,
0,
6,
layer.shadows().len() as u64,
shadows.len() as u64,
);
*offset = next_offset;
}
fn render_quads(
&mut self,
scene: &Scene,
layer: &Layer,
quads: &[Quad],
scale_factor: f32,
offset: &mut usize,
drawable_size: Vector2F,
command_encoder: &metal::RenderCommandEncoderRef,
) {
if layer.quads().is_empty() {
if quads.is_empty() {
return;
}
align_offset(offset);
let next_offset = *offset + layer.quads().len() * mem::size_of::<shaders::GPUIQuad>();
let next_offset = *offset + quads.len() * mem::size_of::<shaders::GPUIQuad>();
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
@@ -430,9 +456,9 @@ impl Renderer {
(self.instances.contents() as *mut u8).offset(*offset as isize)
as *mut shaders::GPUIQuad
};
for (ix, quad) in layer.quads().iter().enumerate() {
let bounds = quad.bounds * scene.scale_factor();
let border_width = quad.border.width * scene.scale_factor();
for (ix, quad) in quads.iter().enumerate() {
let bounds = quad.bounds * scale_factor;
let border_width = quad.border.width * scale_factor;
let shader_quad = shaders::GPUIQuad {
origin: bounds.origin().round().to_float2(),
size: bounds.size().round().to_float2(),
@@ -445,7 +471,7 @@ impl Renderer {
border_bottom: border_width * (quad.border.bottom as usize as f32),
border_left: border_width * (quad.border.left as usize as f32),
border_color: quad.border.color.to_uchar4(),
corner_radius: quad.corner_radius * scene.scale_factor(),
corner_radius: quad.corner_radius * scale_factor,
};
unsafe {
*(buffer_contents.offset(ix as isize)) = shader_quad;
@@ -456,35 +482,36 @@ impl Renderer {
metal::MTLPrimitiveType::Triangle,
0,
6,
layer.quads().len() as u64,
quads.len() as u64,
);
*offset = next_offset;
}
fn render_sprites(
&mut self,
scene: &Scene,
layer: &Layer,
glyphs: &[Glyph],
icons: &[Icon],
scale_factor: f32,
offset: &mut usize,
drawable_size: Vector2F,
command_encoder: &metal::RenderCommandEncoderRef,
) {
if layer.glyphs().is_empty() && layer.icons().is_empty() {
if glyphs.is_empty() && icons.is_empty() {
return;
}
let mut sprites_by_atlas = HashMap::new();
for glyph in layer.glyphs() {
for glyph in glyphs {
if let Some(sprite) = self.sprite_cache.render_glyph(
glyph.font_id,
glyph.font_size,
glyph.id,
glyph.origin,
scene.scale_factor(),
scale_factor,
) {
// Snap sprite to pixel grid.
let origin = (glyph.origin * scene.scale_factor()).floor() + sprite.offset.to_f32();
let origin = (glyph.origin * scale_factor).floor() + sprite.offset.to_f32();
sprites_by_atlas
.entry(sprite.atlas_id)
.or_insert_with(Vec::new)
@@ -499,9 +526,9 @@ impl Renderer {
}
}
for icon in layer.icons() {
let origin = icon.bounds.origin() * scene.scale_factor();
let target_size = icon.bounds.size() * scene.scale_factor();
for icon in icons {
let origin = icon.bounds.origin() * scale_factor;
let target_size = icon.bounds.size() * scale_factor;
let source_size = (target_size * 2.).ceil().to_i32();
let sprite =
+98 -18
View File
@@ -8,13 +8,14 @@ use crate::{
use block::ConcreteBlock;
use cocoa::{
appkit::{
NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
NSViewWidthSizable, NSWindow, NSWindowStyleMask,
CGPoint, NSApplication, NSBackingStoreBuffered, NSScreen, NSView, NSViewHeightSizable,
NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowStyleMask,
},
base::{id, nil},
foundation::{NSAutoreleasePool, NSInteger, NSSize, NSString},
quartzcore::AutoresizingMask,
};
use core_graphics::display::CGRect;
use ctor::ctor;
use foreign_types::ForeignType as _;
use objc::{
@@ -65,6 +66,10 @@ unsafe fn build_classes() {
sel!(sendEvent:),
send_event as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidResize:),
window_did_resize as extern "C" fn(&Object, Sel, id),
);
decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
decl.register()
};
@@ -129,7 +134,7 @@ struct WindowState {
id: usize,
native_window: id,
event_callback: Option<Box<dyn FnMut(Event)>>,
resize_callback: Option<Box<dyn FnMut(&mut dyn platform::WindowContext)>>,
resize_callback: Option<Box<dyn FnMut()>>,
close_callback: Option<Box<dyn FnOnce()>>,
synthetic_drag_counter: usize,
executor: Rc<executor::Foreground>,
@@ -138,6 +143,7 @@ struct WindowState {
command_queue: metal::CommandQueue,
last_fresh_keydown: Option<(Keystroke, String)>,
layer: id,
traffic_light_position: Option<Vector2F>,
}
impl Window {
@@ -153,11 +159,15 @@ impl Window {
let pool = NSAutoreleasePool::new(nil);
let frame = options.bounds.to_ns_rect();
let style_mask = NSWindowStyleMask::NSClosableWindowMask
let mut style_mask = NSWindowStyleMask::NSClosableWindowMask
| NSWindowStyleMask::NSMiniaturizableWindowMask
| NSWindowStyleMask::NSResizableWindowMask
| NSWindowStyleMask::NSTitledWindowMask;
if options.titlebar_appears_transparent {
style_mask |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
}
let native_window: id = msg_send![WINDOW_CLASS, alloc];
let native_window = native_window.initWithContentRect_styleMask_backing_defer_(
frame,
@@ -199,12 +209,14 @@ impl Window {
command_queue: device.new_command_queue(),
last_fresh_keydown: None,
layer,
traffic_light_position: options.traffic_light_position,
})));
(*native_window).set_ivar(
WINDOW_STATE_IVAR,
Rc::into_raw(window.0.clone()) as *const c_void,
);
native_window.setDelegate_(native_window);
(*native_view).set_ivar(
WINDOW_STATE_IVAR,
Rc::into_raw(window.0.clone()) as *const c_void,
@@ -213,6 +225,9 @@ impl Window {
if let Some(title) = options.title.as_ref() {
native_window.setTitle_(NSString::alloc(nil).init_str(title));
}
if options.titlebar_appears_transparent {
native_window.setTitlebarAppearsTransparent_(YES);
}
native_window.setAcceptsMouseMovedEvents_(YES);
native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
@@ -235,6 +250,7 @@ impl Window {
native_window.center();
native_window.makeKeyAndOrderFront_(nil);
window.0.borrow().move_traffic_light();
pool.drain();
window
@@ -272,7 +288,7 @@ impl platform::Window for Window {
self.0.as_ref().borrow_mut().event_callback = Some(callback);
}
fn on_resize(&mut self, callback: Box<dyn FnMut(&mut dyn platform::WindowContext)>) {
fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
self.0.as_ref().borrow_mut().resize_callback = Some(callback);
}
@@ -329,6 +345,56 @@ impl platform::WindowContext for Window {
fn present_scene(&mut self, scene: Scene) {
self.0.as_ref().borrow_mut().present_scene(scene);
}
fn titlebar_height(&self) -> f32 {
self.0.as_ref().borrow().titlebar_height()
}
}
impl WindowState {
fn move_traffic_light(&self) {
if let Some(traffic_light_position) = self.traffic_light_position {
let titlebar_height = self.titlebar_height();
unsafe {
let close_button: id = msg_send![
self.native_window,
standardWindowButton: NSWindowButton::NSWindowCloseButton
];
let min_button: id = msg_send![
self.native_window,
standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
];
let zoom_button: id = msg_send![
self.native_window,
standardWindowButton: NSWindowButton::NSWindowZoomButton
];
let mut close_button_frame: CGRect = msg_send![close_button, frame];
let mut min_button_frame: CGRect = msg_send![min_button, frame];
let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
let mut origin = vec2f(
traffic_light_position.x(),
titlebar_height
- traffic_light_position.y()
- close_button_frame.size.height as f32,
);
let button_spacing =
(min_button_frame.origin.x - close_button_frame.origin.x) as f32;
close_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
let _: () = msg_send![close_button, setFrame: close_button_frame];
origin.set_x(origin.x() + button_spacing);
min_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
let _: () = msg_send![min_button, setFrame: min_button_frame];
origin.set_x(origin.x() + button_spacing);
zoom_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
}
}
}
}
impl platform::WindowContext for WindowState {
@@ -345,6 +411,14 @@ impl platform::WindowContext for WindowState {
}
}
fn titlebar_height(&self) -> f32 {
unsafe {
let frame = NSWindow::frame(self.native_window);
let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
(frame.size.height - content_layout_rect.size.height) as f32
}
}
fn present_scene(&mut self, scene: Scene) {
self.scene_to_render = Some(scene);
unsafe {
@@ -442,6 +516,11 @@ extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
}
}
extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
let window_state = unsafe { get_window_state(this) };
window_state.as_ref().borrow().move_traffic_light();
}
extern "C" fn close_window(this: &Object, _: Sel) {
unsafe {
let close_callback = {
@@ -469,24 +548,24 @@ extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
let window_state = unsafe { get_window_state(this) };
let mut window_state = window_state.as_ref().borrow_mut();
let mut window_state_borrow = window_state.as_ref().borrow_mut();
unsafe {
let _: () =
msg_send![window_state.layer, setContentsScale: window_state.scale_factor() as f64];
let _: () = msg_send![window_state_borrow.layer, setContentsScale: window_state_borrow.scale_factor() as f64];
}
if let Some(mut callback) = window_state.resize_callback.take() {
callback(&mut *window_state);
window_state.resize_callback = Some(callback);
if let Some(mut callback) = window_state_borrow.resize_callback.take() {
drop(window_state_borrow);
callback();
window_state.as_ref().borrow_mut().resize_callback = Some(callback);
};
}
extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
let window_state = unsafe { get_window_state(this) };
let mut window_state = window_state.as_ref().borrow_mut();
let mut window_state_borrow = window_state.as_ref().borrow_mut();
if window_state.size() == vec2f(size.width as f32, size.height as f32) {
if window_state_borrow.size() == vec2f(size.width as f32, size.height as f32) {
return;
}
@@ -494,19 +573,20 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
}
let scale_factor = window_state.scale_factor() as f64;
let scale_factor = window_state_borrow.scale_factor() as f64;
let drawable_size: NSSize = NSSize {
width: size.width * scale_factor,
height: size.height * scale_factor,
};
unsafe {
let _: () = msg_send![window_state.layer, setDrawableSize: drawable_size];
let _: () = msg_send![window_state_borrow.layer, setDrawableSize: drawable_size];
}
if let Some(mut callback) = window_state.resize_callback.take() {
callback(&mut *window_state);
window_state.resize_callback = Some(callback);
if let Some(mut callback) = window_state_borrow.resize_callback.take() {
drop(window_state_borrow);
callback();
window_state.borrow_mut().resize_callback = Some(callback);
};
}
+26 -7
View File
@@ -1,4 +1,6 @@
use crate::ClipboardItem;
use super::CursorStyle;
use crate::{AnyAction, ClipboardItem};
use anyhow::Result;
use parking_lot::Mutex;
use pathfinder_geometry::vector::Vector2F;
use std::{
@@ -8,11 +10,13 @@ use std::{
rc::Rc,
sync::Arc,
};
use time::UtcOffset;
pub struct Platform {
dispatcher: Arc<dyn super::Dispatcher>,
fonts: Arc<dyn super::FontSystem>,
current_clipboard_item: Mutex<Option<ClipboardItem>>,
cursor: Mutex<CursorStyle>,
}
#[derive(Default)]
@@ -27,7 +31,7 @@ pub struct Window {
scale_factor: f32,
current_scene: Option<crate::Scene>,
event_handlers: Vec<Box<dyn FnMut(super::Event)>>,
resize_handlers: Vec<Box<dyn FnMut(&mut dyn super::WindowContext)>>,
resize_handlers: Vec<Box<dyn FnMut()>>,
close_handlers: Vec<Box<dyn FnOnce()>>,
pub(crate) last_prompt: RefCell<Option<Box<dyn FnOnce(usize)>>>,
}
@@ -62,7 +66,7 @@ impl super::ForegroundPlatform for ForegroundPlatform {
unimplemented!()
}
fn on_menu_command(&self, _: Box<dyn FnMut(&str, Option<&dyn Any>)>) {}
fn on_menu_command(&self, _: Box<dyn FnMut(&dyn AnyAction)>) {}
fn set_menus(&self, _: Vec<crate::Menu>) {}
@@ -84,6 +88,7 @@ impl Platform {
dispatcher: Arc::new(Dispatcher),
fonts: Arc::new(super::current::FontSystem::new()),
current_clipboard_item: Default::default(),
cursor: Mutex::new(CursorStyle::Arrow),
}
}
}
@@ -124,10 +129,20 @@ impl super::Platform for Platform {
fn open_url(&self, _: &str) {}
fn write_credentials(&self, _: &str, _: &str, _: &[u8]) {}
fn write_credentials(&self, _: &str, _: &str, _: &[u8]) -> Result<()> {
Ok(())
}
fn read_credentials(&self, _: &str) -> Option<(String, Vec<u8>)> {
None
fn read_credentials(&self, _: &str) -> Result<Option<(String, Vec<u8>)>> {
Ok(None)
}
fn set_cursor_style(&self, style: CursorStyle) {
*self.cursor.lock() = style;
}
fn local_timezone(&self) -> UtcOffset {
UtcOffset::UTC
}
}
@@ -164,6 +179,10 @@ impl super::WindowContext for Window {
self.scale_factor
}
fn titlebar_height(&self) -> f32 {
24.
}
fn present_scene(&mut self, scene: crate::Scene) {
self.current_scene = Some(scene);
}
@@ -178,7 +197,7 @@ impl super::Window for Window {
self.event_handlers.push(callback);
}
fn on_resize(&mut self, callback: Box<dyn FnMut(&mut dyn super::WindowContext)>) {
fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
self.resize_handlers.push(callback);
}