From 91a36ddc2227d60b555e69bc37d6827544a537e1 Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Sat, 1 Jan 2022 16:34:19 -0800 Subject: [PATCH 01/13] macos dependencies are also ios dependencies --- druid-shell/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/druid-shell/Cargo.toml b/druid-shell/Cargo.toml index 79a795b60d..f5116bd1b2 100755 --- a/druid-shell/Cargo.toml +++ b/druid-shell/Cargo.toml @@ -67,7 +67,7 @@ features = ["d2d1_1", "dwrite", "winbase", "libloaderapi", "errhandlingapi", "wi "d3d11", "dwmapi", "wincon", "fileapi", "processenv", "winbase", "handleapi", "shellapi", "winnls"] -[target.'cfg(target_os="macos")'.dependencies] +[target.'cfg(any(target_os="macos", target-os="ios"))'.dependencies] block = "0.1.6" cocoa = "0.24.0" objc = "0.2.7" From ce3ee113ec6435b8da697f424e75eddb5f75c74b Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Sat, 1 Jan 2022 16:35:12 -0800 Subject: [PATCH 02/13] only have a single noop menu implementation --- druid-shell/src/backend/shared/mod.rs | 2 + druid-shell/src/backend/shared/noop_menu.rs | 50 +++++++++++++++++++++ druid-shell/src/backend/web/menu.rs | 43 +----------------- druid-shell/src/backend/x11/menu.rs | 43 +----------------- 4 files changed, 55 insertions(+), 83 deletions(-) create mode 100644 druid-shell/src/backend/shared/noop_menu.rs diff --git a/druid-shell/src/backend/shared/mod.rs b/druid-shell/src/backend/shared/mod.rs index da805c19ab..1e9a33045f 100644 --- a/druid-shell/src/backend/shared/mod.rs +++ b/druid-shell/src/backend/shared/mod.rs @@ -20,3 +20,5 @@ cfg_if::cfg_if! { pub use keyboard::*; } } + +pub(crate) mod noop_menu; diff --git a/druid-shell/src/backend/shared/noop_menu.rs b/druid-shell/src/backend/shared/noop_menu.rs new file mode 100644 index 0000000000..0f3156c870 --- /dev/null +++ b/druid-shell/src/backend/shared/noop_menu.rs @@ -0,0 +1,50 @@ +// Copyright 2020 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! X11 menus implementation. + +use crate::hotkey::HotKey; + +pub struct Menu; + +impl Menu { + pub fn new() -> Menu { + tracing::warn!("Menu::new is currently unimplemented for this backend."); + Menu {} + } + + pub fn new_for_popup() -> Menu { + tracing::warn!("Menu::new_for_popup is currently unimplemented for this backend."); + Menu {} + } + + pub fn add_dropdown(&mut self, mut _menu: Menu, _text: &str, _enabled: bool) { + tracing::warn!("Menu::add_dropdown is currently unimplemented for this backend."); + } + + pub fn add_item( + &mut self, + _id: u32, + _text: &str, + _key: Option<&HotKey>, + _enabled: bool, + _selected: bool, + ) { + tracing::warn!("Menu::add_item is currently unimplemented for this backend."); + } + + pub fn add_separator(&mut self) { + tracing::warn!("Menu::add_separator is currently unimplemented for this backend."); + } +} diff --git a/druid-shell/src/backend/web/menu.rs b/druid-shell/src/backend/web/menu.rs index 4d37b85c6e..16d9f141da 100644 --- a/druid-shell/src/backend/web/menu.rs +++ b/druid-shell/src/backend/web/menu.rs @@ -12,45 +12,4 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Safe wrapper for menus. - -use crate::hotkey::HotKey; - -/// A menu object, which can be either a top-level menubar or a -/// submenu. -pub struct Menu; - -impl Drop for Menu { - fn drop(&mut self) { - // TODO - } -} - -impl Menu { - pub fn new() -> Menu { - Menu - } - - pub fn new_for_popup() -> Menu { - Menu - } - - pub fn add_dropdown(&mut self, _menu: Menu, _text: &str, _enabled: bool) { - tracing::warn!("unimplemented"); - } - - pub fn add_item( - &mut self, - _id: u32, - _text: &str, - _key: Option<&HotKey>, - _enabled: bool, - _selected: bool, - ) { - tracing::warn!("unimplemented"); - } - - pub fn add_separator(&mut self) { - tracing::warn!("unimplemented"); - } -} +pub use super::shared::noop_menu::*; diff --git a/druid-shell/src/backend/x11/menu.rs b/druid-shell/src/backend/x11/menu.rs index 70e570cbb7..dadd585c05 100644 --- a/druid-shell/src/backend/x11/menu.rs +++ b/druid-shell/src/backend/x11/menu.rs @@ -12,44 +12,5 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! X11 menus implementation. - -use crate::hotkey::HotKey; - -pub struct Menu; - -impl Menu { - pub fn new() -> Menu { - // TODO(x11/menus): implement Menu::new (currently a no-op) - tracing::warn!("Menu::new is currently unimplemented for X11 backend."); - Menu {} - } - - pub fn new_for_popup() -> Menu { - // TODO(x11/menus): implement Menu::new_for_popup (currently a no-op) - tracing::warn!("Menu::new_for_popup is currently unimplemented for X11 backend."); - Menu {} - } - - pub fn add_dropdown(&mut self, mut _menu: Menu, _text: &str, _enabled: bool) { - // TODO(x11/menus): implement Menu::add_dropdown (currently a no-op) - tracing::warn!("Menu::add_dropdown is currently unimplemented for X11 backend."); - } - - pub fn add_item( - &mut self, - _id: u32, - _text: &str, - _key: Option<&HotKey>, - _enabled: bool, - _selected: bool, - ) { - // TODO(x11/menus): implement Menu::add_item (currently a no-op) - tracing::warn!("Menu::add_item is currently unimplemented for X11 backend."); - } - - pub fn add_separator(&mut self) { - // TODO(x11/menus): implement Menu::add_separator (currently a no-op) - tracing::warn!("Menu::add_separator is currently unimplemented for X11 backend."); - } -} +// TODO(x11/menus): implement menus (currently a no-op) +pub use super::shared::noop_menu::*; From 57504dc2f4525055d662ddcc64f5b538f061d3e1 Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Sat, 1 Jan 2022 16:36:06 -0800 Subject: [PATCH 03/13] fix noop_menu desc --- druid-shell/src/backend/shared/noop_menu.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/druid-shell/src/backend/shared/noop_menu.rs b/druid-shell/src/backend/shared/noop_menu.rs index 0f3156c870..5e39ec6c86 100644 --- a/druid-shell/src/backend/shared/noop_menu.rs +++ b/druid-shell/src/backend/shared/noop_menu.rs @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! X11 menus implementation. +//! Implementation of menu that does nothing, for platforms that either don't have a menu available, or +//! just don't have an implementation in druid-shell yet. use crate::hotkey::HotKey; From 69fb929ce67c1216753743c23bde33e8b5ca93c6 Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Sat, 1 Jan 2022 17:10:03 -0800 Subject: [PATCH 04/13] fix import paths, move mac util functions into a common module shared between macos and ios --- druid-shell/Cargo.toml | 6 +++--- druid-shell/src/backend/mac/mod.rs | 4 +++- druid-shell/src/backend/mod.rs | 7 +++++++ .../src/backend/{mac/util.rs => shared/cocoa_util.rs} | 0 druid-shell/src/backend/shared/mod.rs | 5 +++++ druid-shell/src/backend/web/menu.rs | 2 +- druid-shell/src/backend/x11/menu.rs | 2 +- 7 files changed, 20 insertions(+), 6 deletions(-) rename druid-shell/src/backend/{mac/util.rs => shared/cocoa_util.rs} (100%) diff --git a/druid-shell/Cargo.toml b/druid-shell/Cargo.toml index f5116bd1b2..001da69cf9 100755 --- a/druid-shell/Cargo.toml +++ b/druid-shell/Cargo.toml @@ -41,7 +41,7 @@ serde = ["kurbo/serde"] [dependencies] # NOTE: When changing the piet or kurbo versions, ensure that # the kurbo version included in piet is compatible with the kurbo version specified here. -piet-common = "=0.5.0" +piet-common = {path = "../../piet/piet-common"} kurbo = "0.8.2" tracing = "0.1.22" @@ -67,7 +67,7 @@ features = ["d2d1_1", "dwrite", "winbase", "libloaderapi", "errhandlingapi", "wi "d3d11", "dwmapi", "wincon", "fileapi", "processenv", "winbase", "handleapi", "shellapi", "winnls"] -[target.'cfg(any(target_os="macos", target-os="ios"))'.dependencies] +[target.'cfg(any(target_os="macos", target_os="ios"))'.dependencies] block = "0.1.6" cocoa = "0.24.0" objc = "0.2.7" @@ -96,7 +96,7 @@ version = "0.3.44" features = ["Window", "MouseEvent", "CssStyleDeclaration", "WheelEvent", "KeyEvent", "KeyboardEvent", "Navigator"] [dev-dependencies] -piet-common = { version = "=0.5.0", features = ["png"] } +piet-common = { path = "../../piet/piet-common", features = ["png"] } static_assertions = "1.1.0" test-log = { version = "0.2.5", features = ["trace"], default-features = false } tracing-subscriber = { version = "0.3.2", features = ["env-filter"] } diff --git a/druid-shell/src/backend/mac/mod.rs b/druid-shell/src/backend/mac/mod.rs index c84d879cf8..08df0d6406 100644 --- a/druid-shell/src/backend/mac/mod.rs +++ b/druid-shell/src/backend/mac/mod.rs @@ -25,5 +25,7 @@ mod keyboard; pub mod menu; pub mod screen; pub mod text_input; -pub mod util; pub mod window; +pub mod util { + pub(crate) use crate::backend::shared::cocoa_util::*; +} diff --git a/druid-shell/src/backend/mod.rs b/druid-shell/src/backend/mod.rs index f07150398d..581dedc990 100644 --- a/druid-shell/src/backend/mod.rs +++ b/druid-shell/src/backend/mod.rs @@ -28,6 +28,13 @@ pub use mac::*; #[cfg(target_os = "macos")] pub(crate) mod shared; +#[cfg(target_os = "ios")] +mod ios; +#[cfg(target_os = "ios")] +pub use ios::*; +#[cfg(target_os = "ios")] +pub(crate) mod shared; + #[cfg(all(feature = "x11", any(target_os = "linux", target_os = "openbsd")))] mod x11; #[cfg(all(feature = "x11", any(target_os = "linux", target_os = "openbsd")))] diff --git a/druid-shell/src/backend/mac/util.rs b/druid-shell/src/backend/shared/cocoa_util.rs similarity index 100% rename from druid-shell/src/backend/mac/util.rs rename to druid-shell/src/backend/shared/cocoa_util.rs diff --git a/druid-shell/src/backend/shared/mod.rs b/druid-shell/src/backend/shared/mod.rs index 1e9a33045f..b59f3dc62f 100644 --- a/druid-shell/src/backend/shared/mod.rs +++ b/druid-shell/src/backend/shared/mod.rs @@ -20,5 +20,10 @@ cfg_if::cfg_if! { pub use keyboard::*; } } +cfg_if::cfg_if! { + if #[cfg(any(target_os = "macos", target_os = "ios"))] { + pub(crate) mod cocoa_util; + } +} pub(crate) mod noop_menu; diff --git a/druid-shell/src/backend/web/menu.rs b/druid-shell/src/backend/web/menu.rs index 16d9f141da..a9594cb88c 100644 --- a/druid-shell/src/backend/web/menu.rs +++ b/druid-shell/src/backend/web/menu.rs @@ -12,4 +12,4 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub use super::shared::noop_menu::*; +pub use crate::backend::shared::noop_menu::*; diff --git a/druid-shell/src/backend/x11/menu.rs b/druid-shell/src/backend/x11/menu.rs index dadd585c05..87b4e563a4 100644 --- a/druid-shell/src/backend/x11/menu.rs +++ b/druid-shell/src/backend/x11/menu.rs @@ -13,4 +13,4 @@ // limitations under the License. // TODO(x11/menus): implement menus (currently a no-op) -pub use super::shared::noop_menu::*; +pub use crate::backend::shared::noop_menu::*; From ab8bb137ff2424545752c01ef36e44467b8c8db4 Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Sat, 1 Jan 2022 17:56:26 -0800 Subject: [PATCH 05/13] initial rough module structure of ios backend copied mostly from mac and web --- druid-shell/src/backend/ios/application.rs | 42 + druid-shell/src/backend/ios/clipboard.rs | 60 + druid-shell/src/backend/ios/error.rs | 26 + druid-shell/src/backend/ios/menu.rs | 15 + druid-shell/src/backend/ios/mod.rs | 24 + druid-shell/src/backend/ios/screen.rs | 26 + druid-shell/src/backend/ios/window.rs | 1469 ++++++++++++++++++++ 7 files changed, 1662 insertions(+) create mode 100644 druid-shell/src/backend/ios/application.rs create mode 100644 druid-shell/src/backend/ios/clipboard.rs create mode 100644 druid-shell/src/backend/ios/error.rs create mode 100644 druid-shell/src/backend/ios/menu.rs create mode 100644 druid-shell/src/backend/ios/mod.rs create mode 100644 druid-shell/src/backend/ios/screen.rs create mode 100644 druid-shell/src/backend/ios/window.rs diff --git a/druid-shell/src/backend/ios/application.rs b/druid-shell/src/backend/ios/application.rs new file mode 100644 index 0000000000..8767f37297 --- /dev/null +++ b/druid-shell/src/backend/ios/application.rs @@ -0,0 +1,42 @@ +// Copyright 2022 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! iOS implementation of features at the application scope. + +use crate::application::AppHandler; + +use super::clipboard::Clipboard; +use super::error::Error; + +#[derive(Clone)] +pub(crate) struct Application; + +impl Application { + pub fn new() -> Result { + Ok(Application) + } + + pub fn run(self, _handler: Option>) {} + + pub fn quit(&self) {} + + pub fn clipboard(&self) -> Clipboard { + Clipboard + } + + pub fn get_locale() -> String { + tracing::warn!("unimplemented"); + "".to_string() + } +} diff --git a/druid-shell/src/backend/ios/clipboard.rs b/druid-shell/src/backend/ios/clipboard.rs new file mode 100644 index 0000000000..edef010a76 --- /dev/null +++ b/druid-shell/src/backend/ios/clipboard.rs @@ -0,0 +1,60 @@ +// Copyright 2022 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Interactions with the system pasteboard on iOS. + +use crate::clipboard::{ClipboardFormat, FormatId}; + +/// The browser clipboard. +#[derive(Debug, Clone, Default)] +pub struct Clipboard; + +impl Clipboard { + /// Put a string onto the system clipboard. + pub fn put_string(&mut self, _s: impl AsRef) { + tracing::warn!("unimplemented"); + } + + /// Put multi-format data on the system clipboard. + pub fn put_formats(&mut self, _formats: &[ClipboardFormat]) { + tracing::warn!("unimplemented"); + } + + /// Get a string from the system clipboard, if one is available. + pub fn get_string(&self) -> Option { + tracing::warn!("unimplemented"); + None + } + + /// Given a list of supported clipboard types, returns the supported type which has + /// highest priority on the system clipboard, or `None` if no types are supported. + pub fn preferred_format(&self, _formats: &[FormatId]) -> Option { + tracing::warn!("unimplemented"); + None + } + + /// Return data in a given format, if available. + /// + /// It is recommended that the `fmt` argument be a format returned by + /// [`Clipboard::preferred_format`] + pub fn get_format(&self, _format: FormatId) -> Option> { + tracing::warn!("unimplemented"); + None + } + + pub fn available_type_names(&self) -> Vec { + tracing::warn!("unimplemented"); + Vec::new() + } +} diff --git a/druid-shell/src/backend/ios/error.rs b/druid-shell/src/backend/ios/error.rs new file mode 100644 index 0000000000..537c201d12 --- /dev/null +++ b/druid-shell/src/backend/ios/error.rs @@ -0,0 +1,26 @@ +// Copyright 2022 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! iOS backend errors. + +#[derive(Debug, Clone)] +pub struct Error; + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "iOS Platform Error") + } +} + +impl std::error::Error for Error {} diff --git a/druid-shell/src/backend/ios/menu.rs b/druid-shell/src/backend/ios/menu.rs new file mode 100644 index 0000000000..2feefec06c --- /dev/null +++ b/druid-shell/src/backend/ios/menu.rs @@ -0,0 +1,15 @@ +// Copyright 2022 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use crate::backend::shared::noop_menu::*; diff --git a/druid-shell/src/backend/ios/mod.rs b/druid-shell/src/backend/ios/mod.rs new file mode 100644 index 0000000000..f942592a91 --- /dev/null +++ b/druid-shell/src/backend/ios/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2022 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! macOS druid-shell backend. + +#![allow(clippy::let_unit_value)] + +pub mod application; +pub mod clipboard; +pub mod error; +pub mod menu; +pub mod screen; +pub mod window; diff --git a/druid-shell/src/backend/ios/screen.rs b/druid-shell/src/backend/ios/screen.rs new file mode 100644 index 0000000000..6b40cedb8e --- /dev/null +++ b/druid-shell/src/backend/ios/screen.rs @@ -0,0 +1,26 @@ +// Copyright 2022 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! macOS Monitors and Screen information. + +use crate::kurbo::Rect; +use crate::screen::Monitor; +use cocoa::base::id; +use cocoa::foundation::NSArray; +use objc::{class, msg_send, sel, sel_impl}; + +pub(crate) fn get_monitors() -> Vec { + tracing::warn!("unimplemented"); + vec![] +} diff --git a/druid-shell/src/backend/ios/window.rs b/druid-shell/src/backend/ios/window.rs new file mode 100644 index 0000000000..e6e1f970f7 --- /dev/null +++ b/druid-shell/src/backend/ios/window.rs @@ -0,0 +1,1469 @@ +// Copyright 2022 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! macOS implementation of window creation. + +#![allow(non_snake_case)] + +// use std::ffi::c_void; +// use std::mem; +use std::sync::{Arc, Mutex, Weak}; +// use std::time::Instant; + +// use block::ConcreteBlock; +// use cocoa::base::{id, nil, BOOL, NO, YES}; +// use cocoa::foundation::{ +// NSArray, NSAutoreleasePool, NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger, +// }; +// use core_graphics::context::CGContextRef; +// use foreign_types::ForeignTypeRef; +// use lazy_static::lazy_static; +// use objc::declare::ClassDecl; +use objc::rc::WeakPtr; +// use objc::runtime::{Class, Object, Protocol, Sel}; +// use objc::{class, msg_send, sel, sel_impl}; +// use tracing::{debug, error, info}; + +// #[cfg(feature = "raw-win-handle")] +// use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle}; + +// use crate::kurbo::{Insets, Point, Rect, Size, Vec2}; +// use crate::piet::{Piet, PietText, RenderContext}; + +// use super::application::Application; +// use super::dialog; +// use super::keyboard::{make_modifiers, KeyboardState}; +// use super::menu::Menu; +// use super::text_input::NSRange; +// use super::util::{assert_main_thread, make_nsstring}; +// use crate::common_util::IdleCallback; +// use crate::dialog::{FileDialogOptions, FileDialogType}; +// use crate::keyboard_types::KeyState; +// use crate::mouse::{Cursor, CursorDesc, MouseButton, MouseButtons, MouseEvent}; +// use crate::region::Region; +// use crate::scale::Scale; +// use crate::text::{Event, InputHandler}; +// use crate::window::{ +// FileDialogToken, IdleToken, TextFieldToken, TimerToken, WinHandler, WindowLevel, WindowState, +// }; +// use crate::Error; + +// #[allow(non_upper_case_globals)] +// const NSWindowDidBecomeKeyNotification: &str = "NSWindowDidBecomeKeyNotification"; + +// #[allow(dead_code)] +// #[allow(non_upper_case_globals)] +// mod levels { +// use crate::window::WindowLevel; + +// // These are the levels that AppKit seems to have. +// pub const NSModalPanelLevel: i32 = 24; +// pub const NSNormalWindowLevel: i32 = 0; +// pub const NSFloatingWindowLevel: i32 = 3; +// pub const NSTornOffMenuWindowLevel: i32 = NSFloatingWindowLevel; +// pub const NSSubmenuWindowLevel: i32 = NSFloatingWindowLevel; +// pub const NSModalPanelWindowLevel: i32 = 8; +// pub const NSStatusWindowLevel: i32 = 25; +// pub const NSPopUpMenuWindowLevel: i32 = 101; +// pub const NSScreenSaverWindowLevel: i32 = 1000; + +// pub fn as_raw_window_level(window_level: WindowLevel) -> i32 { +// use WindowLevel::*; +// match window_level { +// AppWindow => NSNormalWindowLevel, +// Tooltip(_) => NSFloatingWindowLevel, +// DropDown(_) => NSFloatingWindowLevel, +// Modal(_) => NSModalPanelWindowLevel, +// } +// } +// } + +// #[derive(Clone)] +pub(crate) struct WindowHandle { + /// This is an NSView, as our concept of "window" is more the top-level container holding + /// a view. Also, this is better for hosted applications such as VST. + nsview: WeakPtr, + idle_queue: Weak>>, +} +// impl PartialEq for WindowHandle { +// fn eq(&self, other: &Self) -> bool { +// match (self.idle_queue.upgrade(), other.idle_queue.upgrade()) { +// (None, None) => true, +// (Some(s), Some(o)) => std::sync::Arc::ptr_eq(&s, &o), +// (_, _) => false, +// } +// } +// } +// impl Eq for WindowHandle {} + +// impl std::fmt::Debug for WindowHandle { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { +// f.write_str("WindowHandle{\n")?; +// f.write_str("}")?; +// Ok(()) +// } +// } + +// impl Default for WindowHandle { +// fn default() -> Self { +// WindowHandle { +// nsview: unsafe { WeakPtr::new(nil) }, +// idle_queue: Default::default(), +// } +// } +// } + +// /// Builder abstraction for creating new windows. +// pub(crate) struct WindowBuilder { +// handler: Option>, +// title: String, +// menu: Option, +// size: Size, +// min_size: Option, +// position: Option, +// level: Option, +// window_state: Option, +// resizable: bool, +// show_titlebar: bool, +// transparent: bool, +// } + +#[derive(Clone)] +pub(crate) struct IdleHandle { + nsview: WeakPtr, + idle_queue: Weak>>, +} + +#[derive(Debug)] +enum DeferredOp { + // SetSize(Size), +// SetPosition(Point), +} + +/// This represents different Idle Callback Mechanism +enum IdleKind { + // Callback(Box), +// Token(IdleToken), +// DeferredOp(DeferredOp), +} + +// /// This is the state associated with our custom NSView. +// struct ViewState { +// nsview: WeakPtr, +// handler: Box, +// idle_queue: Arc>>, +// /// Tracks window focusing left clicks +// focus_click: bool, +// // Tracks whether we have already received the mouseExited event +// mouse_left: bool, +// keyboard_state: KeyboardState, +// text: PietText, +// active_text_input: Option, +// parent: Option, +// } + +#[derive(Clone, PartialEq)] +// TODO: support custom cursors on iPadOS +pub struct CustomCursor; + +// impl WindowBuilder { +// pub fn new(_app: Application) -> WindowBuilder { +// WindowBuilder { +// handler: None, +// title: String::new(), +// menu: None, +// size: Size::new(500., 400.), +// min_size: None, +// position: None, +// level: None, +// window_state: None, +// resizable: true, +// show_titlebar: true, +// transparent: false, +// } +// } + +// pub fn set_handler(&mut self, handler: Box) { +// self.handler = Some(handler); +// } + +// pub fn set_size(&mut self, size: Size) { +// self.size = size; +// } + +// pub fn set_min_size(&mut self, size: Size) { +// self.min_size = Some(size); +// } + +// pub fn resizable(&mut self, resizable: bool) { +// self.resizable = resizable; +// } + +// pub fn show_titlebar(&mut self, show_titlebar: bool) { +// self.show_titlebar = show_titlebar; +// } + +// pub fn set_transparent(&mut self, transparent: bool) { +// self.transparent = transparent; +// } + +// pub fn set_level(&mut self, level: WindowLevel) { +// self.level = Some(level); +// } + +// pub fn set_position(&mut self, position: Point) { +// self.position = Some(position) +// } + +// pub fn set_window_state(&mut self, state: WindowState) { +// self.window_state = Some(state); +// } + +// pub fn set_title(&mut self, title: impl Into) { +// self.title = title.into(); +// } + +// pub fn set_menu(&mut self, menu: Menu) { +// self.menu = Some(menu); +// } + +// pub fn build(self) -> Result { +// assert_main_thread(); +// unsafe { +// let mut style_mask = NSWindowStyleMask::NSClosableWindowMask +// | NSWindowStyleMask::NSMiniaturizableWindowMask; + +// if self.show_titlebar { +// style_mask |= NSWindowStyleMask::NSTitledWindowMask; +// } + +// if self.resizable { +// style_mask |= NSWindowStyleMask::NSResizableWindowMask; +// } + +// let screen_height = crate::Screen::get_display_rect().height(); +// let position = self.position.unwrap_or_else(|| Point::new(20., 20.)); +// let origin = NSPoint::new(position.x, screen_height - position.y - self.size.height); // Flip back + +// let rect = NSRect::new(origin, NSSize::new(self.size.width, self.size.height)); + +// let window: id = msg_send![WINDOW_CLASS.0, alloc]; +// let window = window.initWithContentRect_styleMask_backing_defer_( +// rect, +// style_mask, +// NSBackingStoreBuffered, +// NO, +// ); + +// if let Some(min_size) = self.min_size { +// let size = NSSize::new(min_size.width, min_size.height); +// window.setContentMinSize_(size); +// } + +// if self.transparent { +// window.setOpaque_(NO); +// window.setBackgroundColor_(NSColor::clearColor(nil)); +// } + +// window.setTitle_(make_nsstring(&self.title)); + +// let (view, idle_queue) = make_view(self.handler.expect("view")); +// let content_view = window.contentView(); +// let frame = NSView::frame(content_view); +// view.initWithFrame_(frame); + +// let () = msg_send![window, setDelegate: view]; + +// if let Some(menu) = self.menu { +// NSApp().setMainMenu_(menu.menu); +// } + +// content_view.addSubview_(view); +// let view_state: *mut c_void = *(*view).get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// let mut handle = WindowHandle { +// nsview: view_state.nsview.clone(), +// idle_queue, +// }; + +// if let Some(window_state) = self.window_state { +// handle.set_window_state(window_state); +// } + +// if let Some(level) = self.level { +// match &level { +// WindowLevel::Tooltip(parent) => (*view_state).parent = Some(parent.clone()), +// WindowLevel::DropDown(parent) => (*view_state).parent = Some(parent.clone()), +// WindowLevel::Modal(parent) => (*view_state).parent = Some(parent.clone()), +// _ => {} +// } +// handle.set_level(level); +// } + +// // set_window_state above could have invalidated the frame size +// let frame = NSView::frame(content_view); + +// (*view_state).handler.connect(&handle.clone().into()); +// (*view_state).handler.scale(Scale::default()); +// (*view_state) +// .handler +// .size(Size::new(frame.size.width, frame.size.height)); + +// Ok(handle) +// } +// } +// } + +// // Wrap pointer because lazy_static requires Sync. +// struct ViewClass(*const Class); +// unsafe impl Sync for ViewClass {} + +// lazy_static! { +// static ref VIEW_CLASS: ViewClass = unsafe { +// let mut decl = ClassDecl::new("DruidView", class!(NSView)).expect("View class defined"); +// decl.add_ivar::<*mut c_void>("viewState"); + +// decl.add_method( +// sel!(isFlipped), +// isFlipped as extern "C" fn(&Object, Sel) -> BOOL, +// ); +// extern "C" fn isFlipped(_this: &Object, _sel: Sel) -> BOOL { +// YES +// } +// decl.add_method( +// sel!(acceptsFirstResponder), +// acceptsFirstResponder as extern "C" fn(&Object, Sel) -> BOOL, +// ); +// extern "C" fn acceptsFirstResponder(_this: &Object, _sel: Sel) -> BOOL { +// YES +// } +// // acceptsFirstMouse is called when a left mouse click would focus the window +// decl.add_method( +// sel!(acceptsFirstMouse:), +// acceptsFirstMouse as extern "C" fn(&Object, Sel, id) -> BOOL, +// ); +// extern "C" fn acceptsFirstMouse(this: &Object, _sel: Sel, _nsevent: id) -> BOOL { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// view_state.focus_click = true; +// } +// YES +// } +// decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel)); +// extern "C" fn dealloc(this: &Object, _sel: Sel) { +// info!("view is dealloc'ed"); +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// Box::from_raw(view_state as *mut ViewState); +// } +// } + +// decl.add_method( +// sel!(windowDidBecomeKey:), +// window_did_become_key as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(windowDidResignKey:), +// window_did_resign_key as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(setFrameSize:), +// set_frame_size as extern "C" fn(&mut Object, Sel, NSSize), +// ); +// decl.add_method( +// sel!(mouseDown:), +// mouse_down_left as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(rightMouseDown:), +// mouse_down_right as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(otherMouseDown:), +// mouse_down_other as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(mouseUp:), +// mouse_up_left as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(rightMouseUp:), +// mouse_up_right as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(otherMouseUp:), +// mouse_up_other as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(mouseMoved:), +// mouse_move as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(mouseDragged:), +// mouse_move as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(otherMouseDragged:), +// mouse_move as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(mouseEntered:), +// mouse_enter as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(mouseExited:), +// mouse_leave as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(scrollWheel:), +// scroll_wheel as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(magnifyWithEvent:), +// pinch_event as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(keyDown:), +// key_down as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method(sel!(keyUp:), key_up as extern "C" fn(&mut Object, Sel, id)); +// decl.add_method( +// sel!(flagsChanged:), +// mods_changed as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(drawRect:), +// draw_rect as extern "C" fn(&mut Object, Sel, NSRect), +// ); +// decl.add_method(sel!(runIdle), run_idle as extern "C" fn(&mut Object, Sel)); +// decl.add_method(sel!(viewWillDraw), view_will_draw as extern "C" fn(&mut Object, Sel)); +// decl.add_method(sel!(redraw), redraw as extern "C" fn(&mut Object, Sel)); +// decl.add_method( +// sel!(handleTimer:), +// handle_timer as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(handleMenuItem:), +// handle_menu_item as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(showContextMenu:), +// show_context_menu as extern "C" fn(&mut Object, Sel, id), +// ); +// decl.add_method( +// sel!(windowShouldClose:), +// window_should_close as extern "C" fn(&mut Object, Sel, id)->BOOL, +// ); +// decl.add_method( +// sel!(windowWillClose:), +// window_will_close as extern "C" fn(&mut Object, Sel, id), +// ); + +// // methods for NSTextInputClient +// decl.add_method(sel!(hasMarkedText), super::text_input::has_marked_text as extern fn(&mut Object, Sel) -> BOOL); +// decl.add_method( +// sel!(markedRange), +// super::text_input::marked_range as extern fn(&mut Object, Sel) -> NSRange, +// ); +// decl.add_method(sel!(selectedRange), super::text_input::selected_range as extern fn(&mut Object, Sel) -> NSRange); +// decl.add_method( +// sel!(setMarkedText:selectedRange:replacementRange:), +// super::text_input::set_marked_text as extern fn(&mut Object, Sel, id, NSRange, NSRange), +// ); +// decl.add_method(sel!(unmarkText), super::text_input::unmark_text as extern fn(&mut Object, Sel)); +// decl.add_method( +// sel!(validAttributesForMarkedText), +// super::text_input::valid_attributes_for_marked_text as extern fn(&mut Object, Sel) -> id, +// ); +// decl.add_method( +// sel!(attributedSubstringForProposedRange:actualRange:), +// super::text_input::attributed_substring_for_proposed_range +// as extern fn(&mut Object, Sel, NSRange, *mut c_void) -> id, +// ); +// decl.add_method( +// sel!(insertText:replacementRange:), +// super::text_input::insert_text as extern fn(&mut Object, Sel, id, NSRange), +// ); +// decl.add_method( +// sel!(characterIndexForPoint:), +// super::text_input::character_index_for_point as extern fn(&mut Object, Sel, NSPoint) -> NSUInteger, +// ); +// decl.add_method( +// sel!(firstRectForCharacterRange:actualRange:), +// super::text_input::first_rect_for_character_range +// as extern fn(&mut Object, Sel, NSRange, *mut c_void) -> NSRect, +// ); +// decl.add_method( +// sel!(doCommandBySelector:), +// super::text_input::do_command_by_selector as extern fn(&mut Object, Sel, Sel), +// ); + +// let protocol = Protocol::get("NSTextInputClient").unwrap(); +// decl.add_protocol(protocol); + +// ViewClass(decl.register()) +// }; +// } + +// /// Acquires a lock to an `InputHandler`, passes it to a closure, and releases the lock. +// pub(super) fn with_edit_lock_from_window( +// this: &mut Object, +// mutable: bool, +// f: impl FnOnce(Box) -> R, +// ) -> Option { +// let view_state = unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// &mut (*view_state) +// }; +// let input_token = view_state.active_text_input?; +// let handler = view_state.handler.acquire_input_lock(input_token, mutable); +// let r = f(handler); +// view_state.handler.release_input_lock(input_token); +// Some(r) +// } + +// fn make_view(handler: Box) -> (id, Weak>>) { +// let idle_queue = Arc::new(Mutex::new(Vec::new())); +// let queue_handle = Arc::downgrade(&idle_queue); +// unsafe { +// let view: id = msg_send![VIEW_CLASS.0, new]; +// let nsview = WeakPtr::new(view); +// let keyboard_state = KeyboardState::new(); +// let state = ViewState { +// nsview, +// handler, +// idle_queue, +// focus_click: false, +// mouse_left: true, +// keyboard_state, +// text: PietText::new_with_unique_state(), +// active_text_input: None, +// parent: None, +// }; +// let state_ptr = Box::into_raw(Box::new(state)); +// (*view).set_ivar("viewState", state_ptr as *mut c_void); +// let options: NSAutoresizingMaskOptions = NSViewWidthSizable | NSViewHeightSizable; +// view.setAutoresizingMask_(options); + +// // The rect of the tracking area doesn't matter, because +// // we use the InVisibleRect option where the OS syncs the size automatically. +// let rect = NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 0.)); +// let opts = NSTrackingAreaOptions::MouseEnteredAndExited +// | NSTrackingAreaOptions::MouseMoved +// | NSTrackingAreaOptions::ActiveAlways +// | NSTrackingAreaOptions::InVisibleRect; +// let tracking_area = NSTrackingArea::alloc(nil) +// .initWithRect_options_owner_userInfo(rect, opts, view, nil) +// .autorelease(); +// view.addTrackingArea(tracking_area); + +// (view.autorelease(), queue_handle) +// } +// } + +// struct WindowClass(*const Class); +// unsafe impl Sync for WindowClass {} + +// lazy_static! { +// static ref WINDOW_CLASS: WindowClass = unsafe { +// let mut decl = +// ClassDecl::new("DruidWindow", class!(NSWindow)).expect("Window class defined"); +// decl.add_method( +// sel!(canBecomeKeyWindow), +// canBecomeKeyWindow as extern "C" fn(&Object, Sel) -> BOOL, +// ); +// extern "C" fn canBecomeKeyWindow(_this: &Object, _sel: Sel) -> BOOL { +// YES +// } +// WindowClass(decl.register()) +// }; +// } + +// extern "C" fn set_frame_size(this: &mut Object, _: Sel, size: NSSize) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// (*view_state) +// .handler +// .size(Size::new(size.width, size.height)); +// let superclass = msg_send![this, superclass]; +// let () = msg_send![super(this, superclass), setFrameSize: size]; +// } +// } + +// fn mouse_event( +// nsevent: id, +// view: id, +// count: u8, +// focus: bool, +// button: MouseButton, +// wheel_delta: Vec2, +// ) -> MouseEvent { +// unsafe { +// let point = nsevent.locationInWindow(); +// let view_point = view.convertPoint_fromView_(point, nil); +// let pos = Point::new(view_point.x as f64, view_point.y as f64); +// let buttons = get_mouse_buttons(NSEvent::pressedMouseButtons(nsevent)); +// let modifiers = make_modifiers(nsevent.modifierFlags()); +// MouseEvent { +// pos, +// buttons, +// mods: modifiers, +// count, +// focus, +// button, +// wheel_delta, +// } +// } +// } + +// fn get_mouse_button(button: NSInteger) -> Option { +// match button { +// 0 => Some(MouseButton::Left), +// 1 => Some(MouseButton::Right), +// 2 => Some(MouseButton::Middle), +// 3 => Some(MouseButton::X1), +// 4 => Some(MouseButton::X2), +// _ => None, +// } +// } + +// fn get_mouse_buttons(mask: NSUInteger) -> MouseButtons { +// let mut buttons = MouseButtons::new(); +// if mask & 1 != 0 { +// buttons.insert(MouseButton::Left); +// } +// if mask & 1 << 1 != 0 { +// buttons.insert(MouseButton::Right); +// } +// if mask & 1 << 2 != 0 { +// buttons.insert(MouseButton::Middle); +// } +// if mask & 1 << 3 != 0 { +// buttons.insert(MouseButton::X1); +// } +// if mask & 1 << 4 != 0 { +// buttons.insert(MouseButton::X2); +// } +// buttons +// } + +// extern "C" fn mouse_down_left(this: &mut Object, _: Sel, nsevent: id) { +// mouse_down(this, nsevent, MouseButton::Left); +// } + +// extern "C" fn mouse_down_right(this: &mut Object, _: Sel, nsevent: id) { +// mouse_down(this, nsevent, MouseButton::Right); +// } + +// extern "C" fn mouse_down_other(this: &mut Object, _: Sel, nsevent: id) { +// unsafe { +// if let Some(button) = get_mouse_button(nsevent.buttonNumber()) { +// mouse_down(this, nsevent, button); +// } +// } +// } + +// fn mouse_down(this: &mut Object, nsevent: id, button: MouseButton) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// let count = nsevent.clickCount() as u8; +// let focus = view_state.focus_click && button == MouseButton::Left; +// let event = mouse_event(nsevent, this as id, count, focus, button, Vec2::ZERO); +// (*view_state).handler.mouse_down(&event); +// } +// } + +// extern "C" fn mouse_up_left(this: &mut Object, _: Sel, nsevent: id) { +// mouse_up(this, nsevent, MouseButton::Left); +// } + +// extern "C" fn mouse_up_right(this: &mut Object, _: Sel, nsevent: id) { +// mouse_up(this, nsevent, MouseButton::Right); +// } + +// extern "C" fn mouse_up_other(this: &mut Object, _: Sel, nsevent: id) { +// unsafe { +// if let Some(button) = get_mouse_button(nsevent.buttonNumber()) { +// mouse_up(this, nsevent, button); +// } +// } +// } + +// fn mouse_up(this: &mut Object, nsevent: id, button: MouseButton) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// let focus = if view_state.focus_click && button == MouseButton::Left { +// view_state.focus_click = false; +// true +// } else { +// false +// }; +// let event = mouse_event(nsevent, this as id, 0, focus, button, Vec2::ZERO); +// (*view_state).handler.mouse_up(&event); +// // If we have already received a mouseExited event then that means +// // we're still receiving mouse events because some buttons are being held down. +// // When the last held button is released and we haven't received a mouseEntered event, +// // then we will no longer receive mouse events until the next mouseEntered event +// // and need to inform the handler of the mouse leaving. +// if view_state.mouse_left && event.buttons.is_empty() { +// (*view_state).handler.mouse_leave(); +// } +// } +// } + +// extern "C" fn mouse_move(this: &mut Object, _: Sel, nsevent: id) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// let event = mouse_event(nsevent, this as id, 0, false, MouseButton::None, Vec2::ZERO); +// (*view_state).handler.mouse_move(&event); +// } +// } + +// extern "C" fn mouse_enter(this: &mut Object, _sel: Sel, nsevent: id) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// view_state.mouse_left = false; +// let event = mouse_event(nsevent, this, 0, false, MouseButton::None, Vec2::ZERO); +// (*view_state).handler.mouse_move(&event); +// } +// } + +// extern "C" fn mouse_leave(this: &mut Object, _: Sel, _nsevent: id) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// view_state.mouse_left = true; +// (*view_state).handler.mouse_leave(); +// } +// } + +// extern "C" fn scroll_wheel(this: &mut Object, _: Sel, nsevent: id) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// let (dx, dy) = { +// let dx = -nsevent.scrollingDeltaX() as f64; +// let dy = -nsevent.scrollingDeltaY() as f64; +// if nsevent.hasPreciseScrollingDeltas() == cocoa::base::YES { +// (dx, dy) +// } else { +// (dx * 32.0, dy * 32.0) +// } +// }; + +// let event = mouse_event( +// nsevent, +// this as id, +// 0, +// false, +// MouseButton::None, +// Vec2::new(dx, dy), +// ); +// (*view_state).handler.wheel(&event); +// } +// } + +// extern "C" fn pinch_event(this: &mut Object, _: Sel, nsevent: id) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); + +// let delta: CGFloat = msg_send![nsevent, magnification]; +// (*view_state).handler.zoom(delta as f64); +// } +// } + +// extern "C" fn key_down(this: &mut Object, _: Sel, nsevent: id) { +// let view_state = unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// &mut *(view_state as *mut ViewState) +// }; +// if let Some(event) = (*view_state).keyboard_state.process_native_event(nsevent) { +// if !(*view_state).handler.key_down(event) { +// // key down not handled; foward to text input system +// unsafe { +// let events = NSArray::arrayWithObjects(nil, &[nsevent]); +// let _: () = msg_send![*(*view_state).nsview.load(), interpretKeyEvents: events]; +// } +// } +// } +// } + +// extern "C" fn key_up(this: &mut Object, _: Sel, nsevent: id) { +// let view_state = unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// &mut *(view_state as *mut ViewState) +// }; +// if let Some(event) = (*view_state).keyboard_state.process_native_event(nsevent) { +// (*view_state).handler.key_up(event); +// } +// } + +// extern "C" fn mods_changed(this: &mut Object, _: Sel, nsevent: id) { +// let view_state = unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// &mut *(view_state as *mut ViewState) +// }; +// if let Some(event) = (*view_state).keyboard_state.process_native_event(nsevent) { +// if event.state == KeyState::Down { +// (*view_state).handler.key_down(event); +// } else { +// (*view_state).handler.key_up(event); +// } +// } +// } + +// extern "C" fn view_will_draw(this: &mut Object, _: Sel) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// (*view_state).handler.prepare_paint(); +// } +// } + +// extern "C" fn draw_rect(this: &mut Object, _: Sel, dirtyRect: NSRect) { +// unsafe { +// let context: id = msg_send![class![NSGraphicsContext], currentContext]; +// //FIXME: when core_graphics is at 0.20, we should be able to use +// //core_graphics::sys::CGContextRef as our pointer type. +// let cgcontext_ptr: *mut ::CType = +// msg_send![context, CGContext]; +// let cgcontext_ref = CGContextRef::from_ptr_mut(cgcontext_ptr); + +// // FIXME: use the actual invalid region instead of just this bounding box. +// // https://developer.apple.com/documentation/appkit/nsview/1483772-getrectsbeingdrawn?language=objc +// let rect = Rect::from_origin_size( +// (dirtyRect.origin.x, dirtyRect.origin.y), +// (dirtyRect.size.width, dirtyRect.size.height), +// ); +// let invalid = Region::from(rect); + +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// let mut piet_ctx = Piet::new_y_down(cgcontext_ref, Some(view_state.text.clone())); + +// (*view_state).handler.paint(&mut piet_ctx, &invalid); +// if let Err(e) = piet_ctx.finish() { +// error!("{}", e) +// } + +// let superclass = msg_send![this, superclass]; +// let () = msg_send![super(this, superclass), drawRect: dirtyRect]; +// } +// } + +// fn run_deferred(this: &mut Object, view_state: &mut ViewState, op: DeferredOp) { +// match op { +// DeferredOp::SetSize(size) => set_size_deferred(this, view_state, size), +// DeferredOp::SetPosition(pos) => set_position_deferred(this, view_state, pos), +// } +// } + +// fn set_size_deferred(this: &mut Object, _view_state: &mut ViewState, size: Size) { +// unsafe { +// let window: id = msg_send![this, window]; +// let current_frame: NSRect = msg_send![window, frame]; +// let mut new_frame = current_frame; + +// // maintain druid origin (as mac origin is bottom left) +// new_frame.origin.y -= size.height - current_frame.size.height; +// new_frame.size.width = size.width; +// new_frame.size.height = size.height; +// let () = msg_send![window, setFrame: new_frame display: YES]; +// } +// } + +// fn set_position_deferred(this: &mut Object, _view_state: &mut ViewState, position: Point) { +// unsafe { +// let window: id = msg_send![this, window]; +// let frame: NSRect = msg_send![window, frame]; + +// let mut new_frame = frame; +// new_frame.origin.x = position.x; +// // TODO Everywhere we use the height for flipping around y it should be the max y in orig mac coords. +// // Need to set up a 3 screen config to test in this arrangement. +// // 3 +// // 1 +// // 2 + +// let screen_height = crate::Screen::get_display_rect().height(); +// new_frame.origin.y = screen_height - position.y - frame.size.height; // Flip back +// let () = msg_send![window, setFrame: new_frame display: YES]; +// debug!("set_position_deferred {:?}", position); +// } +// } + +// extern "C" fn run_idle(this: &mut Object, _: Sel) { +// let view_state = unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// &mut *(view_state as *mut ViewState) +// }; +// let queue: Vec<_> = mem::take(&mut view_state.idle_queue.lock().expect("queue")); +// for item in queue { +// match item { +// IdleKind::Callback(it) => it.call(&mut *view_state.handler), +// IdleKind::Token(it) => { +// view_state.handler.as_mut().idle(it); +// } +// IdleKind::DeferredOp(op) => run_deferred(this, view_state, op), +// } +// } +// } + +// extern "C" fn redraw(this: &mut Object, _: Sel) { +// unsafe { +// let () = msg_send![this as *const _, setNeedsDisplay: YES]; +// } +// } + +// extern "C" fn handle_timer(this: &mut Object, _: Sel, timer: id) { +// let view_state = unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// &mut *(view_state as *mut ViewState) +// }; +// let token = unsafe { +// let user_info: id = msg_send![timer, userInfo]; +// msg_send![user_info, unsignedIntValue] +// }; + +// (*view_state).handler.timer(TimerToken::from_raw(token)); +// } + +// extern "C" fn handle_menu_item(this: &mut Object, _: Sel, item: id) { +// unsafe { +// let tag: isize = msg_send![item, tag]; +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// (*view_state).handler.command(tag as u32); +// } +// } + +// extern "C" fn show_context_menu(this: &mut Object, _: Sel, item: id) { +// unsafe { +// let window: id = msg_send![this as *const _, window]; +// let mut location: NSPoint = msg_send![window, mouseLocationOutsideOfEventStream]; +// let bounds: NSRect = msg_send![this as *const _, bounds]; +// location.y = bounds.size.height - location.y; +// let _: BOOL = msg_send![item, popUpMenuPositioningItem: nil atLocation: location inView: this as *const _]; +// } +// } + +// extern "C" fn window_did_become_key(this: &mut Object, _: Sel, _notification: id) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// (*view_state).handler.got_focus(); +// } +// } + +// extern "C" fn window_did_resign_key(this: &mut Object, _: Sel, _notification: id) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// (*view_state).handler.lost_focus(); +// } +// } + +// extern "C" fn window_should_close(this: &mut Object, _: Sel, _window: id) -> BOOL { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// (*view_state).handler.request_close(); +// NO +// } +// } + +// extern "C" fn window_will_close(this: &mut Object, _: Sel, _notification: id) { +// unsafe { +// let view_state: *mut c_void = *this.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// (*view_state).handler.destroy(); +// } +// } + +// impl WindowHandle { +// pub fn show(&self) { +// unsafe { +// let window: id = msg_send![*self.nsview.load(), window]; +// // register our view class to be alerted when it becomes the key view. +// let notif_center_class = class!(NSNotificationCenter); +// let notif_string = NSString::alloc(nil) +// .init_str(NSWindowDidBecomeKeyNotification) +// .autorelease(); +// let notif_center: id = msg_send![notif_center_class, defaultCenter]; +// let () = msg_send![notif_center, addObserver:*self.nsview.load() selector: sel!(windowDidBecomeKey:) name: notif_string object: window]; +// window.makeKeyAndOrderFront_(nil) +// } +// } + +// /// Close the window. +// pub fn close(&self) { +// unsafe { +// let window: id = msg_send![*self.nsview.load(), window]; +// let () = msg_send![window, performSelectorOnMainThread: sel!(close) withObject: nil waitUntilDone: NO]; +// } +// } + +// /// Bring this window to the front of the window stack and give it focus. +// pub fn bring_to_front_and_focus(&self) { +// unsafe { +// let window: id = msg_send![*self.nsview.load(), window]; +// let () = msg_send![window, performSelectorOnMainThread: sel!(makeKeyAndOrderFront:) withObject: nil waitUntilDone: NO]; +// } +// } + +// pub fn request_anim_frame(&self) { +// unsafe { +// // TODO: synchronize with screen refresh rate using CVDisplayLink instead. +// let () = msg_send![*self.nsview.load(), performSelectorOnMainThread: sel!(redraw) +// withObject: nil waitUntilDone: NO]; +// } +// } + +// // Request invalidation of the entire window contents. +// pub fn invalidate(&self) { +// unsafe { +// // We could share impl with redraw, but we'd need to deal with nil. +// let () = msg_send![*self.nsview.load(), setNeedsDisplay: YES]; +// } +// } + +// /// Request invalidation of one rectangle. +// pub fn invalidate_rect(&self, rect: Rect) { +// let rect = NSRect::new( +// NSPoint::new(rect.x0, rect.y0), +// NSSize::new(rect.width(), rect.height()), +// ); +// unsafe { +// // We could share impl with redraw, but we'd need to deal with nil. +// let () = msg_send![*self.nsview.load(), setNeedsDisplayInRect: rect]; +// } +// } + +// pub fn set_cursor(&mut self, cursor: &Cursor) { +// unsafe { +// let nscursor = class!(NSCursor); +// #[allow(deprecated)] +// let cursor: id = match cursor { +// Cursor::Arrow => msg_send![nscursor, arrowCursor], +// Cursor::IBeam => msg_send![nscursor, IBeamCursor], +// Cursor::Pointer => msg_send![nscursor, pointingHandCursor], +// Cursor::Crosshair => msg_send![nscursor, crosshairCursor], +// Cursor::OpenHand => msg_send![nscursor, openHandCursor], +// Cursor::NotAllowed => msg_send![nscursor, operationNotAllowedCursor], +// Cursor::ResizeLeftRight => msg_send![nscursor, resizeLeftRightCursor], +// Cursor::ResizeUpDown => msg_send![nscursor, resizeUpDownCursor], +// // TODO: support custom cursors +// Cursor::Custom(_) => msg_send![nscursor, arrowCursor], +// }; +// let () = msg_send![cursor, set]; +// } +// } + +// pub fn make_cursor(&self, _cursor_desc: &CursorDesc) -> Option { +// tracing::warn!("Custom cursors are not yet supported in the macOS backend"); +// None +// } + +// pub fn request_timer(&self, deadline: std::time::Instant) -> TimerToken { +// let ti = time_interval_from_deadline(deadline); +// let token = TimerToken::next(); +// unsafe { +// let nstimer = class!(NSTimer); +// let nsnumber = class!(NSNumber); +// let user_info: id = msg_send![nsnumber, numberWithUnsignedInteger: token.into_raw()]; +// let selector = sel!(handleTimer:); +// let view = self.nsview.load(); +// let timer: id = msg_send![nstimer, timerWithTimeInterval: ti target: view selector: selector userInfo: user_info repeats: NO]; +// let runloop: id = msg_send![class!(NSRunLoop), currentRunLoop]; +// let () = msg_send![runloop, addTimer: timer forMode: NSRunLoopCommonModes]; +// } +// token +// } + +// pub fn text(&self) -> PietText { +// let view = self.nsview.load(); +// unsafe { +// if let Some(view) = (*view).as_ref() { +// let state: *mut c_void = *view.get_ivar("viewState"); +// (*(state as *mut ViewState)).text.clone() +// } else { +// // this codepath should only happen during tests in druid, when view is nil +// PietText::new_with_unique_state() +// } +// } +// } + +// pub fn add_text_field(&self) -> TextFieldToken { +// TextFieldToken::next() +// } + +// pub fn remove_text_field(&self, token: TextFieldToken) { +// let view = self.nsview.load(); +// unsafe { +// if let Some(view) = (*view).as_ref() { +// let state: *mut c_void = *view.get_ivar("viewState"); +// let state = &mut (*(state as *mut ViewState)); +// if state.active_text_input == Some(token) { +// state.active_text_input = None; +// } +// } +// } +// } + +// pub fn set_focused_text_field(&self, active_field: Option) { +// unsafe { +// if let Some(view) = self.nsview.load().as_ref() { +// let state: *mut c_void = *view.get_ivar("viewState"); +// let state = &mut (*(state as *mut ViewState)); + +// if let Some(old_field) = state.active_text_input { +// self.update_text_field(old_field, Event::Reset); +// } +// state.active_text_input = active_field; +// if let Some(new_field) = active_field { +// self.update_text_field(new_field, Event::Reset); +// } +// } +// } +// } + +// pub fn update_text_field(&self, token: TextFieldToken, update: Event) { +// unsafe { +// if let Some(view) = self.nsview.load().as_ref() { +// let state: *mut c_void = *view.get_ivar("viewState"); +// let state = &mut (*(state as *mut ViewState)); + +// if state.active_text_input != Some(token) { +// return; +// } +// match update { +// Event::LayoutChanged => { +// let input_context: id = msg_send![*self.nsview.load(), inputContext]; +// let _: () = msg_send![input_context, invalidateCharacterCoordinates]; +// } +// Event::Reset | Event::SelectionChanged => { +// let input_context: id = msg_send![*self.nsview.load(), inputContext]; +// let _: () = msg_send![input_context, discardMarkedText]; +// let mut edit_lock = state.handler.acquire_input_lock(token, true); +// edit_lock.set_composition_range(None); +// state.handler.release_input_lock(token); +// } +// } +// } +// } +// } + +// pub fn open_file(&mut self, options: FileDialogOptions) -> Option { +// Some(self.open_save_impl(FileDialogType::Open, options)) +// } + +// pub fn save_as(&mut self, options: FileDialogOptions) -> Option { +// Some(self.open_save_impl(FileDialogType::Save, options)) +// } + +// fn open_save_impl(&mut self, ty: FileDialogType, opts: FileDialogOptions) -> FileDialogToken { +// let token = FileDialogToken::next(); +// let self_clone = self.clone(); +// unsafe { +// let panel = dialog::build_panel(ty, opts.clone()); +// let block = ConcreteBlock::new(move |response: dialog::NSModalResponse| { +// let url = dialog::get_file_info(panel, opts.clone(), response); +// let view = self_clone.nsview.load(); +// if let Some(view) = (*view).as_ref() { +// let view_state: *mut c_void = *view.get_ivar("viewState"); +// let view_state = &mut *(view_state as *mut ViewState); +// if ty == FileDialogType::Open { +// (*view_state).handler.open_file(token, url); +// } else if ty == FileDialogType::Save { +// (*view_state).handler.save_as(token, url); +// } +// } +// }); +// let block = block.copy(); +// let () = msg_send![panel, beginWithCompletionHandler: block]; +// } +// token +// } + +// /// Set the title for this menu. +// pub fn set_title(&self, title: &str) { +// unsafe { +// let window: id = msg_send![*self.nsview.load(), window]; +// let title = make_nsstring(title); +// window.setTitle_(title); +// } +// } + +// // TODO: Implement this +// pub fn show_titlebar(&self, _show_titlebar: bool) {} + +// // Need to translate mac y coords, as they start from bottom left +// pub fn set_position(&self, mut position: Point) { +// // TODO: Maybe @cmyr can get this into a state where modal windows follow the parent? +// // There is an API to do child windows, (https://developer.apple.com/documentation/appkit/nswindow/1419152-addchildwindow) +// // but I have no good way of testing and making sure this works. +// unsafe { +// if let Some(view) = self.nsview.load().as_ref() { +// let state: *mut c_void = *view.get_ivar("viewState"); +// let state = &mut (*(state as *mut ViewState)); +// if let Some(parent_state) = &state.parent { +// let pos = (*parent_state).get_position(); +// position += (pos.x, pos.y) +// } +// } +// } + +// self.defer(DeferredOp::SetPosition(position)) +// } + +// pub fn get_position(&self) -> Point { +// unsafe { +// // TODO this should be the max y in orig mac coords +// let screen_height = crate::Screen::get_display_rect().height(); + +// let window: id = msg_send![*self.nsview.load(), window]; +// let current_frame: NSRect = msg_send![window, frame]; + +// let mut position = Point::new( +// current_frame.origin.x, +// screen_height - current_frame.origin.y - current_frame.size.height, +// ); +// if let Some(view) = self.nsview.load().as_ref() { +// let state: *mut c_void = *view.get_ivar("viewState"); +// let state = &mut (*(state as *mut ViewState)); +// if let Some(parent_state) = &state.parent { +// let pos = (*parent_state).get_position(); +// position -= (pos.x, pos.y) +// } +// } +// position +// } +// } + +// pub fn content_insets(&self) -> Insets { +// unsafe { +// let screen_height = crate::Screen::get_display_rect().height(); + +// let window: id = msg_send![*self.nsview.load(), window]; +// let clr: NSRect = msg_send![window, contentLayoutRect]; + +// let window_frame_r: NSRect = NSWindow::frame(window); +// let content_frame_r: NSRect = NSWindow::convertRectToScreen_(window, clr); + +// let window_frame_rk = Rect::from_origin_size( +// ( +// window_frame_r.origin.x, +// screen_height - window_frame_r.origin.y - window_frame_r.size.height, +// ), +// (window_frame_r.size.width, window_frame_r.size.height), +// ); +// let content_frame_rk = Rect::from_origin_size( +// ( +// content_frame_r.origin.x, +// screen_height - content_frame_r.origin.y - content_frame_r.size.height, +// ), +// (content_frame_r.size.width, content_frame_r.size.height), +// ); +// window_frame_rk - content_frame_rk +// } +// } + +// fn set_level(&self, level: WindowLevel) { +// unsafe { +// let level = levels::as_raw_window_level(level); +// let window: id = msg_send![*self.nsview.load(), window]; +// let () = msg_send![window, setLevel: level]; +// } +// } + +// pub fn set_size(&self, size: Size) { +// self.defer(DeferredOp::SetSize(size)); +// } + +// pub fn get_size(&self) -> Size { +// unsafe { +// let window: id = msg_send![*self.nsview.load(), window]; +// let current_frame: NSRect = msg_send![window, frame]; +// Size::new(current_frame.size.width, current_frame.size.height) +// } +// } + +// pub fn get_window_state(&self) -> WindowState { +// unsafe { +// let window: id = msg_send![*self.nsview.load(), window]; +// let isMin: BOOL = msg_send![window, isMiniaturized]; +// if isMin != NO { +// return WindowState::Minimized; +// } +// let isZoomed: BOOL = msg_send![window, isZoomed]; +// if isZoomed != NO { +// return WindowState::Maximized; +// } +// } +// WindowState::Restored +// } + +// pub fn set_window_state(&mut self, state: WindowState) { +// let cur_state = self.get_window_state(); +// unsafe { +// let window: id = msg_send![*self.nsview.load(), window]; +// match (state, cur_state) { +// (s1, s2) if s1 == s2 => (), +// (WindowState::Minimized, _) => { +// let () = msg_send![window, performMiniaturize: self]; +// } +// (WindowState::Maximized, _) => { +// let () = msg_send![window, performZoom: self]; +// } +// (WindowState::Restored, WindowState::Maximized) => { +// let () = msg_send![window, performZoom: self]; +// } +// (WindowState::Restored, WindowState::Minimized) => { +// let () = msg_send![window, deminiaturize: self]; +// } +// (WindowState::Restored, WindowState::Restored) => {} // Can't be reached +// } +// } +// } + +// pub fn handle_titlebar(&self, _val: bool) { +// tracing::warn!("WindowHandle::handle_titlebar is currently unimplemented for Mac."); +// } + +// pub fn resizable(&self, resizable: bool) { +// unsafe { +// let window: id = msg_send![*self.nsview.load(), window]; +// let mut style_mask: NSWindowStyleMask = window.styleMask(); + +// if resizable { +// style_mask |= NSWindowStyleMask::NSResizableWindowMask; +// } else { +// style_mask &= !NSWindowStyleMask::NSResizableWindowMask; +// } + +// window.setStyleMask_(style_mask); +// } +// } + +// pub fn set_menu(&self, menu: Menu) { +// unsafe { +// NSApp().setMainMenu_(menu.menu); +// } +// } + +// //FIXME: we should be using the x, y values passed by the caller, but then +// //we have to figure out some way to pass them along with this performSelector: +// //call. This isn't super hard, I'm just not up for it right now. +// pub fn show_context_menu(&self, menu: Menu, _pos: Point) { +// unsafe { +// let () = msg_send![*self.nsview.load(), performSelectorOnMainThread: sel!(showContextMenu:) withObject: menu.menu waitUntilDone: NO]; +// } +// } + +// fn defer(&self, op: DeferredOp) { +// if let Some(i) = self.get_idle_handle() { +// i.add_idle(IdleKind::DeferredOp(op)) +// } +// } + +// /// Get a handle that can be used to schedule an idle task. +// pub fn get_idle_handle(&self) -> Option { +// if self.nsview.load().is_null() { +// None +// } else { +// Some(IdleHandle { +// nsview: self.nsview.clone(), +// idle_queue: self.idle_queue.clone(), +// }) +// } +// } + +// /// Get the `Scale` of the window. +// pub fn get_scale(&self) -> Result { +// // TODO: Get actual Scale +// Ok(Scale::new(1.0, 1.0)) +// } +// } + +// #[cfg(feature = "raw-win-handle")] +// unsafe impl HasRawWindowHandle for WindowHandle { +// fn raw_window_handle(&self) -> RawWindowHandle { +// let nsv = self.nsview.load(); +// let handle = MacOSHandle { +// ns_view: *nsv as *mut _, +// ..MacOSHandle::empty() +// }; +// RawWindowHandle::MacOS(handle) +// } +// } + +unsafe impl Send for IdleHandle {} + +// impl IdleHandle { +// fn add_idle(&self, idle: IdleKind) { +// if let Some(queue) = self.idle_queue.upgrade() { +// let mut queue = queue.lock().expect("queue lock"); +// if queue.is_empty() { +// unsafe { +// let nsview = self.nsview.load(); +// // Note: the nsview might be nil here if the window has been dropped, but that's ok. +// let () = msg_send!(*nsview, performSelectorOnMainThread: sel!(runIdle) +// withObject: nil waitUntilDone: NO); +// } +// } +// queue.push(idle); +// } +// } + +// /// Add an idle handler, which is called (once) when the message loop +// /// is empty. The idle handler will be run from the main UI thread, and +// /// won't be scheduled if the associated view has been dropped. +// /// +// /// Note: the name "idle" suggests that it will be scheduled with a lower +// /// priority than other UI events, but that's not necessarily the case. +// pub fn add_idle_callback(&self, callback: F) +// where +// F: FnOnce(&mut dyn WinHandler) + Send + 'static, +// { +// self.add_idle(IdleKind::Callback(Box::new(callback))); +// } + +// pub fn add_idle_token(&self, token: IdleToken) { +// self.add_idle(IdleKind::Token(token)); +// } +// } + +// /// Convert an `Instant` into an NSTimeInterval, i.e. a fractional number +// /// of seconds from now. +// /// +// /// This may lose some precision for multi-month durations. +// fn time_interval_from_deadline(deadline: std::time::Instant) -> f64 { +// let now = Instant::now(); +// if now >= deadline { +// 0.0 +// } else { +// let t = deadline - now; +// let secs = t.as_secs() as f64; +// let subsecs = f64::from(t.subsec_micros()) * 0.000_001; +// secs + subsecs +// } +// } From a5e60f4cce60e4e5d9d680c57cbf2abf7cdf10bf Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Tue, 4 Jan 2022 14:34:24 -0800 Subject: [PATCH 06/13] allow unused --- druid-shell/src/backend/shared/noop_menu.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/druid-shell/src/backend/shared/noop_menu.rs b/druid-shell/src/backend/shared/noop_menu.rs index 5e39ec6c86..87aaf823c1 100644 --- a/druid-shell/src/backend/shared/noop_menu.rs +++ b/druid-shell/src/backend/shared/noop_menu.rs @@ -15,8 +15,9 @@ //! Implementation of menu that does nothing, for platforms that either don't have a menu available, or //! just don't have an implementation in druid-shell yet. -use crate::hotkey::HotKey; +#![allow(unused)] +use crate::hotkey::HotKey; pub struct Menu; impl Menu { From a89fb15b3de43c71b067c68025a911265ed68f3c Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Tue, 4 Jan 2022 15:05:00 -0800 Subject: [PATCH 07/13] old version of piet-common --- druid-shell/Cargo.toml | 4 ++-- druid-shell/src/backend/mod.rs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/druid-shell/Cargo.toml b/druid-shell/Cargo.toml index 001da69cf9..13c9c94f04 100755 --- a/druid-shell/Cargo.toml +++ b/druid-shell/Cargo.toml @@ -41,7 +41,7 @@ serde = ["kurbo/serde"] [dependencies] # NOTE: When changing the piet or kurbo versions, ensure that # the kurbo version included in piet is compatible with the kurbo version specified here. -piet-common = {path = "../../piet/piet-common"} +piet-common = "=0.5.0" kurbo = "0.8.2" tracing = "0.1.22" @@ -96,7 +96,7 @@ version = "0.3.44" features = ["Window", "MouseEvent", "CssStyleDeclaration", "WheelEvent", "KeyEvent", "KeyboardEvent", "Navigator"] [dev-dependencies] -piet-common = { path = "../../piet/piet-common", features = ["png"] } +piet-common = { version = "=0.5.0", features = ["png"] } static_assertions = "1.1.0" test-log = { version = "0.2.5", features = ["trace"], default-features = false } tracing-subscriber = { version = "0.3.2", features = ["env-filter"] } diff --git a/druid-shell/src/backend/mod.rs b/druid-shell/src/backend/mod.rs index 581dedc990..a127c51b28 100644 --- a/druid-shell/src/backend/mod.rs +++ b/druid-shell/src/backend/mod.rs @@ -53,3 +53,5 @@ pub(crate) mod shared; mod web; #[cfg(target_arch = "wasm32")] pub use web::*; +#[cfg(target_arch = "wasm32")] +pub(crate) mod shared; From 3d6eeb24cc54509217955b57db6d7c1d41d1fedd Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Tue, 4 Jan 2022 15:20:34 -0800 Subject: [PATCH 08/13] piet common fix --- druid-shell/Cargo.toml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/druid-shell/Cargo.toml b/druid-shell/Cargo.toml index 13c9c94f04..c985885706 100755 --- a/druid-shell/Cargo.toml +++ b/druid-shell/Cargo.toml @@ -41,7 +41,6 @@ serde = ["kurbo/serde"] [dependencies] # NOTE: When changing the piet or kurbo versions, ensure that # the kurbo version included in piet is compatible with the kurbo version specified here. -piet-common = "=0.5.0" kurbo = "0.8.2" tracing = "0.1.22" @@ -56,6 +55,12 @@ keyboard-types = { version = "0.6.2", default_features = false } image = { version = "0.23.12", optional = true, default_features = false } raw-window-handle = { version = "0.3.3", optional = true, default_features = false } +[target.'cfg(not(target_os="ios"))'.dependencies] +piet-common = "=0.5.0" + +[target.'cfg(not(target_os="ios"))'.dev-dependencies] +piet-common = { version = "=0.5.0", features = ["png"] } + [target.'cfg(target_os="windows")'.dependencies] scopeguard = "1.1.0" wio = "0.2.2" @@ -96,7 +101,6 @@ version = "0.3.44" features = ["Window", "MouseEvent", "CssStyleDeclaration", "WheelEvent", "KeyEvent", "KeyboardEvent", "Navigator"] [dev-dependencies] -piet-common = { version = "=0.5.0", features = ["png"] } static_assertions = "1.1.0" test-log = { version = "0.2.5", features = ["trace"], default-features = false } tracing-subscriber = { version = "0.3.2", features = ["env-filter"] } From 0b9751aceea7db14881eb1b25363ae107255c5aa Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Tue, 4 Jan 2022 15:25:22 -0800 Subject: [PATCH 09/13] old cargo --- druid-shell/Cargo.toml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/druid-shell/Cargo.toml b/druid-shell/Cargo.toml index c985885706..13c9c94f04 100755 --- a/druid-shell/Cargo.toml +++ b/druid-shell/Cargo.toml @@ -41,6 +41,7 @@ serde = ["kurbo/serde"] [dependencies] # NOTE: When changing the piet or kurbo versions, ensure that # the kurbo version included in piet is compatible with the kurbo version specified here. +piet-common = "=0.5.0" kurbo = "0.8.2" tracing = "0.1.22" @@ -55,12 +56,6 @@ keyboard-types = { version = "0.6.2", default_features = false } image = { version = "0.23.12", optional = true, default_features = false } raw-window-handle = { version = "0.3.3", optional = true, default_features = false } -[target.'cfg(not(target_os="ios"))'.dependencies] -piet-common = "=0.5.0" - -[target.'cfg(not(target_os="ios"))'.dev-dependencies] -piet-common = { version = "=0.5.0", features = ["png"] } - [target.'cfg(target_os="windows")'.dependencies] scopeguard = "1.1.0" wio = "0.2.2" @@ -101,6 +96,7 @@ version = "0.3.44" features = ["Window", "MouseEvent", "CssStyleDeclaration", "WheelEvent", "KeyEvent", "KeyboardEvent", "Navigator"] [dev-dependencies] +piet-common = { version = "=0.5.0", features = ["png"] } static_assertions = "1.1.0" test-log = { version = "0.2.5", features = ["trace"], default-features = false } tracing-subscriber = { version = "0.3.2", features = ["env-filter"] } From 36ac62a7499982c1d2e8eb98d038f61aae09b138 Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Tue, 4 Jan 2022 15:27:31 -0800 Subject: [PATCH 10/13] path --- druid-shell/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/druid-shell/Cargo.toml b/druid-shell/Cargo.toml index 13c9c94f04..891bbf1283 100755 --- a/druid-shell/Cargo.toml +++ b/druid-shell/Cargo.toml @@ -41,7 +41,7 @@ serde = ["kurbo/serde"] [dependencies] # NOTE: When changing the piet or kurbo versions, ensure that # the kurbo version included in piet is compatible with the kurbo version specified here. -piet-common = "=0.5.0" +piet-common = { path = "../../piet/piet-common" } kurbo = "0.8.2" tracing = "0.1.22" @@ -96,7 +96,7 @@ version = "0.3.44" features = ["Window", "MouseEvent", "CssStyleDeclaration", "WheelEvent", "KeyEvent", "KeyboardEvent", "Navigator"] [dev-dependencies] -piet-common = { version = "=0.5.0", features = ["png"] } +piet-common = { path = "../../piet/piet-common", features = ["png"] } static_assertions = "1.1.0" test-log = { version = "0.2.5", features = ["trace"], default-features = false } tracing-subscriber = { version = "0.3.2", features = ["env-filter"] } From fa6e4b37ae1027ad7eee0e2c49d34dc39e5ad99c Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Sat, 15 Jan 2022 07:44:35 -0800 Subject: [PATCH 11/13] it compiles with lots of unimplemented!() --- druid-shell/Cargo.toml | 4 +- druid-shell/src/backend/ios/window.rs | 1401 +++++++++++++------------ 2 files changed, 705 insertions(+), 700 deletions(-) diff --git a/druid-shell/Cargo.toml b/druid-shell/Cargo.toml index 891bbf1283..9584a3e1cb 100755 --- a/druid-shell/Cargo.toml +++ b/druid-shell/Cargo.toml @@ -41,7 +41,7 @@ serde = ["kurbo/serde"] [dependencies] # NOTE: When changing the piet or kurbo versions, ensure that # the kurbo version included in piet is compatible with the kurbo version specified here. -piet-common = { path = "../../piet/piet-common" } +piet-common = { git = "https://github.com/linebender/piet", rev = "57cc49a8a88703b1c754a8c2c7f6f96007a24d4d" } kurbo = "0.8.2" tracing = "0.1.22" @@ -96,7 +96,7 @@ version = "0.3.44" features = ["Window", "MouseEvent", "CssStyleDeclaration", "WheelEvent", "KeyEvent", "KeyboardEvent", "Navigator"] [dev-dependencies] -piet-common = { path = "../../piet/piet-common", features = ["png"] } +piet-common = { git = "https://github.com/linebender/piet", rev = "57cc49a8a88703b1c754a8c2c7f6f96007a24d4d", features = ["png"] } static_assertions = "1.1.0" test-log = { version = "0.2.5", features = ["trace"], default-features = false } tracing-subscriber = { version = "0.3.2", features = ["env-filter"] } diff --git a/druid-shell/src/backend/ios/window.rs b/druid-shell/src/backend/ios/window.rs index e6e1f970f7..0f89116233 100644 --- a/druid-shell/src/backend/ios/window.rs +++ b/druid-shell/src/backend/ios/window.rs @@ -16,48 +16,42 @@ #![allow(non_snake_case)] -// use std::ffi::c_void; -// use std::mem; +use std::ffi::c_void; +use std::mem; use std::sync::{Arc, Mutex, Weak}; -// use std::time::Instant; - -// use block::ConcreteBlock; -// use cocoa::base::{id, nil, BOOL, NO, YES}; -// use cocoa::foundation::{ -// NSArray, NSAutoreleasePool, NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger, -// }; -// use core_graphics::context::CGContextRef; -// use foreign_types::ForeignTypeRef; -// use lazy_static::lazy_static; -// use objc::declare::ClassDecl; +use std::time::Instant; + +use block::ConcreteBlock; +use cocoa::base::{id, nil, BOOL, NO, YES}; +use cocoa::foundation::{NSArray, NSAutoreleasePool, NSInteger, NSString, NSUInteger}; +use core_graphics::context::CGContextRef; +use foreign_types::ForeignTypeRef; +use lazy_static::lazy_static; +use objc::declare::ClassDecl; use objc::rc::WeakPtr; -// use objc::runtime::{Class, Object, Protocol, Sel}; -// use objc::{class, msg_send, sel, sel_impl}; -// use tracing::{debug, error, info}; - -// #[cfg(feature = "raw-win-handle")] -// use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle}; - -// use crate::kurbo::{Insets, Point, Rect, Size, Vec2}; -// use crate::piet::{Piet, PietText, RenderContext}; - -// use super::application::Application; -// use super::dialog; -// use super::keyboard::{make_modifiers, KeyboardState}; -// use super::menu::Menu; -// use super::text_input::NSRange; -// use super::util::{assert_main_thread, make_nsstring}; -// use crate::common_util::IdleCallback; -// use crate::dialog::{FileDialogOptions, FileDialogType}; -// use crate::keyboard_types::KeyState; -// use crate::mouse::{Cursor, CursorDesc, MouseButton, MouseButtons, MouseEvent}; -// use crate::region::Region; -// use crate::scale::Scale; -// use crate::text::{Event, InputHandler}; -// use crate::window::{ -// FileDialogToken, IdleToken, TextFieldToken, TimerToken, WinHandler, WindowLevel, WindowState, -// }; -// use crate::Error; +use objc::runtime::{Class, Object, Protocol, Sel}; +use objc::{class, msg_send, sel, sel_impl}; +use tracing::{debug, error, info}; + +#[cfg(feature = "raw-win-handle")] +use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle}; + +use crate::kurbo::{Insets, Point, Rect, Size, Vec2}; +use crate::piet::{Piet, PietText, RenderContext}; + +use super::application::Application; +use super::menu::Menu; +use crate::common_util::IdleCallback; +use crate::dialog::{FileDialogOptions, FileDialogType}; +use crate::keyboard_types::KeyState; +use crate::mouse::{Cursor, CursorDesc, MouseButton, MouseButtons, MouseEvent}; +use crate::region::Region; +use crate::scale::Scale; +use crate::text::{Event, InputHandler}; +use crate::window::{ + FileDialogToken, IdleToken, TextFieldToken, TimerToken, WinHandler, WindowLevel, WindowState, +}; +use crate::Error; // #[allow(non_upper_case_globals)] // const NSWindowDidBecomeKeyNotification: &str = "NSWindowDidBecomeKeyNotification"; @@ -89,55 +83,54 @@ use objc::rc::WeakPtr; // } // } -// #[derive(Clone)] +#[derive(Clone)] pub(crate) struct WindowHandle { - /// This is an NSView, as our concept of "window" is more the top-level container holding - /// a view. Also, this is better for hosted applications such as VST. - nsview: WeakPtr, + // TODO rlord: should this be a uiwindow? uiwindowscene? + uiview: WeakPtr, idle_queue: Weak>>, } -// impl PartialEq for WindowHandle { -// fn eq(&self, other: &Self) -> bool { -// match (self.idle_queue.upgrade(), other.idle_queue.upgrade()) { -// (None, None) => true, -// (Some(s), Some(o)) => std::sync::Arc::ptr_eq(&s, &o), -// (_, _) => false, -// } -// } -// } -// impl Eq for WindowHandle {} - -// impl std::fmt::Debug for WindowHandle { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { -// f.write_str("WindowHandle{\n")?; -// f.write_str("}")?; -// Ok(()) -// } -// } +impl PartialEq for WindowHandle { + fn eq(&self, other: &Self) -> bool { + match (self.idle_queue.upgrade(), other.idle_queue.upgrade()) { + (None, None) => true, + (Some(s), Some(o)) => std::sync::Arc::ptr_eq(&s, &o), + (_, _) => false, + } + } +} +impl Eq for WindowHandle {} + +impl std::fmt::Debug for WindowHandle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + f.write_str("WindowHandle{\n")?; + f.write_str("}")?; + Ok(()) + } +} -// impl Default for WindowHandle { -// fn default() -> Self { -// WindowHandle { -// nsview: unsafe { WeakPtr::new(nil) }, -// idle_queue: Default::default(), -// } -// } -// } +impl Default for WindowHandle { + fn default() -> Self { + WindowHandle { + uiview: unsafe { WeakPtr::new(nil) }, + idle_queue: Default::default(), + } + } +} -// /// Builder abstraction for creating new windows. -// pub(crate) struct WindowBuilder { -// handler: Option>, -// title: String, -// menu: Option, -// size: Size, -// min_size: Option, -// position: Option, -// level: Option, -// window_state: Option, -// resizable: bool, -// show_titlebar: bool, -// transparent: bool, -// } +/// Builder abstraction for creating new windows. +pub(crate) struct WindowBuilder { + handler: Option>, + title: String, + menu: Option, + size: Size, + min_size: Option, + position: Option, + level: Option, + window_state: Option, + resizable: bool, + show_titlebar: bool, + transparent: bool, +} #[derive(Clone)] pub(crate) struct IdleHandle { @@ -153,179 +146,180 @@ enum DeferredOp { /// This represents different Idle Callback Mechanism enum IdleKind { - // Callback(Box), -// Token(IdleToken), -// DeferredOp(DeferredOp), + Callback(Box), + Token(IdleToken), + // DeferredOp(DeferredOp), } -// /// This is the state associated with our custom NSView. -// struct ViewState { -// nsview: WeakPtr, -// handler: Box, -// idle_queue: Arc>>, -// /// Tracks window focusing left clicks -// focus_click: bool, -// // Tracks whether we have already received the mouseExited event -// mouse_left: bool, -// keyboard_state: KeyboardState, -// text: PietText, -// active_text_input: Option, -// parent: Option, -// } +/// This is the state associated with our custom UIView. +struct ViewState { + uiview: WeakPtr, + handler: Box, + idle_queue: Arc>>, + /// Tracks window focusing left clicks + // focus_click: bool, + // Tracks whether we have already received the mouseExited event + // mouse_left: bool, + // keyboard_state: KeyboardState, + text: PietText, + // active_text_input: Option, + // parent: Option, +} #[derive(Clone, PartialEq)] // TODO: support custom cursors on iPadOS pub struct CustomCursor; -// impl WindowBuilder { -// pub fn new(_app: Application) -> WindowBuilder { -// WindowBuilder { -// handler: None, -// title: String::new(), -// menu: None, -// size: Size::new(500., 400.), -// min_size: None, -// position: None, -// level: None, -// window_state: None, -// resizable: true, -// show_titlebar: true, -// transparent: false, -// } -// } - -// pub fn set_handler(&mut self, handler: Box) { -// self.handler = Some(handler); -// } - -// pub fn set_size(&mut self, size: Size) { -// self.size = size; -// } - -// pub fn set_min_size(&mut self, size: Size) { -// self.min_size = Some(size); -// } - -// pub fn resizable(&mut self, resizable: bool) { -// self.resizable = resizable; -// } - -// pub fn show_titlebar(&mut self, show_titlebar: bool) { -// self.show_titlebar = show_titlebar; -// } - -// pub fn set_transparent(&mut self, transparent: bool) { -// self.transparent = transparent; -// } - -// pub fn set_level(&mut self, level: WindowLevel) { -// self.level = Some(level); -// } - -// pub fn set_position(&mut self, position: Point) { -// self.position = Some(position) -// } - -// pub fn set_window_state(&mut self, state: WindowState) { -// self.window_state = Some(state); -// } - -// pub fn set_title(&mut self, title: impl Into) { -// self.title = title.into(); -// } - -// pub fn set_menu(&mut self, menu: Menu) { -// self.menu = Some(menu); -// } - -// pub fn build(self) -> Result { -// assert_main_thread(); -// unsafe { -// let mut style_mask = NSWindowStyleMask::NSClosableWindowMask -// | NSWindowStyleMask::NSMiniaturizableWindowMask; - -// if self.show_titlebar { -// style_mask |= NSWindowStyleMask::NSTitledWindowMask; -// } - -// if self.resizable { -// style_mask |= NSWindowStyleMask::NSResizableWindowMask; -// } - -// let screen_height = crate::Screen::get_display_rect().height(); -// let position = self.position.unwrap_or_else(|| Point::new(20., 20.)); -// let origin = NSPoint::new(position.x, screen_height - position.y - self.size.height); // Flip back - -// let rect = NSRect::new(origin, NSSize::new(self.size.width, self.size.height)); - -// let window: id = msg_send![WINDOW_CLASS.0, alloc]; -// let window = window.initWithContentRect_styleMask_backing_defer_( -// rect, -// style_mask, -// NSBackingStoreBuffered, -// NO, -// ); - -// if let Some(min_size) = self.min_size { -// let size = NSSize::new(min_size.width, min_size.height); -// window.setContentMinSize_(size); -// } - -// if self.transparent { -// window.setOpaque_(NO); -// window.setBackgroundColor_(NSColor::clearColor(nil)); -// } - -// window.setTitle_(make_nsstring(&self.title)); - -// let (view, idle_queue) = make_view(self.handler.expect("view")); -// let content_view = window.contentView(); -// let frame = NSView::frame(content_view); -// view.initWithFrame_(frame); - -// let () = msg_send![window, setDelegate: view]; - -// if let Some(menu) = self.menu { -// NSApp().setMainMenu_(menu.menu); -// } - -// content_view.addSubview_(view); -// let view_state: *mut c_void = *(*view).get_ivar("viewState"); -// let view_state = &mut *(view_state as *mut ViewState); -// let mut handle = WindowHandle { -// nsview: view_state.nsview.clone(), -// idle_queue, -// }; - -// if let Some(window_state) = self.window_state { -// handle.set_window_state(window_state); -// } - -// if let Some(level) = self.level { -// match &level { -// WindowLevel::Tooltip(parent) => (*view_state).parent = Some(parent.clone()), -// WindowLevel::DropDown(parent) => (*view_state).parent = Some(parent.clone()), -// WindowLevel::Modal(parent) => (*view_state).parent = Some(parent.clone()), -// _ => {} -// } -// handle.set_level(level); -// } - -// // set_window_state above could have invalidated the frame size -// let frame = NSView::frame(content_view); - -// (*view_state).handler.connect(&handle.clone().into()); -// (*view_state).handler.scale(Scale::default()); -// (*view_state) -// .handler -// .size(Size::new(frame.size.width, frame.size.height)); - -// Ok(handle) -// } -// } -// } +impl WindowBuilder { + pub fn new(_app: Application) -> WindowBuilder { + WindowBuilder { + handler: None, + title: String::new(), + menu: None, + size: Size::new(500., 400.), + min_size: None, + position: None, + level: None, + window_state: None, + resizable: true, + show_titlebar: true, + transparent: false, + } + } + + pub fn set_handler(&mut self, handler: Box) { + self.handler = Some(handler); + } + + pub fn set_size(&mut self, size: Size) { + self.size = size; + } + + pub fn set_min_size(&mut self, size: Size) { + self.min_size = Some(size); + } + + pub fn resizable(&mut self, resizable: bool) { + self.resizable = resizable; + } + + pub fn show_titlebar(&mut self, show_titlebar: bool) { + self.show_titlebar = show_titlebar; + } + + pub fn set_transparent(&mut self, transparent: bool) { + self.transparent = transparent; + } + + pub fn set_level(&mut self, level: WindowLevel) { + self.level = Some(level); + } + + pub fn set_position(&mut self, position: Point) { + self.position = Some(position) + } + + pub fn set_window_state(&mut self, state: WindowState) { + self.window_state = Some(state); + } + + pub fn set_title(&mut self, title: impl Into) { + self.title = title.into(); + } + + pub fn set_menu(&mut self, menu: Menu) { + self.menu = Some(menu); + } + + pub fn build(self) -> Result { + // assert_main_thread(); + unsafe { + // let mut style_mask = NSWindowStyleMask::NSClosableWindowMask + // | NSWindowStyleMask::NSMiniaturizableWindowMask; + + // if self.show_titlebar { + // style_mask |= NSWindowStyleMask::NSTitledWindowMask; + // } + + // if self.resizable { + // style_mask |= NSWindowStyleMask::NSResizableWindowMask; + // } + + // let screen_height = crate::Screen::get_display_rect().height(); + // let position = self.position.unwrap_or_else(|| Point::new(20., 20.)); + // let origin = NSPoint::new(position.x, screen_height - position.y - self.size.height); // Flip back + + // let rect = NSRect::new(origin, NSSize::new(self.size.width, self.size.height)); + + // let window: id = msg_send![WINDOW_CLASS.0, alloc]; + // let window = window.initWithContentRect_styleMask_backing_defer_( + // rect, + // style_mask, + // NSBackingStoreBuffered, + // NO, + // ); + + // if let Some(min_size) = self.min_size { + // let size = NSSize::new(min_size.width, min_size.height); + // window.setContentMinSize_(size); + // } + + // if self.transparent { + // window.setOpaque_(NO); + // window.setBackgroundColor_(NSColor::clearColor(nil)); + // } + + // window.setTitle_(make_nsstring(&self.title)); + + // let (view, idle_queue) = make_view(self.handler.expect("view")); + // let content_view = window.contentView(); + // let frame = NSView::frame(content_view); + // view.initWithFrame_(frame); + + // let () = msg_send![window, setDelegate: view]; + + // if let Some(menu) = self.menu { + // NSApp().setMainMenu_(menu.menu); + // } + + // content_view.addSubview_(view); + // let view_state: *mut c_void = *(*view).get_ivar("viewState"); + // let view_state = &mut *(view_state as *mut ViewState); + // let mut handle = WindowHandle { + // nsview: view_state.nsview.clone(), + // idle_queue, + // }; + + // if let Some(window_state) = self.window_state { + // handle.set_window_state(window_state); + // } + + // if let Some(level) = self.level { + // match &level { + // WindowLevel::Tooltip(parent) => (*view_state).parent = Some(parent.clone()), + // WindowLevel::DropDown(parent) => (*view_state).parent = Some(parent.clone()), + // WindowLevel::Modal(parent) => (*view_state).parent = Some(parent.clone()), + // _ => {} + // } + // handle.set_level(level); + // } + + // // set_window_state above could have invalidated the frame size + // let frame = NSView::frame(content_view); + + // (*view_state).handler.connect(&handle.clone().into()); + // (*view_state).handler.scale(Scale::default()); + // (*view_state) + // .handler + // .size(Size::new(frame.size.width, frame.size.height)); + + // Ok(handle) + } + unimplemented!() + } +} -// // Wrap pointer because lazy_static requires Sync. +// Wrap pointer because lazy_static requires Sync. // struct ViewClass(*const Class); // unsafe impl Sync for ViewClass {} @@ -999,458 +993,469 @@ pub struct CustomCursor; // } // } -// impl WindowHandle { -// pub fn show(&self) { -// unsafe { -// let window: id = msg_send![*self.nsview.load(), window]; -// // register our view class to be alerted when it becomes the key view. -// let notif_center_class = class!(NSNotificationCenter); -// let notif_string = NSString::alloc(nil) -// .init_str(NSWindowDidBecomeKeyNotification) -// .autorelease(); -// let notif_center: id = msg_send![notif_center_class, defaultCenter]; -// let () = msg_send![notif_center, addObserver:*self.nsview.load() selector: sel!(windowDidBecomeKey:) name: notif_string object: window]; -// window.makeKeyAndOrderFront_(nil) -// } -// } - -// /// Close the window. -// pub fn close(&self) { -// unsafe { -// let window: id = msg_send![*self.nsview.load(), window]; -// let () = msg_send![window, performSelectorOnMainThread: sel!(close) withObject: nil waitUntilDone: NO]; -// } -// } - -// /// Bring this window to the front of the window stack and give it focus. -// pub fn bring_to_front_and_focus(&self) { -// unsafe { -// let window: id = msg_send![*self.nsview.load(), window]; -// let () = msg_send![window, performSelectorOnMainThread: sel!(makeKeyAndOrderFront:) withObject: nil waitUntilDone: NO]; -// } -// } - -// pub fn request_anim_frame(&self) { -// unsafe { -// // TODO: synchronize with screen refresh rate using CVDisplayLink instead. -// let () = msg_send![*self.nsview.load(), performSelectorOnMainThread: sel!(redraw) -// withObject: nil waitUntilDone: NO]; -// } -// } - -// // Request invalidation of the entire window contents. -// pub fn invalidate(&self) { -// unsafe { -// // We could share impl with redraw, but we'd need to deal with nil. -// let () = msg_send![*self.nsview.load(), setNeedsDisplay: YES]; -// } -// } - -// /// Request invalidation of one rectangle. -// pub fn invalidate_rect(&self, rect: Rect) { -// let rect = NSRect::new( -// NSPoint::new(rect.x0, rect.y0), -// NSSize::new(rect.width(), rect.height()), -// ); -// unsafe { -// // We could share impl with redraw, but we'd need to deal with nil. -// let () = msg_send![*self.nsview.load(), setNeedsDisplayInRect: rect]; -// } -// } - -// pub fn set_cursor(&mut self, cursor: &Cursor) { -// unsafe { -// let nscursor = class!(NSCursor); -// #[allow(deprecated)] -// let cursor: id = match cursor { -// Cursor::Arrow => msg_send![nscursor, arrowCursor], -// Cursor::IBeam => msg_send![nscursor, IBeamCursor], -// Cursor::Pointer => msg_send![nscursor, pointingHandCursor], -// Cursor::Crosshair => msg_send![nscursor, crosshairCursor], -// Cursor::OpenHand => msg_send![nscursor, openHandCursor], -// Cursor::NotAllowed => msg_send![nscursor, operationNotAllowedCursor], -// Cursor::ResizeLeftRight => msg_send![nscursor, resizeLeftRightCursor], -// Cursor::ResizeUpDown => msg_send![nscursor, resizeUpDownCursor], -// // TODO: support custom cursors -// Cursor::Custom(_) => msg_send![nscursor, arrowCursor], -// }; -// let () = msg_send![cursor, set]; -// } -// } - -// pub fn make_cursor(&self, _cursor_desc: &CursorDesc) -> Option { -// tracing::warn!("Custom cursors are not yet supported in the macOS backend"); -// None -// } - -// pub fn request_timer(&self, deadline: std::time::Instant) -> TimerToken { -// let ti = time_interval_from_deadline(deadline); -// let token = TimerToken::next(); -// unsafe { -// let nstimer = class!(NSTimer); -// let nsnumber = class!(NSNumber); -// let user_info: id = msg_send![nsnumber, numberWithUnsignedInteger: token.into_raw()]; -// let selector = sel!(handleTimer:); -// let view = self.nsview.load(); -// let timer: id = msg_send![nstimer, timerWithTimeInterval: ti target: view selector: selector userInfo: user_info repeats: NO]; -// let runloop: id = msg_send![class!(NSRunLoop), currentRunLoop]; -// let () = msg_send![runloop, addTimer: timer forMode: NSRunLoopCommonModes]; -// } -// token -// } - -// pub fn text(&self) -> PietText { -// let view = self.nsview.load(); -// unsafe { -// if let Some(view) = (*view).as_ref() { -// let state: *mut c_void = *view.get_ivar("viewState"); -// (*(state as *mut ViewState)).text.clone() -// } else { -// // this codepath should only happen during tests in druid, when view is nil -// PietText::new_with_unique_state() -// } -// } -// } - -// pub fn add_text_field(&self) -> TextFieldToken { -// TextFieldToken::next() -// } - -// pub fn remove_text_field(&self, token: TextFieldToken) { -// let view = self.nsview.load(); -// unsafe { -// if let Some(view) = (*view).as_ref() { -// let state: *mut c_void = *view.get_ivar("viewState"); -// let state = &mut (*(state as *mut ViewState)); -// if state.active_text_input == Some(token) { -// state.active_text_input = None; -// } -// } -// } -// } - -// pub fn set_focused_text_field(&self, active_field: Option) { -// unsafe { -// if let Some(view) = self.nsview.load().as_ref() { -// let state: *mut c_void = *view.get_ivar("viewState"); -// let state = &mut (*(state as *mut ViewState)); - -// if let Some(old_field) = state.active_text_input { -// self.update_text_field(old_field, Event::Reset); -// } -// state.active_text_input = active_field; -// if let Some(new_field) = active_field { -// self.update_text_field(new_field, Event::Reset); -// } -// } -// } -// } - -// pub fn update_text_field(&self, token: TextFieldToken, update: Event) { -// unsafe { -// if let Some(view) = self.nsview.load().as_ref() { -// let state: *mut c_void = *view.get_ivar("viewState"); -// let state = &mut (*(state as *mut ViewState)); - -// if state.active_text_input != Some(token) { -// return; -// } -// match update { -// Event::LayoutChanged => { -// let input_context: id = msg_send![*self.nsview.load(), inputContext]; -// let _: () = msg_send![input_context, invalidateCharacterCoordinates]; -// } -// Event::Reset | Event::SelectionChanged => { -// let input_context: id = msg_send![*self.nsview.load(), inputContext]; -// let _: () = msg_send![input_context, discardMarkedText]; -// let mut edit_lock = state.handler.acquire_input_lock(token, true); -// edit_lock.set_composition_range(None); -// state.handler.release_input_lock(token); -// } -// } -// } -// } -// } - -// pub fn open_file(&mut self, options: FileDialogOptions) -> Option { -// Some(self.open_save_impl(FileDialogType::Open, options)) -// } - -// pub fn save_as(&mut self, options: FileDialogOptions) -> Option { -// Some(self.open_save_impl(FileDialogType::Save, options)) -// } - -// fn open_save_impl(&mut self, ty: FileDialogType, opts: FileDialogOptions) -> FileDialogToken { -// let token = FileDialogToken::next(); -// let self_clone = self.clone(); -// unsafe { -// let panel = dialog::build_panel(ty, opts.clone()); -// let block = ConcreteBlock::new(move |response: dialog::NSModalResponse| { -// let url = dialog::get_file_info(panel, opts.clone(), response); -// let view = self_clone.nsview.load(); -// if let Some(view) = (*view).as_ref() { -// let view_state: *mut c_void = *view.get_ivar("viewState"); -// let view_state = &mut *(view_state as *mut ViewState); -// if ty == FileDialogType::Open { -// (*view_state).handler.open_file(token, url); -// } else if ty == FileDialogType::Save { -// (*view_state).handler.save_as(token, url); -// } -// } -// }); -// let block = block.copy(); -// let () = msg_send![panel, beginWithCompletionHandler: block]; -// } -// token -// } - -// /// Set the title for this menu. -// pub fn set_title(&self, title: &str) { -// unsafe { -// let window: id = msg_send![*self.nsview.load(), window]; -// let title = make_nsstring(title); -// window.setTitle_(title); -// } -// } - -// // TODO: Implement this -// pub fn show_titlebar(&self, _show_titlebar: bool) {} - -// // Need to translate mac y coords, as they start from bottom left -// pub fn set_position(&self, mut position: Point) { -// // TODO: Maybe @cmyr can get this into a state where modal windows follow the parent? -// // There is an API to do child windows, (https://developer.apple.com/documentation/appkit/nswindow/1419152-addchildwindow) -// // but I have no good way of testing and making sure this works. -// unsafe { -// if let Some(view) = self.nsview.load().as_ref() { -// let state: *mut c_void = *view.get_ivar("viewState"); -// let state = &mut (*(state as *mut ViewState)); -// if let Some(parent_state) = &state.parent { -// let pos = (*parent_state).get_position(); -// position += (pos.x, pos.y) -// } -// } -// } - -// self.defer(DeferredOp::SetPosition(position)) -// } - -// pub fn get_position(&self) -> Point { -// unsafe { -// // TODO this should be the max y in orig mac coords -// let screen_height = crate::Screen::get_display_rect().height(); - -// let window: id = msg_send![*self.nsview.load(), window]; -// let current_frame: NSRect = msg_send![window, frame]; - -// let mut position = Point::new( -// current_frame.origin.x, -// screen_height - current_frame.origin.y - current_frame.size.height, -// ); -// if let Some(view) = self.nsview.load().as_ref() { -// let state: *mut c_void = *view.get_ivar("viewState"); -// let state = &mut (*(state as *mut ViewState)); -// if let Some(parent_state) = &state.parent { -// let pos = (*parent_state).get_position(); -// position -= (pos.x, pos.y) -// } -// } -// position -// } -// } - -// pub fn content_insets(&self) -> Insets { -// unsafe { -// let screen_height = crate::Screen::get_display_rect().height(); - -// let window: id = msg_send![*self.nsview.load(), window]; -// let clr: NSRect = msg_send![window, contentLayoutRect]; - -// let window_frame_r: NSRect = NSWindow::frame(window); -// let content_frame_r: NSRect = NSWindow::convertRectToScreen_(window, clr); - -// let window_frame_rk = Rect::from_origin_size( -// ( -// window_frame_r.origin.x, -// screen_height - window_frame_r.origin.y - window_frame_r.size.height, -// ), -// (window_frame_r.size.width, window_frame_r.size.height), -// ); -// let content_frame_rk = Rect::from_origin_size( -// ( -// content_frame_r.origin.x, -// screen_height - content_frame_r.origin.y - content_frame_r.size.height, -// ), -// (content_frame_r.size.width, content_frame_r.size.height), -// ); -// window_frame_rk - content_frame_rk -// } -// } - -// fn set_level(&self, level: WindowLevel) { -// unsafe { -// let level = levels::as_raw_window_level(level); -// let window: id = msg_send![*self.nsview.load(), window]; -// let () = msg_send![window, setLevel: level]; -// } -// } - -// pub fn set_size(&self, size: Size) { -// self.defer(DeferredOp::SetSize(size)); -// } - -// pub fn get_size(&self) -> Size { -// unsafe { -// let window: id = msg_send![*self.nsview.load(), window]; -// let current_frame: NSRect = msg_send![window, frame]; -// Size::new(current_frame.size.width, current_frame.size.height) -// } -// } - -// pub fn get_window_state(&self) -> WindowState { -// unsafe { -// let window: id = msg_send![*self.nsview.load(), window]; -// let isMin: BOOL = msg_send![window, isMiniaturized]; -// if isMin != NO { -// return WindowState::Minimized; -// } -// let isZoomed: BOOL = msg_send![window, isZoomed]; -// if isZoomed != NO { -// return WindowState::Maximized; -// } -// } -// WindowState::Restored -// } - -// pub fn set_window_state(&mut self, state: WindowState) { -// let cur_state = self.get_window_state(); -// unsafe { -// let window: id = msg_send![*self.nsview.load(), window]; -// match (state, cur_state) { -// (s1, s2) if s1 == s2 => (), -// (WindowState::Minimized, _) => { -// let () = msg_send![window, performMiniaturize: self]; -// } -// (WindowState::Maximized, _) => { -// let () = msg_send![window, performZoom: self]; -// } -// (WindowState::Restored, WindowState::Maximized) => { -// let () = msg_send![window, performZoom: self]; -// } -// (WindowState::Restored, WindowState::Minimized) => { -// let () = msg_send![window, deminiaturize: self]; -// } -// (WindowState::Restored, WindowState::Restored) => {} // Can't be reached -// } -// } -// } - -// pub fn handle_titlebar(&self, _val: bool) { -// tracing::warn!("WindowHandle::handle_titlebar is currently unimplemented for Mac."); -// } - -// pub fn resizable(&self, resizable: bool) { -// unsafe { -// let window: id = msg_send![*self.nsview.load(), window]; -// let mut style_mask: NSWindowStyleMask = window.styleMask(); - -// if resizable { -// style_mask |= NSWindowStyleMask::NSResizableWindowMask; -// } else { -// style_mask &= !NSWindowStyleMask::NSResizableWindowMask; -// } - -// window.setStyleMask_(style_mask); -// } -// } - -// pub fn set_menu(&self, menu: Menu) { -// unsafe { -// NSApp().setMainMenu_(menu.menu); -// } -// } - -// //FIXME: we should be using the x, y values passed by the caller, but then -// //we have to figure out some way to pass them along with this performSelector: -// //call. This isn't super hard, I'm just not up for it right now. -// pub fn show_context_menu(&self, menu: Menu, _pos: Point) { -// unsafe { -// let () = msg_send![*self.nsview.load(), performSelectorOnMainThread: sel!(showContextMenu:) withObject: menu.menu waitUntilDone: NO]; -// } -// } - -// fn defer(&self, op: DeferredOp) { -// if let Some(i) = self.get_idle_handle() { -// i.add_idle(IdleKind::DeferredOp(op)) -// } -// } - -// /// Get a handle that can be used to schedule an idle task. -// pub fn get_idle_handle(&self) -> Option { -// if self.nsview.load().is_null() { -// None -// } else { -// Some(IdleHandle { -// nsview: self.nsview.clone(), -// idle_queue: self.idle_queue.clone(), -// }) -// } -// } - -// /// Get the `Scale` of the window. -// pub fn get_scale(&self) -> Result { -// // TODO: Get actual Scale -// Ok(Scale::new(1.0, 1.0)) -// } -// } - -// #[cfg(feature = "raw-win-handle")] -// unsafe impl HasRawWindowHandle for WindowHandle { -// fn raw_window_handle(&self) -> RawWindowHandle { -// let nsv = self.nsview.load(); -// let handle = MacOSHandle { -// ns_view: *nsv as *mut _, -// ..MacOSHandle::empty() -// }; -// RawWindowHandle::MacOS(handle) -// } -// } +impl WindowHandle { + pub fn show(&self) { + unimplemented!() + // unsafe { + // let window: id = msg_send![*self.nsview.load(), window]; + // // register our view class to be alerted when it becomes the key view. + // let notif_center_class = class!(NSNotificationCenter); + // let notif_string = NSString::alloc(nil) + // .init_str(NSWindowDidBecomeKeyNotification) + // .autorelease(); + // let notif_center: id = msg_send![notif_center_class, defaultCenter]; + // let () = msg_send![notif_center, addObserver:*self.nsview.load() selector: sel!(windowDidBecomeKey:) name: notif_string object: window]; + // window.makeKeyAndOrderFront_(nil) + // } + } + + /// Close the window. + pub fn close(&self) { + unimplemented!() + // unsafe { + // let window: id = msg_send![*self.nsview.load(), window]; + // let () = msg_send![window, performSelectorOnMainThread: sel!(close) withObject: nil waitUntilDone: NO]; + // } + } + + /// Bring this window to the front of the window stack and give it focus. + pub fn bring_to_front_and_focus(&self) { + unimplemented!() + // unsafe { + // let window: id = msg_send![*self.nsview.load(), window]; + // let () = msg_send![window, performSelectorOnMainThread: sel!(makeKeyAndOrderFront:) withObject: nil waitUntilDone: NO]; + // } + } + + /// Bring this window to the front of the window stack and give it focus. + pub fn request_anim_frame(&self) { + unimplemented!() + // unsafe { + // // TODO: synchronize with screen refresh rate using CVDisplayLink instead. + // let () = msg_send![*self.nsview.load(), performSelectorOnMainThread: sel!(redraw) + // withObject: nil waitUntilDone: NO]; + // } + } + + // // Request invalidation of the entire window contents. + pub fn invalidate(&self) { + unimplemented!() + // unsafe { + // // We could share impl with redraw, but we'd need to deal with nil. + // let () = msg_send![*self.nsview.load(), setNeedsDisplay: YES]; + // } + } + + // /// Request invalidation of one rectangle. + pub fn invalidate_rect(&self, rect: Rect) { + unimplemented!() + // let rect = NSRect::new( + // NSPoint::new(rect.x0, rect.y0), + // NSSize::new(rect.width(), rect.height()), + // ); + // unsafe { + // // We could share impl with redraw, but we'd need to deal with nil. + // let () = msg_send![*self.nsview.load(), setNeedsDisplayInRect: rect]; + // } + } + + pub fn set_cursor(&mut self, cursor: &Cursor) { + unimplemented!() + // unsafe { + // let nscursor = class!(NSCursor); + // #[allow(deprecated)] + // let cursor: id = match cursor { + // Cursor::Arrow => msg_send![nscursor, arrowCursor], + // Cursor::IBeam => msg_send![nscursor, IBeamCursor], + // Cursor::Pointer => msg_send![nscursor, pointingHandCursor], + // Cursor::Crosshair => msg_send![nscursor, crosshairCursor], + // Cursor::OpenHand => msg_send![nscursor, openHandCursor], + // Cursor::NotAllowed => msg_send![nscursor, operationNotAllowedCursor], + // Cursor::ResizeLeftRight => msg_send![nscursor, resizeLeftRightCursor], + // Cursor::ResizeUpDown => msg_send![nscursor, resizeUpDownCursor], + // // TODO: support custom cursors + // Cursor::Custom(_) => msg_send![nscursor, arrowCursor], + // }; + // let () = msg_send![cursor, set]; + // } + } + + pub fn make_cursor(&self, _cursor_desc: &CursorDesc) -> Option { + // we should probably support this though?? + tracing::warn!("Custom cursors are not yet supported in the iOS backend"); + None + } + + pub fn request_timer(&self, deadline: std::time::Instant) -> TimerToken { + unimplemented!() + // let ti = time_interval_from_deadline(deadline); + // let token = TimerToken::next(); + // unsafe { + // let nstimer = class!(NSTimer); + // let nsnumber = class!(NSNumber); + // let user_info: id = msg_send![nsnumber, numberWithUnsignedInteger: token.into_raw()]; + // let selector = sel!(handleTimer:); + // let view = self.nsview.load(); + // let timer: id = msg_send![nstimer, timerWithTimeInterval: ti target: view selector: selector userInfo: user_info repeats: NO]; + // let runloop: id = msg_send![class!(NSRunLoop), currentRunLoop]; + // let () = msg_send![runloop, addTimer: timer forMode: NSRunLoopCommonModes]; + // } + // token + } + + pub fn text(&self) -> PietText { + unimplemented!() + // let view = self.nsview.load(); + // unsafe { + // if let Some(view) = (*view).as_ref() { + // let state: *mut c_void = *view.get_ivar("viewState"); + // (*(state as *mut ViewState)).text.clone() + // } else { + // // this codepath should only happen during tests in druid, when view is nil + // PietText::new_with_unique_state() + // } + // } + } + + pub fn add_text_field(&self) -> TextFieldToken { + unimplemented!() + // TextFieldToken::next() + } + + pub fn remove_text_field(&self, token: TextFieldToken) { + unimplemented!() + // let view = self.nsview.load(); + // unsafe { + // if let Some(view) = (*view).as_ref() { + // let state: *mut c_void = *view.get_ivar("viewState"); + // let state = &mut (*(state as *mut ViewState)); + // if state.active_text_input == Some(token) { + // state.active_text_input = None; + // } + // } + // } + } + + pub fn set_focused_text_field(&self, active_field: Option) { + unimplemented!() + // unsafe { + // if let Some(view) = self.nsview.load().as_ref() { + // let state: *mut c_void = *view.get_ivar("viewState"); + // let state = &mut (*(state as *mut ViewState)); + + // if let Some(old_field) = state.active_text_input { + // self.update_text_field(old_field, Event::Reset); + // } + // state.active_text_input = active_field; + // if let Some(new_field) = active_field { + // self.update_text_field(new_field, Event::Reset); + // } + // } + // } + } + + pub fn update_text_field(&self, token: TextFieldToken, update: Event) { + unimplemented!() + // unsafe { + // if let Some(view) = self.nsview.load().as_ref() { + // let state: *mut c_void = *view.get_ivar("viewState"); + // let state = &mut (*(state as *mut ViewState)); + + // if state.active_text_input != Some(token) { + // return; + // } + // match update { + // Event::LayoutChanged => { + // let input_context: id = msg_send![*self.nsview.load(), inputContext]; + // let _: () = msg_send![input_context, invalidateCharacterCoordinates]; + // } + // Event::Reset | Event::SelectionChanged => { + // let input_context: id = msg_send![*self.nsview.load(), inputContext]; + // let _: () = msg_send![input_context, discardMarkedText]; + // let mut edit_lock = state.handler.acquire_input_lock(token, true); + // edit_lock.set_composition_range(None); + // state.handler.release_input_lock(token); + // } + // } + // } + // } + } + + pub fn open_file(&mut self, options: FileDialogOptions) -> Option { + unimplemented!() + // Some(self.open_save_impl(FileDialogType::Open, options)) + } + + pub fn save_as(&mut self, options: FileDialogOptions) -> Option { + unimplemented!() + // Some(self.open_save_impl(FileDialogType::Save, options)) + } + + // fn open_save_impl(&mut self, ty: FileDialogType, opts: FileDialogOptions) -> FileDialogToken { + // let token = FileDialogToken::next(); + // let self_clone = self.clone(); + // unsafe { + // let panel = dialog::build_panel(ty, opts.clone()); + // let block = ConcreteBlock::new(move |response: dialog::NSModalResponse| { + // let url = dialog::get_file_info(panel, opts.clone(), response); + // let view = self_clone.nsview.load(); + // if let Some(view) = (*view).as_ref() { + // let view_state: *mut c_void = *view.get_ivar("viewState"); + // let view_state = &mut *(view_state as *mut ViewState); + // if ty == FileDialogType::Open { + // (*view_state).handler.open_file(token, url); + // } else if ty == FileDialogType::Save { + // (*view_state).handler.save_as(token, url); + // } + // } + // }); + // let block = block.copy(); + // let () = msg_send![panel, beginWithCompletionHandler: block]; + // } + // token + // } + + /// Set the title for this menu. + pub fn set_title(&self, title: &str) { + unimplemented!() + // unsafe { + // let window: id = msg_send![*self.nsview.load(), window]; + // let title = make_nsstring(title); + // window.setTitle_(title); + // } + } + + // TODO: Implement this + pub fn show_titlebar(&self, _show_titlebar: bool) { + unimplemented!() + } + + // Need to translate mac y coords, as they start from bottom left + pub fn set_position(&self, mut position: Point) { + unimplemented!() + // unsafe { + // if let Some(view) = self.nsview.load().as_ref() { + // let state: *mut c_void = *view.get_ivar("viewState"); + // let state = &mut (*(state as *mut ViewState)); + // if let Some(parent_state) = &state.parent { + // let pos = (*parent_state).get_position(); + // position += (pos.x, pos.y) + // } + // } + // } + + // self.defer(DeferredOp::SetPosition(position)) + } + + pub fn get_position(&self) -> Point { + unimplemented!() + // unsafe { + // // TODO this should be the max y in orig mac coords + // let screen_height = crate::Screen::get_display_rect().height(); + + // let window: id = msg_send![*self.nsview.load(), window]; + // let current_frame: NSRect = msg_send![window, frame]; + + // let mut position = Point::new( + // current_frame.origin.x, + // screen_height - current_frame.origin.y - current_frame.size.height, + // ); + // if let Some(view) = self.nsview.load().as_ref() { + // let state: *mut c_void = *view.get_ivar("viewState"); + // let state = &mut (*(state as *mut ViewState)); + // if let Some(parent_state) = &state.parent { + // let pos = (*parent_state).get_position(); + // position -= (pos.x, pos.y) + // } + // } + // position + // } + } + + pub fn content_insets(&self) -> Insets { + unimplemented!() + // unsafe { + // let screen_height = crate::Screen::get_display_rect().height(); + + // let window: id = msg_send![*self.nsview.load(), window]; + // let clr: NSRect = msg_send![window, contentLayoutRect]; + + // let window_frame_r: NSRect = NSWindow::frame(window); + // let content_frame_r: NSRect = NSWindow::convertRectToScreen_(window, clr); + + // let window_frame_rk = Rect::from_origin_size( + // ( + // window_frame_r.origin.x, + // screen_height - window_frame_r.origin.y - window_frame_r.size.height, + // ), + // (window_frame_r.size.width, window_frame_r.size.height), + // ); + // let content_frame_rk = Rect::from_origin_size( + // ( + // content_frame_r.origin.x, + // screen_height - content_frame_r.origin.y - content_frame_r.size.height, + // ), + // (content_frame_r.size.width, content_frame_r.size.height), + // ); + // window_frame_rk - content_frame_rk + // } + } + + pub fn set_size(&self, size: Size) { + unimplemented!() + // self.defer(DeferredOp::SetSize(size)); + } + + pub fn get_size(&self) -> Size { + unimplemented!() + // unsafe { + // let window: id = msg_send![*self.nsview.load(), window]; + // let current_frame: NSRect = msg_send![window, frame]; + // Size::new(current_frame.size.width, current_frame.size.height) + // } + } + + pub fn get_window_state(&self) -> WindowState { + unimplemented!() + // unsafe { + // let window: id = msg_send![*self.nsview.load(), window]; + // let isMin: BOOL = msg_send![window, isMiniaturized]; + // if isMin != NO { + // return WindowState::Minimized; + // } + // let isZoomed: BOOL = msg_send![window, isZoomed]; + // if isZoomed != NO { + // return WindowState::Maximized; + // } + // } + // WindowState::Restored + } + + pub fn set_window_state(&mut self, state: WindowState) { + unimplemented!() + // let cur_state = self.get_window_state(); + // unsafe { + // let window: id = msg_send![*self.nsview.load(), window]; + // match (state, cur_state) { + // (s1, s2) if s1 == s2 => (), + // (WindowState::Minimized, _) => { + // let () = msg_send![window, performMiniaturize: self]; + // } + // (WindowState::Maximized, _) => { + // let () = msg_send![window, performZoom: self]; + // } + // (WindowState::Restored, WindowState::Maximized) => { + // let () = msg_send![window, performZoom: self]; + // } + // (WindowState::Restored, WindowState::Minimized) => { + // let () = msg_send![window, deminiaturize: self]; + // } + // (WindowState::Restored, WindowState::Restored) => {} // Can't be reached + // } + // } + } + + pub fn handle_titlebar(&self, _val: bool) { + unimplemented!() + // tracing::warn!("WindowHandle::handle_titlebar is currently unimplemented for Mac."); + } + + pub fn resizable(&self, resizable: bool) { + unimplemented!() + // unsafe { + // let window: id = msg_send![*self.nsview.load(), window]; + // let mut style_mask: NSWindowStyleMask = window.styleMask(); + + // if resizable { + // style_mask |= NSWindowStyleMask::NSResizableWindowMask; + // } else { + // style_mask &= !NSWindowStyleMask::NSResizableWindowMask; + // } + + // window.setStyleMask_(style_mask); + // } + } + + pub fn set_menu(&self, menu: Menu) { + unimplemented!() + // unsafe { + // NSApp().setMainMenu_(menu.menu); + // } + } + + //FIXME: we should be using the x, y values passed by the caller, but then + //we have to figure out some way to pass them along with this performSelector: + //call. This isn't super hard, I'm just not up for it right now. + pub fn show_context_menu(&self, menu: Menu, _pos: Point) { + unimplemented!() + // unsafe { + // let () = msg_send![*self.nsview.load(), performSelectorOnMainThread: sel!(showContextMenu:) withObject: menu.menu waitUntilDone: NO]; + // } + } + + fn defer(&self, op: DeferredOp) { + // if let Some(i) = self.get_idle_handle() { + // i.add_idle(IdleKind::DeferredOp(op)) + // } + } + + /// Get a handle that can be used to schedule an idle task. + pub fn get_idle_handle(&self) -> Option { + unimplemented!() + // if self.nsview.load().is_null() { + // None + // } else { + // Some(IdleHandle { + // nsview: self.nsview.clone(), + // idle_queue: self.idle_queue.clone(), + // }) + // } + } + + /// Get the `Scale` of the window. + pub fn get_scale(&self) -> Result { + unimplemented!() + // TODO: Get actual Scale + // Ok(Scale::new(1.0, 1.0)) + } +} unsafe impl Send for IdleHandle {} -// impl IdleHandle { -// fn add_idle(&self, idle: IdleKind) { -// if let Some(queue) = self.idle_queue.upgrade() { -// let mut queue = queue.lock().expect("queue lock"); -// if queue.is_empty() { -// unsafe { -// let nsview = self.nsview.load(); -// // Note: the nsview might be nil here if the window has been dropped, but that's ok. -// let () = msg_send!(*nsview, performSelectorOnMainThread: sel!(runIdle) -// withObject: nil waitUntilDone: NO); -// } -// } -// queue.push(idle); -// } -// } - -// /// Add an idle handler, which is called (once) when the message loop -// /// is empty. The idle handler will be run from the main UI thread, and -// /// won't be scheduled if the associated view has been dropped. -// /// -// /// Note: the name "idle" suggests that it will be scheduled with a lower -// /// priority than other UI events, but that's not necessarily the case. -// pub fn add_idle_callback(&self, callback: F) -// where -// F: FnOnce(&mut dyn WinHandler) + Send + 'static, -// { -// self.add_idle(IdleKind::Callback(Box::new(callback))); -// } - -// pub fn add_idle_token(&self, token: IdleToken) { -// self.add_idle(IdleKind::Token(token)); -// } -// } +impl IdleHandle { + fn add_idle(&self, idle: IdleKind) { + if let Some(queue) = self.idle_queue.upgrade() { + let mut queue = queue.lock().expect("queue lock"); + if queue.is_empty() { + unsafe { + unimplemented!() + // let nsview = self.nsview.load(); + // // Note: the nsview might be nil here if the window has been dropped, but that's ok. + // let () = msg_send!(*nsview, performSelectorOnMainThread: sel!(runIdle) + // withObject: nil waitUntilDone: NO); + } + } + queue.push(idle); + } + } + + /// Add an idle handler, which is called (once) when the message loop + /// is empty. The idle handler will be run from the main UI thread, and + /// won't be scheduled if the associated view has been dropped. + /// + /// Note: the name "idle" suggests that it will be scheduled with a lower + /// priority than other UI events, but that's not necessarily the case. + pub fn add_idle_callback(&self, callback: F) + where + F: FnOnce(&mut dyn WinHandler) + Send + 'static, + { + self.add_idle(IdleKind::Callback(Box::new(callback))); + } + + pub fn add_idle_token(&self, token: IdleToken) { + self.add_idle(IdleKind::Token(token)); + } +} // /// Convert an `Instant` into an NSTimeInterval, i.e. a fractional number // /// of seconds from now. From f58e715dcb3f2bab81954c4c6a03056a2d9ce0c1 Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Sat, 15 Jan 2022 07:48:48 -0800 Subject: [PATCH 12/13] fix druid example for new piet --- druid/examples/sub_window.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/druid/examples/sub_window.rs b/druid/examples/sub_window.rs index 2fd3e0b5fc..dbed02bdfe 100644 --- a/druid/examples/sub_window.rs +++ b/druid/examples/sub_window.rs @@ -29,6 +29,7 @@ use druid_shell::piet::Text; use druid_shell::{Screen, WindowLevel}; use instant::{Duration, Instant}; use piet_common::{TextLayout, TextLayoutBuilder}; +use piet_common::{TextLayout, TextLayoutBuilder}; const VERTICAL_WIDGET_SPACING: f64 = 20.0; const TEXT_BOX_WIDTH: f64 = 200.0; From 94dea47c93aef6503cde542d3e679de04b2e435b Mon Sep 17 00:00:00 2001 From: Robert Lord Date: Sat, 15 Jan 2022 07:51:01 -0800 Subject: [PATCH 13/13] fix druid piet-common version --- druid/Cargo.toml | 2 +- druid/examples/sub_window.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/druid/Cargo.toml b/druid/Cargo.toml index 3b0b7c276c..78d0a5d354 100644 --- a/druid/Cargo.toml +++ b/druid/Cargo.toml @@ -80,7 +80,7 @@ console_error_panic_hook = { version = "0.1.6" } float-cmp = { version = "0.8.0", features = ["std"], default-features = false } # tempfile 3.2.0 broke wasm; I assume it will be yanked (Jan 12, 2021) tempfile = "=3.1.0" -piet-common = { version = "=0.5.0", features = ["png"] } +piet-common = { git = "https://github.com/linebender/piet", rev = "57cc49a8a88703b1c754a8c2c7f6f96007a24d4d", features = ["png"] } pulldown-cmark = { version = "0.8", default-features = false } test-log = { version = "0.2.5", features = ["trace"], default-features = false } # test-env-log needs it diff --git a/druid/examples/sub_window.rs b/druid/examples/sub_window.rs index dbed02bdfe..2fd3e0b5fc 100644 --- a/druid/examples/sub_window.rs +++ b/druid/examples/sub_window.rs @@ -29,7 +29,6 @@ use druid_shell::piet::Text; use druid_shell::{Screen, WindowLevel}; use instant::{Duration, Instant}; use piet_common::{TextLayout, TextLayoutBuilder}; -use piet_common::{TextLayout, TextLayoutBuilder}; const VERTICAL_WIDGET_SPACING: f64 = 20.0; const TEXT_BOX_WIDTH: f64 = 200.0;