Merge pull request #29
Merge `cargo fmt` work from @madsmtm (https://github.com/ryanmcgrath/cacao/pull/29). See context for merge oddities here: https://github.com/ryanmcgrath/cacao/pull/33
This commit is contained in:
commit
0294647683
23
.github/workflows/ci.yml
vendored
Normal file
23
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
fmt:
|
||||
name: Check formatting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: rustfmt
|
||||
override: true
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: -- --check
|
|
@ -1,9 +1,9 @@
|
|||
# Cacao Architecture
|
||||
Cacao is a library to interface with AppKit (macOS) or UIKit (iOS/iPadOS/tvOS). It uses the Objective-C runtime to
|
||||
Cacao is a library to interface with AppKit (macOS) or UIKit (iOS/iPadOS/tvOS). It uses the Objective-C runtime to
|
||||
handle calling into these frameworks.
|
||||
|
||||
Said frameworks typically use an Object Oriented style of programming (subclasses, etc), which can be tricky to
|
||||
handle with the way that Rust works with regards to ownership. Thankfully, AppKit & UIKit often also use a
|
||||
Said frameworks typically use an Object Oriented style of programming (subclasses, etc), which can be tricky to
|
||||
handle with the way that Rust works with regards to ownership. Thankfully, AppKit & UIKit often also use a
|
||||
delegate pattern - objects registered to receive callbacks. With some creative assumptions, we can get somewhat close
|
||||
to expected conventions.
|
||||
|
||||
|
@ -208,7 +208,7 @@ impl View {
|
|||
height: LayoutAnchorDimension::height(view),
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
|
||||
|
||||
layer: Layer::wrap(unsafe {
|
||||
msg_send![view, layer]
|
||||
}),
|
||||
|
@ -236,7 +236,7 @@ impl<T> View<T> where T: ViewDelegate + 'static {
|
|||
pub fn with(delegate: T) -> View<T> {
|
||||
let class = register_view_class_with_delegate(&delegate);
|
||||
let mut delegate = Box::new(delegate);
|
||||
|
||||
|
||||
let view = unsafe {
|
||||
let view: id = msg_send![class, new];
|
||||
let ptr = Box::into_raw(delegate);
|
||||
|
@ -246,7 +246,7 @@ impl<T> View<T> where T: ViewDelegate + 'static {
|
|||
};
|
||||
|
||||
let mut view = View::init(view);
|
||||
(&mut delegate).did_load(view.clone_as_handle());
|
||||
(&mut delegate).did_load(view.clone_as_handle());
|
||||
view.delegate = Some(delegate);
|
||||
view
|
||||
}
|
||||
|
@ -360,10 +360,10 @@ pub(crate) fn register_view_class() -> *const Class {
|
|||
let superclass = class!(NSView);
|
||||
let mut decl = ClassDecl::new("RSTView", superclass).unwrap();
|
||||
|
||||
decl.add_method(sel!(isFlipped), enforce_normalcy as extern fn(&Object, _) -> BOOL);
|
||||
decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
||||
|
||||
decl.add_ivar::<id>(BACKGROUND_COLOR);
|
||||
|
||||
|
||||
VIEW_CLASS = decl.register();
|
||||
});
|
||||
|
||||
|
@ -371,7 +371,7 @@ pub(crate) fn register_view_class() -> *const Class {
|
|||
}
|
||||
```
|
||||
|
||||
This function (called inside `View::new()`) creates one reusable `View` subclass, and returns the type on subsequent calls. We're able to add methods to it (`add_method`) which match
|
||||
This function (called inside `View::new()`) creates one reusable `View` subclass, and returns the type on subsequent calls. We're able to add methods to it (`add_method`) which match
|
||||
Objective-C method signatures, as well as provision space for variable storage (`add_ivar`).
|
||||
|
||||
For our _delegate_ types, we need a different class creation method - one that creates a subclass per-unique-type:
|
||||
|
@ -384,12 +384,12 @@ pub(crate) fn register_view_class_with_delegate<T: ViewDelegate>(instance: &T) -
|
|||
|
||||
decl.add_method(
|
||||
sel!(isFlipped),
|
||||
enforce_normalcy as extern fn(&Object, _) -> BOOL
|
||||
enforce_normalcy as extern "C" fn(&Object, _) -> BOOL
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(draggingEntered:),
|
||||
dragging_entered::<T> as extern fn (&mut Object, _, _) -> NSUInteger
|
||||
dragging_entered::<T> as extern "C" fn (&mut Object, _, _) -> NSUInteger
|
||||
);
|
||||
})
|
||||
}
|
||||
|
@ -401,7 +401,7 @@ to the Rust `ViewDelegate` implementation.
|
|||
The methods we're setting up can range from simple to complex - take `isFlipped`:
|
||||
|
||||
``` rust
|
||||
extern fn is_flipped(_: &Object, _: Sel) -> BOOL {
|
||||
extern "C" fn is_flipped(_: &Object, _: Sel) -> BOOL {
|
||||
return YES;
|
||||
}
|
||||
```
|
||||
|
@ -409,7 +409,7 @@ extern fn is_flipped(_: &Object, _: Sel) -> BOOL {
|
|||
Here, we just want to tell `NSView` to use top,left as the origin point, so we need to respond `YES` in this subclass method.
|
||||
|
||||
``` rust
|
||||
extern fn dragging_entered<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) -> NSUInteger {
|
||||
extern "C" fn dragging_entered<T: ViewDelegate>(this: &mut Object, _: Sel, info: id) -> NSUInteger {
|
||||
let view = utils::load::<T>(this, VIEW_DELEGATE_PTR);
|
||||
view.dragging_entered(DragInfo {
|
||||
info: unsafe { Id::from_ptr(info) }
|
||||
|
|
|
@ -99,5 +99,5 @@ Check out [their README](https://github.com/rust-lang-nursery/rustfmt) for detai
|
|||
|
||||
|
||||
### Notes
|
||||
This project prefers verbose naming, to a certain degree - UI code is read more often than written, so it's
|
||||
This project prefers verbose naming, to a certain degree - UI code is read more often than written, so it's
|
||||
worthwhile to ensure that it scans well. It also maps well to existing Cocoa/cacao idioms and is generally preferred.
|
||||
|
|
30
README.md
30
README.md
|
@ -10,17 +10,17 @@ inform development. That said, this library is currently early stages and may ha
|
|||
your own risk. However, provided you follow the rules (regarding memory/ownership) it's
|
||||
already fine for some apps. The core repository has a wealth of examples to help you get started.
|
||||
|
||||
> **Important**
|
||||
>
|
||||
> If you are migrating from 0.2 to 0.3, you should elect either `appkit` or `uikit` as a feature in your `Cargo.toml`. This change was made to
|
||||
> **Important**
|
||||
>
|
||||
> If you are migrating from 0.2 to 0.3, you should elect either `appkit` or `uikit` as a feature in your `Cargo.toml`. This change was made to
|
||||
> support platforms that aren't just macOS/iOS/tvOS (e.g, gnustep, airyx). One of these features is required to work; `appkit` is defaulted for
|
||||
> ease of development.
|
||||
|
||||
>_Note that this crate relies on the Objective-C runtime. Interfacing with the runtime **requires**
|
||||
unsafe blocks; this crate handles those unsafe interactions for you and provides a safe wrapper,
|
||||
but by using this crate you understand that usage of `unsafe` is a given and will be somewhat
|
||||
rampant for wrapped controls. This does **not** mean you can't assess, review, or question unsafe
|
||||
usage - just know it's happening, and in large part it's not going away. Issues pertaining to the mere
|
||||
unsafe blocks; this crate handles those unsafe interactions for you and provides a safe wrapper,
|
||||
but by using this crate you understand that usage of `unsafe` is a given and will be somewhat
|
||||
rampant for wrapped controls. This does **not** mean you can't assess, review, or question unsafe
|
||||
usage - just know it's happening, and in large part it's not going away. Issues pertaining to the mere
|
||||
existence of unsafe will be closed without comment._
|
||||
|
||||
If you're looking to build the docs for this on your local machine, you'll want the following due to the way feature flags work
|
||||
|
@ -108,17 +108,17 @@ The following are a list of [Cargo features][cargo-features] that can be enabled
|
|||
[cargo-features]: https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-features-section
|
||||
|
||||
## General Notes
|
||||
**Why not extend the existing cocoa-rs crate?**
|
||||
**Why not extend the existing cocoa-rs crate?**
|
||||
A good question. At the end of the day, that crate (I believe, and someone can correct me if I'm wrong) is somewhat tied to Servo, and I wanted to experiment with what the best approach for representing the Cocoa UI model in Rust was. This crate doesn't ignore their work entirely, either - `core_foundation` and `core_graphics` are used internally and re-exported for general use.
|
||||
|
||||
**Why should I write in Rust, rather than X language?**
|
||||
**Why should I write in Rust, rather than X language?**
|
||||
In _my_ case, I want to be able to write native applications for my devices (and the platform I like to build products for) without being locked in to writing in Apple-specific languages... and without writing in C/C++ or JavaScript (note: the _toolchain_, not the language - ES6/Typescript are fine). I want to do this because I'm tired of hitting a mountain of work when I want to port my applications to other ecosystems. I think that Rust offers a (growing, but significant) viable model for sharing code across platforms and ecosystems without sacrificing performance.
|
||||
|
||||
_(This is the part where the internet lights up and rants about some combination of Electron, Qt, and so on - we're not bothering here as it's beaten to death elsewhere)_
|
||||
|
||||
This crate is useful for people who don't need to go all-in on the Apple ecosystem, but want to port their work there with some relative ease. It's not expected that everyone will suddenly want to rewrite their macOS/iOS/tvOS apps in Rust.
|
||||
|
||||
**Isn't Objective-C dead?**
|
||||
**Isn't Objective-C dead?**
|
||||
Yes, and no.
|
||||
|
||||
It's true that Apple definitely favors Swift, and for good reason (and I say this as an unabashed lover of Objective-C). With that said, I would be surprised if we didn't have another ~5+ years of support; Apple is quick to deprecate, but removing the Objective-C runtime would require a ton of time and effort. Maybe SwiftUI kills it, who knows. A wrapper around this stuff should conceivably make it easier to swap out the underlying UI backend whenever it comes time.
|
||||
|
@ -133,21 +133,21 @@ Some might also decry Objective-C as slow. To that, I'd note the following:
|
|||
|
||||
**tl;dr** it's probably fine, and you have Rust for your performance needs.
|
||||
|
||||
**Why not just wrap UIKit, and then rely on Catalyst?**
|
||||
**Why not just wrap UIKit, and then rely on Catalyst?**
|
||||
I have yet to see a single application where Catalyst felt good. The goal is good, though, and if it got to a point where that just seemed like the way forward (e.g, Apple just kills AppKit) then it's certainly an option.
|
||||
|
||||
**You can't possibly wrap all platform-specific behavior here...**
|
||||
**You can't possibly wrap all platform-specific behavior here...**
|
||||
Correct! Each UI control contains a `objc` field, which you can use as an escape hatch - if the control doesn't support something, you're free to drop to the Objective-C runtime yourself and handle it.
|
||||
|
||||
**Why don't you use bindings to automatically generate this stuff?**
|
||||
**Why don't you use bindings to automatically generate this stuff?**
|
||||
For initial exploration purposes I've done most of this by hand, as I wanted to find an approach that fit well in the Rust model before committing to binding generation. This is something I'll likely focus on next now that I've got things "working" well enough.
|
||||
|
||||
**Is this related to Cacao, the Swift project?**
|
||||
**Is this related to Cacao, the Swift project?**
|
||||
No. The project referred to in this question aimed to map portions of Cocoa and UIKit over to run on Linux, but hasn't seen activity in some time (it was really cool, too!).
|
||||
|
||||
Open source project naming in 2020 is like trying to buy a `.com` domain: everything good is taken. Luckily, multiple projects can share a name... so that's what's going to happen here.
|
||||
|
||||
**Isn't this kind of cheating the Rust object model?**
|
||||
**Isn't this kind of cheating the Rust object model?**
|
||||
Depends on how you look at it. I personally don't care too much - the GUI layer for these platforms is a hard requirement to support for certain classes of products, and giving them up also means giving up battle-tested tools for things like Accessibility and deeper OS integration. With that said, internally there are efforts to try and make things respect Rust's model of how things should work.
|
||||
|
||||
You can think of this as similar to gtk-rs. If you want to support or try a more _pure_ model, go check out Druid or something. :)
|
||||
|
|
8
build.rs
8
build.rs
|
@ -2,10 +2,10 @@
|
|||
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-lib=framework=Foundation");
|
||||
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
println!("cargo:rustc-link-lib=framework=AppKit");
|
||||
|
||||
|
||||
#[cfg(feature = "uikit")]
|
||||
println!("cargo:rustc-link-lib=framework=UIKit");
|
||||
|
||||
|
@ -15,13 +15,13 @@ fn main() {
|
|||
|
||||
#[cfg(feature = "webview")]
|
||||
println!("cargo:rustc-link-lib=framework=WebKit");
|
||||
|
||||
|
||||
#[cfg(feature = "cloudkit")]
|
||||
println!("cargo:rustc-link-lib=framework=CloudKit");
|
||||
|
||||
#[cfg(feature = "user-notifications")]
|
||||
println!("cargo:rustc-link-lib=framework=UserNotifications");
|
||||
|
||||
|
||||
#[cfg(feature = "quicklook")]
|
||||
println!("cargo:rustc-link-lib=framework=QuickLook");
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! This example builds on the AutoLayout example, but adds in animation
|
||||
//! This example builds on the AutoLayout example, but adds in animation
|
||||
//! via `AnimationContext`. Views and layout anchors have special proxy objects that can be cloned
|
||||
//! into handlers, enabling basic animation support within `AnimationContext`.
|
||||
//!
|
||||
|
@ -9,10 +9,10 @@ use cacao::color::Color;
|
|||
use cacao::layout::{Layout, LayoutConstraint, LayoutConstraintAnimatorProxy};
|
||||
use cacao::view::{View, ViewAnimatorProxy};
|
||||
|
||||
use cacao::appkit::{App, AppDelegate, AnimationContext};
|
||||
use cacao::appkit::{Event, EventMask, EventMonitor};
|
||||
use cacao::appkit::menu::Menu;
|
||||
use cacao::appkit::window::{Window, WindowConfig, WindowDelegate};
|
||||
use cacao::appkit::{AnimationContext, App, AppDelegate};
|
||||
use cacao::appkit::{Event, EventMask, EventMonitor};
|
||||
|
||||
struct BasicApp {
|
||||
window: Window<AppWindow>
|
||||
|
@ -54,33 +54,26 @@ const ANIMATIONS: [[[f64; 5]; 4]; 3] = [
|
|||
[44., 16., 100., 100., 1.],
|
||||
[128., 84., 144., 124., 1.],
|
||||
[32., 32., 44., 44., 0.7],
|
||||
[328., 157., 200., 200., 0.7],
|
||||
[328., 157., 200., 200., 0.7]
|
||||
],
|
||||
|
||||
// Red
|
||||
[
|
||||
[44., 132., 100., 100., 1.],
|
||||
[40., 47., 80., 64., 0.7],
|
||||
[84., 220., 600., 109., 1.0],
|
||||
[48., 600., 340., 44., 0.7],
|
||||
[48., 600., 340., 44., 0.7]
|
||||
],
|
||||
|
||||
// Green
|
||||
[
|
||||
[44., 248., 100., 100., 1.],
|
||||
[420., 232., 420., 244., 0.7],
|
||||
[310., 440., 150., 238., 0.7],
|
||||
[32., 32., 44., 44., 1.],
|
||||
[32., 32., 44., 44., 1.]
|
||||
]
|
||||
];
|
||||
|
||||
/// A helper method for generating frame constraints that we want to be animating.
|
||||
fn apply_styles(
|
||||
view: &View,
|
||||
parent: &View,
|
||||
background_color: Color,
|
||||
animation_table_index: usize
|
||||
) -> [LayoutConstraint; 4] {
|
||||
fn apply_styles(view: &View, parent: &View, background_color: Color, animation_table_index: usize) -> [LayoutConstraint; 4] {
|
||||
view.set_background_color(background_color);
|
||||
view.layer.set_corner_radius(16.);
|
||||
parent.add_subview(view);
|
||||
|
@ -117,22 +110,26 @@ impl WindowDelegate for AppWindow {
|
|||
let red_frame = apply_styles(&self.red, &self.content, Color::SystemRed, 1);
|
||||
let green_frame = apply_styles(&self.green, &self.content, Color::SystemGreen, 2);
|
||||
|
||||
let alpha_animators = [&self.blue, &self.red, &self.green].iter().map(|view| {
|
||||
view.animator.clone()
|
||||
}).collect::<Vec<ViewAnimatorProxy>>();
|
||||
let alpha_animators = [&self.blue, &self.red, &self.green]
|
||||
.iter()
|
||||
.map(|view| view.animator.clone())
|
||||
.collect::<Vec<ViewAnimatorProxy>>();
|
||||
|
||||
let constraint_animators = [blue_frame, red_frame, green_frame].iter().map(|frame| {
|
||||
LayoutConstraint::activate(frame);
|
||||
let constraint_animators = [blue_frame, red_frame, green_frame]
|
||||
.iter()
|
||||
.map(|frame| {
|
||||
LayoutConstraint::activate(frame);
|
||||
|
||||
vec![
|
||||
frame[0].animator.clone(),
|
||||
frame[1].animator.clone(),
|
||||
frame[2].animator.clone(),
|
||||
frame[3].animator.clone(),
|
||||
]
|
||||
}).collect::<Vec<Vec<LayoutConstraintAnimatorProxy>>>();
|
||||
vec![
|
||||
frame[0].animator.clone(),
|
||||
frame[1].animator.clone(),
|
||||
frame[2].animator.clone(),
|
||||
frame[3].animator.clone(),
|
||||
]
|
||||
})
|
||||
.collect::<Vec<Vec<LayoutConstraintAnimatorProxy>>>();
|
||||
|
||||
// Monitor key change events for w/a/s/d, and then animate each view to their correct
|
||||
// Monitor key change events for w/a/s/d, and then animate each view to their correct
|
||||
// frame and alpha value.
|
||||
self.key_monitor = Some(Event::local_monitor(EventMask::KeyDown, move |evt| {
|
||||
let characters = evt.characters();
|
||||
|
@ -175,5 +172,6 @@ impl WindowDelegate for AppWindow {
|
|||
fn main() {
|
||||
App::new("com.test.window", BasicApp {
|
||||
window: Window::with(WindowConfig::default(), AppWindow::default())
|
||||
}).run();
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ use cacao::color::{Color, Theme};
|
|||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::view::View;
|
||||
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
use cacao::appkit::menu::{Menu, MenuItem};
|
||||
use cacao::appkit::window::{Window, WindowConfig, WindowDelegate};
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
|
||||
struct BasicApp {
|
||||
window: Window<AppWindow>
|
||||
|
@ -23,23 +23,16 @@ impl AppDelegate for BasicApp {
|
|||
MenuItem::HideOthers,
|
||||
MenuItem::ShowAll,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Quit
|
||||
MenuItem::Quit,
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![
|
||||
MenuItem::CloseWindow
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![
|
||||
MenuItem::EnterFullScreen
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![MenuItem::CloseWindow]),
|
||||
Menu::new("View", vec![MenuItem::EnterFullScreen]),
|
||||
Menu::new("Window", vec![
|
||||
MenuItem::Minimize,
|
||||
MenuItem::Zoom,
|
||||
MenuItem::Separator,
|
||||
MenuItem::new("Bring All to Front")
|
||||
])
|
||||
MenuItem::new("Bring All to Front"),
|
||||
]),
|
||||
]);
|
||||
|
||||
App::activate();
|
||||
|
@ -89,16 +82,14 @@ impl WindowDelegate for AppWindow {
|
|||
self.blue.leading.constraint_equal_to(&self.content.leading).offset(16.),
|
||||
self.blue.bottom.constraint_equal_to(&self.content.bottom).offset(-16.),
|
||||
self.blue.width.constraint_equal_to_constant(100.),
|
||||
|
||||
self.red.top.constraint_equal_to(&self.content.top).offset(46.),
|
||||
self.red.leading.constraint_equal_to(&self.blue.trailing).offset(16.),
|
||||
self.red.bottom.constraint_equal_to(&self.content.bottom).offset(-16.),
|
||||
|
||||
self.green.top.constraint_equal_to(&self.content.top).offset(46.),
|
||||
self.green.leading.constraint_equal_to(&self.red.trailing).offset(16.),
|
||||
self.green.trailing.constraint_equal_to(&self.content.trailing).offset(-16.),
|
||||
self.green.bottom.constraint_equal_to(&self.content.bottom).offset(-16.),
|
||||
self.green.width.constraint_equal_to_constant(100.),
|
||||
self.green.width.constraint_equal_to_constant(100.)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -106,5 +97,6 @@ impl WindowDelegate for AppWindow {
|
|||
fn main() {
|
||||
App::new("com.test.window", BasicApp {
|
||||
window: Window::with(WindowConfig::default(), AppWindow::default())
|
||||
}).run();
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
use cacao::notification_center::Dispatcher;
|
||||
use cacao::webview::{WebView, WebViewConfig, WebViewDelegate};
|
||||
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
use cacao::appkit::menu::{Menu, MenuItem};
|
||||
use cacao::appkit::toolbar::Toolbar;
|
||||
use cacao::appkit::window::{Window, WindowConfig, WindowDelegate, WindowToolbarStyle};
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
|
||||
mod toolbar;
|
||||
use toolbar::BrowserToolbar;
|
||||
|
@ -39,13 +39,9 @@ impl AppDelegate for BasicApp {
|
|||
MenuItem::HideOthers,
|
||||
MenuItem::ShowAll,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Quit
|
||||
MenuItem::Quit,
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![
|
||||
MenuItem::CloseWindow
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![MenuItem::CloseWindow]),
|
||||
Menu::new("Edit", vec![
|
||||
MenuItem::Undo,
|
||||
MenuItem::Redo,
|
||||
|
@ -54,21 +50,16 @@ impl AppDelegate for BasicApp {
|
|||
MenuItem::Copy,
|
||||
MenuItem::Paste,
|
||||
MenuItem::Separator,
|
||||
MenuItem::SelectAll
|
||||
MenuItem::SelectAll,
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![
|
||||
MenuItem::EnterFullScreen
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![MenuItem::EnterFullScreen]),
|
||||
Menu::new("Window", vec![
|
||||
MenuItem::Minimize,
|
||||
MenuItem::Zoom,
|
||||
MenuItem::Separator,
|
||||
MenuItem::new("Bring All to Front")
|
||||
MenuItem::new("Bring All to Front"),
|
||||
]),
|
||||
|
||||
Menu::new("Help", vec![])
|
||||
Menu::new("Help", vec![]),
|
||||
]);
|
||||
|
||||
App::activate();
|
||||
|
@ -84,9 +75,15 @@ impl Dispatcher for BasicApp {
|
|||
let webview = &window.content;
|
||||
|
||||
match message {
|
||||
Action::Back => { webview.go_back(); },
|
||||
Action::Forwards => { webview.go_forward(); },
|
||||
Action::Load(url) => { window.load_url(&url); }
|
||||
Action::Back => {
|
||||
webview.go_back();
|
||||
},
|
||||
Action::Forwards => {
|
||||
webview.go_forward();
|
||||
},
|
||||
Action::Load(url) => {
|
||||
window.load_url(&url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,13 +129,17 @@ impl WindowDelegate for AppWindow {
|
|||
|
||||
fn main() {
|
||||
App::new("com.test.window", BasicApp {
|
||||
window: Window::with({
|
||||
let mut config = WindowConfig::default();
|
||||
window: Window::with(
|
||||
{
|
||||
let mut config = WindowConfig::default();
|
||||
|
||||
// This flag is necessary for Big Sur to use the correct toolbar style.
|
||||
config.toolbar_style = WindowToolbarStyle::Expanded;
|
||||
// This flag is necessary for Big Sur to use the correct toolbar style.
|
||||
config.toolbar_style = WindowToolbarStyle::Expanded;
|
||||
|
||||
config
|
||||
}, AppWindow::new())
|
||||
}).run();
|
||||
config
|
||||
},
|
||||
AppWindow::new()
|
||||
)
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
|
||||
use cacao::objc::{msg_send, sel, sel_impl};
|
||||
|
||||
use cacao::button::Button;
|
||||
use cacao::input::{TextField, TextFieldDelegate};
|
||||
|
||||
use cacao::appkit::toolbar::{Toolbar, ToolbarDisplayMode, ToolbarItem, ItemIdentifier, ToolbarDelegate};
|
||||
use cacao::appkit::toolbar::{ItemIdentifier, Toolbar, ToolbarDelegate, ToolbarDisplayMode, ToolbarItem};
|
||||
|
||||
use super::Action;
|
||||
|
||||
|
@ -45,8 +44,8 @@ impl BrowserToolbar {
|
|||
|
||||
let url_bar = TextField::with(URLBar);
|
||||
let url_bar_item = ToolbarItem::new(URL_BAR);
|
||||
|
||||
// We cheat for now to link these, as there's no API for Toolbar yet
|
||||
|
||||
// We cheat for now to link these, as there's no API for Toolbar yet
|
||||
// to support arbitrary view types. The framework is designed to support this kind of
|
||||
// cheating, though: it's not outlandish to need to just manage things yourself when it
|
||||
// comes to Objective-C/AppKit sometimes.
|
||||
|
@ -74,7 +73,7 @@ impl BrowserToolbar {
|
|||
ItemIdentifier::Custom(FWDS_BUTTON),
|
||||
ItemIdentifier::Space,
|
||||
ItemIdentifier::Custom(URL_BAR),
|
||||
ItemIdentifier::Space
|
||||
ItemIdentifier::Space,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +98,9 @@ impl ToolbarDelegate for BrowserToolbar {
|
|||
BACK_BUTTON => &self.back_item,
|
||||
FWDS_BUTTON => &self.forwards_item,
|
||||
URL_BAR => &self.url_bar_item,
|
||||
_ => { std::unreachable!(); }
|
||||
_ => {
|
||||
std::unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use cacao::layout::{LayoutConstraint, Layout};
|
||||
use cacao::button::Button;
|
||||
use cacao::color::Color;
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::view::View;
|
||||
|
||||
use crate::calculator::Msg;
|
||||
use crate::content_view::{button, BUTTON_WIDTH, BUTTON_HEIGHT};
|
||||
use crate::content_view::{button, BUTTON_HEIGHT, BUTTON_WIDTH};
|
||||
|
||||
pub struct ButtonRow {
|
||||
pub view: View,
|
||||
|
@ -15,31 +15,36 @@ impl ButtonRow {
|
|||
pub fn new(x: [Msg; 4], color: Color, action_color: Color) -> Self {
|
||||
let view = View::new();
|
||||
|
||||
let buttons: Vec<Button> = x.iter().map(|y| {
|
||||
let button = button(match y {
|
||||
Msg::Clear => "C",
|
||||
Msg::Add => "+",
|
||||
Msg::Subtract => "-",
|
||||
Msg::Multiply => "X",
|
||||
Msg::Divide => "/",
|
||||
Msg::Invert => "+/-",
|
||||
Msg::Mod => "%",
|
||||
Msg::Push(i) if *i == 1 => "1",
|
||||
Msg::Push(i) if *i == 2 => "2",
|
||||
Msg::Push(i) if *i == 3 => "3",
|
||||
Msg::Push(i) if *i == 4 => "4",
|
||||
Msg::Push(i) if *i == 5 => "5",
|
||||
Msg::Push(i) if *i == 6 => "6",
|
||||
Msg::Push(i) if *i == 7 => "7",
|
||||
Msg::Push(i) if *i == 8 => "8",
|
||||
Msg::Push(i) if *i == 9 => "9",
|
||||
_ => "W"
|
||||
let buttons: Vec<Button> = x
|
||||
.iter()
|
||||
.map(|y| {
|
||||
let button = button(
|
||||
match y {
|
||||
Msg::Clear => "C",
|
||||
Msg::Add => "+",
|
||||
Msg::Subtract => "-",
|
||||
Msg::Multiply => "X",
|
||||
Msg::Divide => "/",
|
||||
Msg::Invert => "+/-",
|
||||
Msg::Mod => "%",
|
||||
Msg::Push(i) if *i == 1 => "1",
|
||||
Msg::Push(i) if *i == 2 => "2",
|
||||
Msg::Push(i) if *i == 3 => "3",
|
||||
Msg::Push(i) if *i == 4 => "4",
|
||||
Msg::Push(i) if *i == 5 => "5",
|
||||
Msg::Push(i) if *i == 6 => "6",
|
||||
Msg::Push(i) if *i == 7 => "7",
|
||||
Msg::Push(i) if *i == 8 => "8",
|
||||
Msg::Push(i) if *i == 9 => "9",
|
||||
_ => "W"
|
||||
},
|
||||
y.clone()
|
||||
);
|
||||
|
||||
}, y.clone());
|
||||
|
||||
view.add_subview(&button);
|
||||
button
|
||||
}).collect();
|
||||
view.add_subview(&button);
|
||||
button
|
||||
})
|
||||
.collect();
|
||||
|
||||
buttons[0].set_background_color(color.clone());
|
||||
buttons[1].set_background_color(color.clone());
|
||||
|
@ -53,30 +58,22 @@ impl ButtonRow {
|
|||
buttons[0].leading.constraint_equal_to(&view.leading),
|
||||
buttons[0].bottom.constraint_equal_to(&view.bottom),
|
||||
width.constraint_equal_to_constant(BUTTON_WIDTH),
|
||||
|
||||
buttons[1].top.constraint_equal_to(&view.top),
|
||||
buttons[1].leading.constraint_equal_to(&buttons[0].trailing).offset(1.),
|
||||
buttons[1].bottom.constraint_equal_to(&view.bottom),
|
||||
buttons[1].width.constraint_equal_to(&width),
|
||||
|
||||
buttons[2].top.constraint_equal_to(&view.top),
|
||||
buttons[2].leading.constraint_equal_to(&buttons[1].trailing).offset(1.),
|
||||
buttons[2].bottom.constraint_equal_to(&view.bottom),
|
||||
buttons[2].width.constraint_equal_to(&width),
|
||||
|
||||
buttons[3].top.constraint_equal_to(&view.top),
|
||||
buttons[3].leading.constraint_equal_to(&buttons[2].trailing).offset(1.),
|
||||
buttons[3].trailing.constraint_equal_to(&view.trailing),
|
||||
buttons[3].bottom.constraint_equal_to(&view.bottom),
|
||||
buttons[3].width.constraint_equal_to(&width),
|
||||
|
||||
view.height.constraint_equal_to_constant(BUTTON_HEIGHT)
|
||||
]);
|
||||
|
||||
Self {
|
||||
view,
|
||||
buttons
|
||||
}
|
||||
Self { view, buttons }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use cacao::lazy_static::lazy_static;
|
||||
use cacao::appkit::App;
|
||||
use cacao::lazy_static::lazy_static;
|
||||
|
||||
use crate::CalculatorApp;
|
||||
|
||||
|
@ -40,7 +40,7 @@ impl Calculator {
|
|||
|
||||
pub fn run(&self, message: Msg) {
|
||||
let mut expression = self.0.write().unwrap();
|
||||
|
||||
|
||||
match message {
|
||||
Msg::Push(i) => {
|
||||
// Realistically you might want to check decimal length here or something.
|
||||
|
@ -49,7 +49,7 @@ impl Calculator {
|
|||
let display = (*expression).join("").split(" ").last().unwrap_or("0").to_string();
|
||||
App::<CalculatorApp, String>::dispatch_main(display);
|
||||
},
|
||||
|
||||
|
||||
Msg::Decimal => {
|
||||
let display = (*expression).join("").split(" ").last().unwrap_or("0").to_string();
|
||||
if !display.contains(".") {
|
||||
|
@ -65,11 +65,11 @@ impl Calculator {
|
|||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Msg::Subtract => {
|
||||
(*expression).push(" - ".to_string());
|
||||
},
|
||||
|
||||
|
||||
Msg::Multiply => {
|
||||
(*expression).push(" * ".to_string());
|
||||
},
|
||||
|
@ -90,14 +90,18 @@ impl Calculator {
|
|||
}
|
||||
|
||||
println!("Expr: {}", expr);
|
||||
|
||||
|
||||
match eval::eval(&expr) {
|
||||
Ok(val) => { App::<CalculatorApp, String>::dispatch_main(val.to_string()); },
|
||||
Err(e) => { eprintln!("Error parsing expression: {:?}", e); }
|
||||
Ok(val) => {
|
||||
App::<CalculatorApp, String>::dispatch_main(val.to_string());
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Error parsing expression: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use cacao::text::{Font, Label, TextAlign};
|
||||
use cacao::layout::{LayoutConstraint, Layout};
|
||||
use cacao::button::{Button, BezelStyle};
|
||||
use cacao::color::Color;
|
||||
use cacao::appkit::FocusRingType;
|
||||
use cacao::button::{BezelStyle, Button};
|
||||
use cacao::color::Color;
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::text::{Font, Label, TextAlign};
|
||||
use cacao::view::{View, ViewDelegate};
|
||||
|
||||
use crate::button_row::ButtonRow;
|
||||
|
@ -53,21 +53,29 @@ impl CalculatorView {
|
|||
results_wrapper,
|
||||
label,
|
||||
|
||||
row0: ButtonRow::new([
|
||||
Msg::Clear, Msg::Invert, Msg::Mod, Msg::Divide
|
||||
], Color::rgb(69, 69, 69), Color::rgb(255, 148, 10)),
|
||||
row0: ButtonRow::new(
|
||||
[Msg::Clear, Msg::Invert, Msg::Mod, Msg::Divide],
|
||||
Color::rgb(69, 69, 69),
|
||||
Color::rgb(255, 148, 10)
|
||||
),
|
||||
|
||||
row1: ButtonRow::new([
|
||||
Msg::Push(7), Msg::Push(8), Msg::Push(9), Msg::Multiply
|
||||
], Color::rgb(100, 100, 100), Color::rgb(255, 148, 10)),
|
||||
row1: ButtonRow::new(
|
||||
[Msg::Push(7), Msg::Push(8), Msg::Push(9), Msg::Multiply],
|
||||
Color::rgb(100, 100, 100),
|
||||
Color::rgb(255, 148, 10)
|
||||
),
|
||||
|
||||
row2: ButtonRow::new([
|
||||
Msg::Push(4), Msg::Push(5), Msg::Push(6), Msg::Subtract
|
||||
], Color::rgb(100, 100, 100), Color::rgb(255, 148, 10)),
|
||||
row2: ButtonRow::new(
|
||||
[Msg::Push(4), Msg::Push(5), Msg::Push(6), Msg::Subtract],
|
||||
Color::rgb(100, 100, 100),
|
||||
Color::rgb(255, 148, 10)
|
||||
),
|
||||
|
||||
row3: ButtonRow::new([
|
||||
Msg::Push(1), Msg::Push(2), Msg::Push(3), Msg::Add
|
||||
], Color::rgb(100, 100, 100), Color::rgb(255, 148, 10)),
|
||||
row3: ButtonRow::new(
|
||||
[Msg::Push(1), Msg::Push(2), Msg::Push(3), Msg::Add],
|
||||
Color::rgb(100, 100, 100),
|
||||
Color::rgb(255, 148, 10)
|
||||
),
|
||||
|
||||
zero: button("0", Msg::Push(0)),
|
||||
dot: button(".", Msg::Decimal),
|
||||
|
@ -108,38 +116,43 @@ impl ViewDelegate for CalculatorView {
|
|||
self.results_wrapper.leading.constraint_equal_to(&view.leading),
|
||||
self.results_wrapper.trailing.constraint_equal_to(&view.trailing),
|
||||
self.results_wrapper.height.constraint_equal_to_constant(80.),
|
||||
|
||||
self.label.leading.constraint_equal_to(&self.results_wrapper.leading).offset(22.),
|
||||
self.label.trailing.constraint_equal_to(&self.results_wrapper.trailing).offset(-16.),
|
||||
self.label.bottom.constraint_equal_to(&self.results_wrapper.bottom).offset(-4.),
|
||||
|
||||
self.label
|
||||
.leading
|
||||
.constraint_equal_to(&self.results_wrapper.leading)
|
||||
.offset(22.),
|
||||
self.label
|
||||
.trailing
|
||||
.constraint_equal_to(&self.results_wrapper.trailing)
|
||||
.offset(-16.),
|
||||
self.label
|
||||
.bottom
|
||||
.constraint_equal_to(&self.results_wrapper.bottom)
|
||||
.offset(-4.),
|
||||
// Buttons laid out from top-left
|
||||
self.row0.view.top.constraint_equal_to(&self.results_wrapper.bottom).offset(1.),
|
||||
self.row0
|
||||
.view
|
||||
.top
|
||||
.constraint_equal_to(&self.results_wrapper.bottom)
|
||||
.offset(1.),
|
||||
self.row0.view.leading.constraint_equal_to(&view.leading),
|
||||
self.row0.view.trailing.constraint_equal_to(&view.trailing),
|
||||
|
||||
self.row1.view.top.constraint_equal_to(&self.row0.view.bottom).offset(1.),
|
||||
self.row1.view.leading.constraint_equal_to(&view.leading),
|
||||
self.row1.view.trailing.constraint_equal_to(&view.trailing),
|
||||
|
||||
self.row2.view.top.constraint_equal_to(&self.row1.view.bottom).offset(1.),
|
||||
self.row2.view.leading.constraint_equal_to(&view.leading),
|
||||
self.row2.view.trailing.constraint_equal_to(&view.trailing),
|
||||
|
||||
self.row3.view.top.constraint_equal_to(&self.row2.view.bottom).offset(1.),
|
||||
self.row3.view.leading.constraint_equal_to(&view.leading),
|
||||
self.row3.view.trailing.constraint_equal_to(&view.trailing),
|
||||
|
||||
self.zero.top.constraint_equal_to(&self.row3.view.bottom).offset(1.),
|
||||
self.zero.leading.constraint_equal_to(&view.leading),
|
||||
self.zero.bottom.constraint_equal_to(&view.bottom),
|
||||
|
||||
self.dot.top.constraint_equal_to(&self.row3.view.bottom).offset(1.),
|
||||
self.dot.leading.constraint_equal_to(&self.zero.trailing).offset(1.),
|
||||
self.dot.bottom.constraint_equal_to(&view.bottom),
|
||||
self.dot.width.constraint_equal_to_constant(BUTTON_WIDTH),
|
||||
self.dot.height.constraint_equal_to_constant(BUTTON_HEIGHT),
|
||||
|
||||
self.equals.top.constraint_equal_to(&self.row3.view.bottom).offset(1.),
|
||||
self.equals.leading.constraint_equal_to(&self.dot.trailing).offset(1.),
|
||||
self.equals.trailing.constraint_equal_to(&view.trailing),
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
use std::sync::RwLock;
|
||||
|
||||
use cacao::appkit::window::{TitleVisibility, Window, WindowConfig};
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
use cacao::appkit::window::{Window, WindowConfig, TitleVisibility};
|
||||
use cacao::appkit::{Event, EventMask, EventMonitor};
|
||||
use cacao::color::Color;
|
||||
use cacao::notification_center::Dispatcher;
|
||||
|
@ -35,12 +35,12 @@ impl AppDelegate for CalculatorApp {
|
|||
App::activate();
|
||||
|
||||
// Event Monitors need to be started after the App has been activated.
|
||||
// We use an RwLock here, but it's possible this entire method can be
|
||||
// We use an RwLock here, but it's possible this entire method can be
|
||||
// &mut self and you wouldn't need these kinds of shenanigans.
|
||||
//self.start_monitoring();
|
||||
|
||||
self.window.set_title("Calculator");
|
||||
self.window.set_background_color(Color::rgb(49,49,49));
|
||||
self.window.set_background_color(Color::rgb(49, 49, 49));
|
||||
self.window.set_title_visibility(TitleVisibility::Hidden);
|
||||
self.window.set_titlebar_appears_transparent(true);
|
||||
self.window.set_movable_by_background(true);
|
||||
|
@ -109,5 +109,6 @@ fn main() {
|
|||
window: Window::new(config),
|
||||
content: View::with(CalculatorView::new()),
|
||||
key_monitor: RwLock::new(None)
|
||||
}).run();
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ use cacao::color::Color;
|
|||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::view::View;
|
||||
|
||||
use cacao::macos::{App, AppDelegate};
|
||||
use cacao::image::{DrawConfig, Image, ImageView};
|
||||
use cacao::macos::menu::{Menu, MenuItem};
|
||||
use cacao::macos::window::{Window};
|
||||
use cacao::image::{ImageView, Image, DrawConfig};
|
||||
use cacao::macos::window::Window;
|
||||
use cacao::macos::{App, AppDelegate};
|
||||
|
||||
struct BasicApp {
|
||||
window: Window,
|
||||
|
@ -22,7 +22,7 @@ impl Default for BasicApp {
|
|||
let config = DrawConfig {
|
||||
source: (100., 100.),
|
||||
target: (800., 800.),
|
||||
resize: cacao::image::ResizeBehavior::Stretch,
|
||||
resize: cacao::image::ResizeBehavior::Stretch
|
||||
};
|
||||
|
||||
Self {
|
||||
|
@ -97,17 +97,15 @@ impl Default for BasicApp {
|
|||
|
||||
impl AppDelegate for BasicApp {
|
||||
fn did_finish_launching(&self) {
|
||||
App::set_menu(vec![
|
||||
Menu::new("", vec![
|
||||
MenuItem::Services,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Hide,
|
||||
MenuItem::HideOthers,
|
||||
MenuItem::ShowAll,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Quit
|
||||
])
|
||||
]);
|
||||
App::set_menu(vec![Menu::new("", vec![
|
||||
MenuItem::Services,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Hide,
|
||||
MenuItem::HideOthers,
|
||||
MenuItem::ShowAll,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Quit,
|
||||
])]);
|
||||
|
||||
App::activate();
|
||||
self.window.set_title("Hello World!");
|
||||
|
@ -120,7 +118,7 @@ impl AppDelegate for BasicApp {
|
|||
self.image_view.top.constraint_equal_to(&self.content_view.top),
|
||||
self.image_view.leading.constraint_equal_to(&self.content_view.leading),
|
||||
self.image_view.trailing.constraint_equal_to(&self.content_view.trailing),
|
||||
self.image_view.bottom.constraint_equal_to(&self.content_view.bottom),
|
||||
self.image_view.bottom.constraint_equal_to(&self.content_view.bottom)
|
||||
]);
|
||||
|
||||
self.window.set_content_view(&self.content_view);
|
||||
|
|
|
@ -36,7 +36,7 @@ impl AppDelegate for DefaultsTest {
|
|||
|
||||
let teststring = defaults.get("teststring").unwrap();
|
||||
assert_eq!(teststring.as_str().unwrap(), "Testing");
|
||||
|
||||
|
||||
let bytes = defaults.get("testdata").unwrap();
|
||||
let s = match std::str::from_utf8(bytes.as_data().unwrap()) {
|
||||
Ok(s) => s,
|
||||
|
|
|
@ -6,9 +6,9 @@ use cacao::geometry::Rect;
|
|||
use cacao::layout::Layout;
|
||||
use cacao::view::View;
|
||||
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
use cacao::appkit::menu::{Menu, MenuItem};
|
||||
use cacao::appkit::window::{Window, WindowConfig, WindowDelegate};
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
|
||||
const CORNER_RADIUS: f64 = 16.;
|
||||
const SPACING: f64 = 10.;
|
||||
|
@ -30,23 +30,16 @@ impl AppDelegate for BasicApp {
|
|||
MenuItem::HideOthers,
|
||||
MenuItem::ShowAll,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Quit
|
||||
MenuItem::Quit,
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![
|
||||
MenuItem::CloseWindow
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![
|
||||
MenuItem::EnterFullScreen
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![MenuItem::CloseWindow]),
|
||||
Menu::new("View", vec![MenuItem::EnterFullScreen]),
|
||||
Menu::new("Window", vec![
|
||||
MenuItem::Minimize,
|
||||
MenuItem::Zoom,
|
||||
MenuItem::Separator,
|
||||
MenuItem::new("Bring All to Front")
|
||||
])
|
||||
MenuItem::new("Bring All to Front"),
|
||||
]),
|
||||
]);
|
||||
|
||||
App::activate();
|
||||
|
@ -115,5 +108,6 @@ impl WindowDelegate for AppWindow {
|
|||
fn main() {
|
||||
App::new("com.test.window", BasicApp {
|
||||
window: Window::with(WindowConfig::default(), AppWindow::default())
|
||||
}).run();
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use std::sync::RwLock;
|
||||
|
||||
use cacao::uikit::{
|
||||
App, AppDelegate, Scene, SceneConfig, SceneSession,
|
||||
SceneConnectionOptions, WindowSceneDelegate, Window
|
||||
};
|
||||
use cacao::uikit::{App, AppDelegate, Scene, SceneConfig, SceneConnectionOptions, SceneSession, Window, WindowSceneDelegate};
|
||||
|
||||
use cacao::color::Color;
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
|
@ -44,12 +41,10 @@ impl ViewDelegate for RootView {
|
|||
self.red.leading.constraint_equal_to(&view.leading).offset(16.),
|
||||
self.red.trailing.constraint_equal_to(&view.trailing).offset(-16.),
|
||||
self.red.height.constraint_equal_to_constant(100.),
|
||||
|
||||
self.green.top.constraint_equal_to(&self.red.bottom).offset(16.),
|
||||
self.green.leading.constraint_equal_to(&view.leading).offset(16.),
|
||||
self.green.trailing.constraint_equal_to(&view.trailing).offset(-16.),
|
||||
self.green.height.constraint_equal_to_constant(120.),
|
||||
|
||||
self.blue.top.constraint_equal_to(&self.green.bottom).offset(16.),
|
||||
self.blue.leading.constraint_equal_to(&view.leading).offset(16.),
|
||||
self.blue.trailing.constraint_equal_to(&view.trailing).offset(-16.),
|
||||
|
@ -65,20 +60,15 @@ pub struct WindowScene {
|
|||
}
|
||||
|
||||
impl WindowSceneDelegate for WindowScene {
|
||||
fn will_connect(
|
||||
&self,
|
||||
scene: Scene,
|
||||
session: SceneSession,
|
||||
options: SceneConnectionOptions
|
||||
) {
|
||||
fn will_connect(&self, scene: Scene, session: SceneSession, options: SceneConnectionOptions) {
|
||||
let bounds = scene.get_bounds();
|
||||
let mut window = Window::new(bounds);
|
||||
window.set_window_scene(scene);
|
||||
|
||||
|
||||
let root_view_controller = ViewController::new(RootView::default());
|
||||
window.set_root_view_controller(&root_view_controller);
|
||||
window.show();
|
||||
|
||||
|
||||
{
|
||||
let mut w = self.window.write().unwrap();
|
||||
*w = Some(window);
|
||||
|
@ -90,7 +80,5 @@ impl WindowSceneDelegate for WindowScene {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
App::new(TestApp::default(), || {
|
||||
Box::new(WindowScene::default())
|
||||
}).run();
|
||||
App::new(TestApp::default(), || Box::new(WindowScene::default())).run();
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ An example that showcases layout out a view with AutoLayout. This requires the f
|
|||
## Frame Layout
|
||||
An example that showcases laying out with a more old school Frame-based approach. Platforms where AutoLayout are not supported will want to try this instead of the AutoLayout example.
|
||||
|
||||
**macOS:**
|
||||
**macOS:**
|
||||
`cargo run --example frame_layout`
|
||||
|
||||
**Platforms lacking AutoLayout:**
|
||||
**Platforms lacking AutoLayout:**
|
||||
`cargo run --example frame_layout --no-default-features --features appkit`
|
||||
|
||||
## Defaults
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
//! This example showcases setting up a basic application and window, setting up some views to
|
||||
//! work with autolayout, and some basic ways to handle colors.
|
||||
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::input::{TextField, TextFieldDelegate};
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::view::View;
|
||||
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
use cacao::appkit::menu::{Menu, MenuItem};
|
||||
use cacao::appkit::window::{Window, WindowConfig, WindowDelegate};
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
|
||||
struct BasicApp {
|
||||
window: Window<AppWindow>
|
||||
|
@ -23,13 +23,9 @@ impl AppDelegate for BasicApp {
|
|||
MenuItem::HideOthers,
|
||||
MenuItem::ShowAll,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Quit
|
||||
MenuItem::Quit,
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![
|
||||
MenuItem::CloseWindow
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![MenuItem::CloseWindow]),
|
||||
Menu::new("Edit", vec![
|
||||
MenuItem::Undo,
|
||||
MenuItem::Redo,
|
||||
|
@ -38,21 +34,16 @@ impl AppDelegate for BasicApp {
|
|||
MenuItem::Copy,
|
||||
MenuItem::Paste,
|
||||
MenuItem::Separator,
|
||||
MenuItem::SelectAll
|
||||
MenuItem::SelectAll,
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![
|
||||
MenuItem::EnterFullScreen
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![MenuItem::EnterFullScreen]),
|
||||
Menu::new("Window", vec![
|
||||
MenuItem::Minimize,
|
||||
MenuItem::Zoom,
|
||||
MenuItem::Separator,
|
||||
MenuItem::new("Bring All to Front")
|
||||
MenuItem::new("Bring All to Front"),
|
||||
]),
|
||||
|
||||
Menu::new("Help", vec![])
|
||||
Menu::new("Help", vec![]),
|
||||
]);
|
||||
|
||||
App::activate();
|
||||
|
@ -79,7 +70,6 @@ impl TextFieldDelegate for ConsoleLogger {
|
|||
println!("Did change to: {}", value);
|
||||
}
|
||||
|
||||
|
||||
fn text_did_end_editing(&self, value: &str) {
|
||||
println!("Ended: {}", value);
|
||||
}
|
||||
|
@ -95,7 +85,7 @@ impl AppWindow {
|
|||
pub fn new() -> Self {
|
||||
AppWindow {
|
||||
input: TextField::with(ConsoleLogger),
|
||||
content: View::new(),
|
||||
content: View::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -121,5 +111,6 @@ impl WindowDelegate for AppWindow {
|
|||
fn main() {
|
||||
App::new("com.test.window", BasicApp {
|
||||
window: Window::with(WindowConfig::default(), AppWindow::new())
|
||||
}).run();
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -9,18 +9,16 @@ mod view;
|
|||
use view::AddNewTodoContentView;
|
||||
|
||||
pub struct AddNewTodoWindow {
|
||||
pub content: ViewController<AddNewTodoContentView>,
|
||||
pub content: ViewController<AddNewTodoContentView>
|
||||
}
|
||||
|
||||
impl AddNewTodoWindow {
|
||||
pub fn new() -> Self {
|
||||
let content = ViewController::new(AddNewTodoContentView::default());
|
||||
|
||||
AddNewTodoWindow {
|
||||
content: content
|
||||
}
|
||||
|
||||
AddNewTodoWindow { content: content }
|
||||
}
|
||||
|
||||
|
||||
pub fn on_message(&self, message: Message) {
|
||||
if let Some(delegate) = &self.content.view.delegate {
|
||||
delegate.on_message(message);
|
||||
|
@ -30,14 +28,14 @@ impl AddNewTodoWindow {
|
|||
|
||||
impl WindowDelegate for AddNewTodoWindow {
|
||||
const NAME: &'static str = "AddNewTodoWindow";
|
||||
|
||||
fn did_load(&mut self, window: Window) {
|
||||
|
||||
fn did_load(&mut self, window: Window) {
|
||||
window.set_autosave_name("AddNewTodoWindow");
|
||||
window.set_minimum_content_size(300, 100);
|
||||
window.set_title("Add a New Task");
|
||||
window.set_content_view_controller(&self.content);
|
||||
}
|
||||
|
||||
|
||||
fn cancel(&self) {
|
||||
dispatch_ui(Message::CloseSheet);
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
//! changes before version 0.1. This approach is unlikely to break as an example while those
|
||||
//! changes are poked and prodded at, even if it is a bit verbose and confusing.
|
||||
|
||||
use cacao::text::Label;
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::text::Label;
|
||||
use cacao::view::{View, ViewDelegate};
|
||||
|
||||
use cacao::button::Button;
|
||||
|
@ -41,17 +41,17 @@ impl AddNewTodoContentView {
|
|||
|
||||
impl ViewDelegate for AddNewTodoContentView {
|
||||
const NAME: &'static str = "AddNewTodoContentView";
|
||||
|
||||
|
||||
fn did_load(&mut self, view: View) {
|
||||
let instructions = Label::new();
|
||||
instructions.set_text("Let's be real: we both know this task isn't getting done.");
|
||||
|
||||
let input = TextField::new();
|
||||
|
||||
|
||||
let mut button = Button::new("Add");
|
||||
button.set_key_equivalent("\r");
|
||||
button.set_action(|| dispatch_ui(Message::ProcessNewTodo));
|
||||
|
||||
|
||||
view.add_subview(&instructions);
|
||||
view.add_subview(&input);
|
||||
view.add_subview(&button);
|
||||
|
@ -60,11 +60,9 @@ impl ViewDelegate for AddNewTodoContentView {
|
|||
instructions.top.constraint_equal_to(&view.top).offset(16.),
|
||||
instructions.leading.constraint_equal_to(&view.leading).offset(16.),
|
||||
instructions.trailing.constraint_equal_to(&view.trailing).offset(-16.),
|
||||
|
||||
input.top.constraint_equal_to(&instructions.bottom).offset(8.),
|
||||
input.leading.constraint_equal_to(&view.leading).offset(16.),
|
||||
input.trailing.constraint_equal_to(&view.trailing).offset(-16.),
|
||||
|
||||
button.top.constraint_equal_to(&input.bottom).offset(8.),
|
||||
button.trailing.constraint_equal_to(&view.trailing).offset(-16.),
|
||||
button.bottom.constraint_equal_to(&view.bottom).offset(-16.)
|
||||
|
|
|
@ -22,7 +22,7 @@ impl AppDelegate for TodosApp {
|
|||
|
||||
App::set_menu(menu());
|
||||
App::activate();
|
||||
|
||||
|
||||
self.window_manager.open_main();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,5 @@ mod todos;
|
|||
mod windows;
|
||||
|
||||
fn main() {
|
||||
App::new(
|
||||
"com.cacao.todo",
|
||||
app::TodosApp::default()
|
||||
).run();
|
||||
App::new("com.cacao.todo", app::TodosApp::default()).run();
|
||||
}
|
||||
|
|
|
@ -14,11 +14,9 @@ pub fn menu() -> Vec<Menu> {
|
|||
Menu::new("", vec![
|
||||
MenuItem::About("Todos".to_string()),
|
||||
MenuItem::Separator,
|
||||
|
||||
MenuItem::new("Preferences").key(",").action(|| {
|
||||
dispatch_ui(Message::OpenPreferencesWindow);
|
||||
}),
|
||||
|
||||
MenuItem::Separator,
|
||||
MenuItem::Services,
|
||||
MenuItem::Separator,
|
||||
|
@ -26,24 +24,19 @@ pub fn menu() -> Vec<Menu> {
|
|||
MenuItem::HideOthers,
|
||||
MenuItem::ShowAll,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Quit
|
||||
MenuItem::Quit,
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![
|
||||
MenuItem::new("Open/Show Window").key("n").action(|| {
|
||||
dispatch_ui(Message::OpenMainWindow);
|
||||
}),
|
||||
|
||||
MenuItem::Separator,
|
||||
|
||||
MenuItem::new("Add Todo").key("+").action(|| {
|
||||
dispatch_ui(Message::OpenNewTodoSheet);
|
||||
}),
|
||||
|
||||
MenuItem::Separator,
|
||||
MenuItem::CloseWindow
|
||||
MenuItem::CloseWindow,
|
||||
]),
|
||||
|
||||
Menu::new("Edit", vec![
|
||||
MenuItem::Undo,
|
||||
MenuItem::Redo,
|
||||
|
@ -52,20 +45,15 @@ pub fn menu() -> Vec<Menu> {
|
|||
MenuItem::Copy,
|
||||
MenuItem::Paste,
|
||||
MenuItem::Separator,
|
||||
MenuItem::SelectAll
|
||||
MenuItem::SelectAll,
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![
|
||||
MenuItem::EnterFullScreen
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![MenuItem::EnterFullScreen]),
|
||||
Menu::new("Window", vec![
|
||||
MenuItem::Minimize,
|
||||
MenuItem::Zoom,
|
||||
MenuItem::Separator,
|
||||
MenuItem::new("Bring All to Front")
|
||||
MenuItem::new("Bring All to Front"),
|
||||
]),
|
||||
|
||||
Menu::new("Help", vec![])
|
||||
Menu::new("Help", vec![]),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use cacao::text::{Label, TextAlign};
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::text::{Label, TextAlign};
|
||||
use cacao::view::{View, ViewDelegate};
|
||||
|
||||
/// A blank advanced preferences view.
|
||||
|
@ -10,9 +10,10 @@ pub struct AdvancedPreferencesContentView {
|
|||
|
||||
impl ViewDelegate for AdvancedPreferencesContentView {
|
||||
const NAME: &'static str = "AdvancedPreferencesContentView";
|
||||
|
||||
|
||||
fn did_load(&mut self, view: View) {
|
||||
self.label.set_text("And this is where advanced preferences would be... if we had any.");
|
||||
self.label
|
||||
.set_text("And this is where advanced preferences would be... if we had any.");
|
||||
self.label.set_text_alignment(TextAlign::Center);
|
||||
view.add_subview(&self.label);
|
||||
|
||||
|
@ -24,4 +25,3 @@ impl ViewDelegate for AdvancedPreferencesContentView {
|
|||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! The main guts of the Preferences window. We store all our preferences in
|
||||
//! `UserDefaults`, so there's not too much extra needed here - we can do most
|
||||
//! The main guts of the Preferences window. We store all our preferences in
|
||||
//! `UserDefaults`, so there's not too much extra needed here - we can do most
|
||||
//! event handlers inline.
|
||||
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
|
@ -12,12 +12,12 @@ use super::toggle_option_view::ToggleOptionView;
|
|||
/// A general preferences view.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct GeneralPreferencesContentView {
|
||||
pub example_option: ToggleOptionView,
|
||||
pub example_option: ToggleOptionView
|
||||
}
|
||||
|
||||
impl ViewDelegate for GeneralPreferencesContentView {
|
||||
const NAME: &'static str = "GeneralPreferencesContentView";
|
||||
|
||||
|
||||
fn did_load(&mut self, view: View) {
|
||||
self.example_option.configure(
|
||||
"An example preference",
|
||||
|
@ -30,10 +30,17 @@ impl ViewDelegate for GeneralPreferencesContentView {
|
|||
|
||||
LayoutConstraint::activate(&[
|
||||
self.example_option.view.top.constraint_equal_to(&view.top).offset(22.),
|
||||
self.example_option.view.leading.constraint_equal_to(&view.leading).offset(22.),
|
||||
self.example_option.view.trailing.constraint_equal_to(&view.trailing).offset(-22.),
|
||||
self.example_option
|
||||
.view
|
||||
.leading
|
||||
.constraint_equal_to(&view.leading)
|
||||
.offset(22.),
|
||||
self.example_option
|
||||
.view
|
||||
.trailing
|
||||
.constraint_equal_to(&view.trailing)
|
||||
.offset(-22.),
|
||||
self.example_option.view.bottom.constraint_equal_to(&view.bottom).offset(-22.)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Implements a stock-ish Preferences window.
|
||||
|
||||
use cacao::appkit::window::{Window, WindowDelegate};
|
||||
use cacao::appkit::toolbar::Toolbar;
|
||||
use cacao::appkit::window::{Window, WindowDelegate};
|
||||
use cacao::view::ViewController;
|
||||
|
||||
use crate::storage::Message;
|
||||
|
@ -33,7 +33,7 @@ impl PreferencesWindow {
|
|||
window: None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn on_message(&self, message: Message) {
|
||||
let window = self.window.as_ref().unwrap();
|
||||
|
||||
|
@ -56,11 +56,11 @@ impl PreferencesWindow {
|
|||
impl WindowDelegate for PreferencesWindow {
|
||||
const NAME: &'static str = "PreferencesWindow";
|
||||
|
||||
fn did_load(&mut self, window: Window) {
|
||||
fn did_load(&mut self, window: Window) {
|
||||
window.set_autosave_name("TodosPreferencesWindow");
|
||||
window.set_movable_by_background(true);
|
||||
window.set_toolbar(&self.toolbar);
|
||||
|
||||
|
||||
self.window = Some(window);
|
||||
|
||||
self.on_message(Message::SwitchPreferencesToGeneralPane);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use cacao::text::Label;
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::switch::Switch;
|
||||
use cacao::view::{View};
|
||||
use cacao::text::Label;
|
||||
use cacao::view::View;
|
||||
|
||||
/// A reusable widget for a toggle; this is effectively a standard checkbox/label combination for
|
||||
/// toggling a boolean value.
|
||||
|
@ -23,7 +23,7 @@ impl Default for ToggleOptionView {
|
|||
|
||||
let title = Label::new();
|
||||
view.add_subview(&title);
|
||||
|
||||
|
||||
let subtitle = Label::new();
|
||||
view.add_subview(&subtitle);
|
||||
|
||||
|
@ -31,11 +31,9 @@ impl Default for ToggleOptionView {
|
|||
switch.top.constraint_equal_to(&view.top),
|
||||
switch.leading.constraint_equal_to(&view.leading),
|
||||
switch.width.constraint_equal_to_constant(24.),
|
||||
|
||||
title.top.constraint_equal_to(&view.top),
|
||||
title.leading.constraint_equal_to(&switch.trailing),
|
||||
title.trailing.constraint_equal_to(&view.trailing),
|
||||
|
||||
subtitle.top.constraint_equal_to(&title.bottom),
|
||||
subtitle.leading.constraint_equal_to(&switch.trailing),
|
||||
subtitle.trailing.constraint_equal_to(&view.trailing),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Implements an example toolbar for a Preferences app. Could be cleaner, probably worth cleaning
|
||||
//! up at some point.
|
||||
|
||||
use cacao::appkit::toolbar::{Toolbar, ToolbarDelegate, ToolbarItem, ItemIdentifier};
|
||||
use cacao::appkit::toolbar::{ItemIdentifier, Toolbar, ToolbarDelegate, ToolbarItem};
|
||||
use cacao::image::{Image, MacSystemIcon};
|
||||
|
||||
use crate::storage::{dispatch_ui, Message};
|
||||
|
@ -11,37 +11,40 @@ pub struct PreferencesToolbar((ToolbarItem, ToolbarItem));
|
|||
|
||||
impl Default for PreferencesToolbar {
|
||||
fn default() -> Self {
|
||||
PreferencesToolbar(({
|
||||
let mut item = ToolbarItem::new("general");
|
||||
item.set_title("General");
|
||||
PreferencesToolbar((
|
||||
{
|
||||
let mut item = ToolbarItem::new("general");
|
||||
item.set_title("General");
|
||||
|
||||
let icon = Image::toolbar_icon(MacSystemIcon::PreferencesGeneral, "General");
|
||||
item.set_image(icon);
|
||||
|
||||
item.set_action(|| {
|
||||
dispatch_ui(Message::SwitchPreferencesToGeneralPane);
|
||||
});
|
||||
let icon = Image::toolbar_icon(MacSystemIcon::PreferencesGeneral, "General");
|
||||
item.set_image(icon);
|
||||
|
||||
item
|
||||
}, {
|
||||
let mut item = ToolbarItem::new("advanced");
|
||||
item.set_title("Advanced");
|
||||
|
||||
let icon = Image::toolbar_icon(MacSystemIcon::PreferencesAdvanced, "Advanced");
|
||||
item.set_image(icon);
|
||||
|
||||
item.set_action(|| {
|
||||
dispatch_ui(Message::SwitchPreferencesToAdvancedPane);
|
||||
});
|
||||
|
||||
item
|
||||
}))
|
||||
item.set_action(|| {
|
||||
dispatch_ui(Message::SwitchPreferencesToGeneralPane);
|
||||
});
|
||||
|
||||
item
|
||||
},
|
||||
{
|
||||
let mut item = ToolbarItem::new("advanced");
|
||||
item.set_title("Advanced");
|
||||
|
||||
let icon = Image::toolbar_icon(MacSystemIcon::PreferencesAdvanced, "Advanced");
|
||||
item.set_image(icon);
|
||||
|
||||
item.set_action(|| {
|
||||
dispatch_ui(Message::SwitchPreferencesToAdvancedPane);
|
||||
});
|
||||
|
||||
item
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToolbarDelegate for PreferencesToolbar {
|
||||
const NAME: &'static str = "PreferencesToolbar";
|
||||
|
||||
|
||||
fn did_load(&mut self, toolbar: Toolbar) {
|
||||
toolbar.set_selected("general");
|
||||
}
|
||||
|
@ -60,9 +63,11 @@ impl ToolbarDelegate for PreferencesToolbar {
|
|||
|
||||
fn item_for(&self, identifier: &str) -> &ToolbarItem {
|
||||
match identifier {
|
||||
"general" => &self.0.0,
|
||||
"advanced" => &self.0.1,
|
||||
_ => { unreachable!(); }
|
||||
"general" => &self.0 .0,
|
||||
"advanced" => &self.0 .1,
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,17 +42,20 @@ impl Defaults {
|
|||
/// should work, and I'd rather it crash with a meaningful message rather than `unwrap()` issues.
|
||||
fn toggle_bool(key: &str) {
|
||||
let mut defaults = UserDefaults::standard();
|
||||
|
||||
|
||||
if let Some(value) = defaults.get(key) {
|
||||
if let Some(value) = value.as_bool() {
|
||||
defaults.insert(key, Value::Bool(!value));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
panic!("Attempting to toggle a boolean value for {}, but it's not a boolean.", key);
|
||||
}
|
||||
|
||||
panic!("Attempting to toggle a boolean value for {}, but this key does not exist.", key);
|
||||
panic!(
|
||||
"Attempting to toggle a boolean value for {}, but this key does not exist.",
|
||||
key
|
||||
);
|
||||
}
|
||||
|
||||
/// A helper method for loading a boolean value held at the specified key. If the value cannot
|
||||
|
@ -62,12 +65,12 @@ fn toggle_bool(key: &str) {
|
|||
/// should work, and I'd rather it crash with a meaningful message rather than `unwrap()` issues.
|
||||
fn load_bool(key: &str) -> bool {
|
||||
let defaults = UserDefaults::standard();
|
||||
|
||||
|
||||
if let Some(value) = defaults.get(key) {
|
||||
if let Some(value) = value.as_bool() {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
panic!("Attempting to load a boolean value for {}, but it's not a boolean.", key);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ mod defaults;
|
|||
pub use defaults::Defaults;
|
||||
|
||||
mod todos;
|
||||
pub use todos::{Todos, Todo, TodoStatus};
|
||||
pub use todos::{Todo, TodoStatus, Todos};
|
||||
|
||||
/// Message passing is our primary way of instructing UI changes without needing to do
|
||||
/// Message passing is our primary way of instructing UI changes without needing to do
|
||||
/// constant crazy referencing in Rust. Dispatch a method using either `dispatch_ui` for the main
|
||||
/// thread, or `dispatch` for a background thread, and the main `TodosApp` will receive the
|
||||
/// message. From there, it can filter down to components, or just handle it as necessary.
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
//! doing this stuff on the main thread; in a more complicated app, you'd probably make different
|
||||
//! choices.
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// The status of a Todo.
|
||||
#[derive(Debug)]
|
||||
|
@ -43,7 +43,7 @@ impl Todos {
|
|||
|
||||
*stack = todos;
|
||||
}
|
||||
|
||||
|
||||
/// Edit a Todo at the row specified.
|
||||
pub fn with_mut<F>(&self, row: usize, handler: F)
|
||||
where
|
||||
|
|
|
@ -7,8 +7,8 @@ use cacao::layout::{Layout, LayoutConstraint};
|
|||
use cacao::listview::ListView;
|
||||
use cacao::view::{View, ViewDelegate};
|
||||
|
||||
use crate::storage::Message;
|
||||
use super::list::TodosListView;
|
||||
use crate::storage::Message;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TodosContentView {
|
||||
|
|
|
@ -2,12 +2,9 @@
|
|||
//! mostly single-threaded example, so we can get away with cutting a few corners and keeping our
|
||||
//! data store in here - but for a larger app, you'd likely do something else.
|
||||
|
||||
use cacao::listview::{
|
||||
ListView, ListViewDelegate, ListViewRow,
|
||||
RowAnimation, RowEdge, RowAction, RowActionStyle
|
||||
};
|
||||
use cacao::listview::{ListView, ListViewDelegate, ListViewRow, RowAction, RowActionStyle, RowAnimation, RowEdge};
|
||||
|
||||
use crate::storage::{dispatch_ui, Message, Todos, TodoStatus};
|
||||
use crate::storage::{dispatch_ui, Message, TodoStatus, Todos};
|
||||
|
||||
mod row;
|
||||
use row::TodoViewRow;
|
||||
|
@ -15,7 +12,7 @@ use row::TodoViewRow;
|
|||
/// An identifier for the cell(s) we dequeue.
|
||||
const TODO_ROW: &'static str = "TodoViewRowCell";
|
||||
|
||||
/// The list view for todos.
|
||||
/// The list view for todos.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TodosListView {
|
||||
view: Option<ListView>,
|
||||
|
@ -33,10 +30,10 @@ impl TodosListView {
|
|||
view.set_row_actions_visible(false);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Message::MarkTodoIncomplete(row) => {
|
||||
self.todos.with_mut(row, |todo| todo.status = TodoStatus::Incomplete);
|
||||
|
||||
|
||||
if let Some(view) = &self.view {
|
||||
view.reload_rows(&[row]);
|
||||
view.set_row_actions_visible(false);
|
||||
|
@ -77,7 +74,7 @@ impl ListViewDelegate for TodosListView {
|
|||
/// configuration.
|
||||
fn item_for(&self, row: usize) -> ListViewRow {
|
||||
let mut view = self.view.as_ref().unwrap().dequeue::<TodoViewRow>(TODO_ROW);
|
||||
|
||||
|
||||
if let Some(view) = &mut view.delegate {
|
||||
self.todos.with(row, |todo| view.configure_with(todo));
|
||||
}
|
||||
|
@ -97,15 +94,23 @@ impl ListViewDelegate for TodosListView {
|
|||
|
||||
self.todos.with(row, |todo| match todo.status {
|
||||
TodoStatus::Complete => {
|
||||
actions.push(RowAction::new("Mark Incomplete", RowActionStyle::Destructive, move |_action, row| {
|
||||
dispatch_ui(Message::MarkTodoIncomplete(row));
|
||||
}));
|
||||
actions.push(RowAction::new(
|
||||
"Mark Incomplete",
|
||||
RowActionStyle::Destructive,
|
||||
move |_action, row| {
|
||||
dispatch_ui(Message::MarkTodoIncomplete(row));
|
||||
}
|
||||
));
|
||||
},
|
||||
|
||||
TodoStatus::Incomplete => {
|
||||
actions.push(RowAction::new("Mark Complete", RowActionStyle::Regular, move |_action, row| {
|
||||
dispatch_ui(Message::MarkTodoComplete(row));
|
||||
}));
|
||||
actions.push(RowAction::new(
|
||||
"Mark Complete",
|
||||
RowActionStyle::Regular,
|
||||
move |_action, row| {
|
||||
dispatch_ui(Message::MarkTodoComplete(row));
|
||||
}
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use cacao::color::Color;
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::text::{Font, Label, LineBreakMode};
|
||||
use cacao::view::{View, ViewDelegate};
|
||||
use cacao::color::Color;
|
||||
|
||||
use crate::storage::{Todo, TodoStatus};
|
||||
|
||||
|
@ -34,7 +34,7 @@ impl TodoViewRow {
|
|||
|
||||
impl ViewDelegate for TodoViewRow {
|
||||
const NAME: &'static str = "TodoViewRow";
|
||||
|
||||
|
||||
/// Called when the view is first created; handles setup of layout and associated styling that
|
||||
/// doesn't change.
|
||||
fn did_load(&mut self, view: View) {
|
||||
|
@ -50,7 +50,6 @@ impl ViewDelegate for TodoViewRow {
|
|||
self.title.top.constraint_equal_to(&view.top).offset(16.),
|
||||
self.title.leading.constraint_equal_to(&view.leading).offset(16.),
|
||||
self.title.trailing.constraint_equal_to(&view.trailing).offset(-16.),
|
||||
|
||||
self.status.top.constraint_equal_to(&self.title.bottom).offset(8.),
|
||||
self.status.leading.constraint_equal_to(&view.leading).offset(16.),
|
||||
self.status.trailing.constraint_equal_to(&view.trailing).offset(-16.),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! The main Todos window.
|
||||
|
||||
use cacao::appkit::window::{Window, WindowDelegate};
|
||||
use cacao::appkit::toolbar::Toolbar;
|
||||
use cacao::appkit::window::{Window, WindowDelegate};
|
||||
use cacao::view::ViewController;
|
||||
|
||||
use crate::storage::Message;
|
||||
|
@ -16,7 +16,7 @@ mod list;
|
|||
|
||||
pub struct TodosWindow {
|
||||
pub content: ViewController<TodosContentView>,
|
||||
pub toolbar: Toolbar<TodosToolbar>,
|
||||
pub toolbar: Toolbar<TodosToolbar>
|
||||
}
|
||||
|
||||
impl TodosWindow {
|
||||
|
@ -26,7 +26,7 @@ impl TodosWindow {
|
|||
toolbar: Toolbar::new("TodosToolbar", TodosToolbar::default())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn on_message(&self, message: Message) {
|
||||
if let Some(delegate) = &self.content.view.delegate {
|
||||
delegate.on_message(message);
|
||||
|
@ -36,8 +36,8 @@ impl TodosWindow {
|
|||
|
||||
impl WindowDelegate for TodosWindow {
|
||||
const NAME: &'static str = "TodosWindow";
|
||||
|
||||
fn did_load(&mut self, window: Window) {
|
||||
|
||||
fn did_load(&mut self, window: Window) {
|
||||
window.set_autosave_name("TodosWindow");
|
||||
window.set_minimum_content_size(400, 400);
|
||||
window.set_movable_by_background(true);
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
//! The main Todos window toolbar. Contains a button to enable adding a new task.
|
||||
|
||||
use cacao::appkit::toolbar::{ItemIdentifier, Toolbar, ToolbarDelegate, ToolbarDisplayMode, ToolbarItem};
|
||||
use cacao::button::Button;
|
||||
use cacao::appkit::toolbar::{
|
||||
Toolbar, ToolbarDelegate, ToolbarItem,
|
||||
ToolbarDisplayMode, ItemIdentifier
|
||||
};
|
||||
|
||||
use crate::storage::{dispatch_ui, Message};
|
||||
|
||||
|
@ -17,7 +14,7 @@ impl Default for TodosToolbar {
|
|||
let mut item = ToolbarItem::new("AddTodoButton");
|
||||
item.set_title("Add Todo");
|
||||
item.set_button(Button::new("+ New"));
|
||||
|
||||
|
||||
item.set_action(|| {
|
||||
dispatch_ui(Message::OpenNewTodoSheet);
|
||||
});
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
|
||||
use std::sync::RwLock;
|
||||
|
||||
use cacao::appkit::window::{Window, WindowConfig, WindowStyle, WindowDelegate, WindowToolbarStyle};
|
||||
use cacao::appkit::window::{Window, WindowConfig, WindowDelegate, WindowStyle, WindowToolbarStyle};
|
||||
use cacao::notification_center::Dispatcher;
|
||||
|
||||
use crate::storage::Message;
|
||||
|
||||
use crate::add::AddNewTodoWindow;
|
||||
use crate::todos::TodosWindow;
|
||||
use crate::preferences::PreferencesWindow;
|
||||
use crate::todos::TodosWindow;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WindowManager {
|
||||
|
@ -29,7 +29,7 @@ where
|
|||
F: Fn() -> (WindowConfig, T)
|
||||
{
|
||||
let mut lock = window.write().unwrap();
|
||||
|
||||
|
||||
if let Some(win) = &*lock {
|
||||
win.show();
|
||||
} else {
|
||||
|
@ -42,9 +42,7 @@ where
|
|||
|
||||
impl WindowManager {
|
||||
pub fn open_main(&self) {
|
||||
open_or_show(&self.main, || (
|
||||
WindowConfig::default(), TodosWindow::new()
|
||||
));
|
||||
open_or_show(&self.main, || (WindowConfig::default(), TodosWindow::new()));
|
||||
}
|
||||
|
||||
/// When we run a sheet, we want to run it on our main window, which is all
|
||||
|
@ -55,7 +53,7 @@ impl WindowManager {
|
|||
F: Fn() + Send + Sync + 'static
|
||||
{
|
||||
let main = self.main.write().unwrap();
|
||||
|
||||
|
||||
if let Some(main_window) = &*main {
|
||||
main_window.begin_sheet(window, completion);
|
||||
}
|
||||
|
@ -100,8 +98,10 @@ impl WindowManager {
|
|||
config.set_initial_dimensions(100., 100., 400., 400.);
|
||||
|
||||
config.set_styles(&[
|
||||
WindowStyle::Resizable, WindowStyle::Miniaturizable,
|
||||
WindowStyle::Closable, WindowStyle::Titled
|
||||
WindowStyle::Resizable,
|
||||
WindowStyle::Miniaturizable,
|
||||
WindowStyle::Closable,
|
||||
WindowStyle::Titled
|
||||
]);
|
||||
|
||||
config.toolbar_style = WindowToolbarStyle::Preferences;
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
|
||||
use cacao::webview::{WebView, WebViewConfig, WebViewDelegate};
|
||||
|
||||
use cacao::macos::{App, AppDelegate};
|
||||
use cacao::macos::menu::{Menu, MenuItem};
|
||||
use cacao::macos::toolbar::Toolbar;
|
||||
use cacao::macos::window::{Window, WindowConfig, WindowDelegate, WindowToolbarStyle};
|
||||
use cacao::macos::{App, AppDelegate};
|
||||
|
||||
struct BasicApp {
|
||||
window: Window<AppWindow>
|
||||
|
@ -38,8 +38,8 @@ impl WebViewDelegate for WebViewInstance {
|
|||
<h1>Welcome 🍫</h1>
|
||||
<a href="/hello.html">Link</a>
|
||||
</body>
|
||||
</html>"#;
|
||||
|
||||
</html>"#;
|
||||
|
||||
let link_html = r#"
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
@ -52,12 +52,12 @@ impl WebViewDelegate for WebViewInstance {
|
|||
<h1>Hello!</h1>
|
||||
<a href="/index.html">Back home</a>
|
||||
</body>
|
||||
</html>"#;
|
||||
</html>"#;
|
||||
|
||||
return match requested_asset_path.as_str() {
|
||||
"/hello.html" => Some(link_html.as_bytes().into()),
|
||||
_ => Some(index_html.as_bytes().into()),
|
||||
}
|
||||
_ => Some(index_html.as_bytes().into())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,5 +100,6 @@ impl WindowDelegate for AppWindow {
|
|||
fn main() {
|
||||
App::new("com.test.window", BasicApp {
|
||||
window: Window::with(WindowConfig::default(), AppWindow::new())
|
||||
}).run();
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! This example showcases setting up a basic application and window.
|
||||
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
use cacao::appkit::menu::{Menu, MenuItem};
|
||||
use cacao::appkit::window::Window;
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
|
||||
#[derive(Default)]
|
||||
struct BasicApp {
|
||||
|
@ -19,23 +19,16 @@ impl AppDelegate for BasicApp {
|
|||
MenuItem::HideOthers,
|
||||
MenuItem::ShowAll,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Quit
|
||||
MenuItem::Quit,
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![
|
||||
MenuItem::CloseWindow
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![
|
||||
MenuItem::EnterFullScreen
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![MenuItem::CloseWindow]),
|
||||
Menu::new("View", vec![MenuItem::EnterFullScreen]),
|
||||
Menu::new("Window", vec![
|
||||
MenuItem::Minimize,
|
||||
MenuItem::Zoom,
|
||||
MenuItem::Separator,
|
||||
MenuItem::new("Bring All to Front")
|
||||
])
|
||||
MenuItem::new("Bring All to Front"),
|
||||
]),
|
||||
]);
|
||||
|
||||
App::activate();
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
//!
|
||||
//! If you're not using that, you can probably get by fine with a standard `NSWindow`.
|
||||
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
use cacao::appkit::menu::{Menu, MenuItem};
|
||||
use cacao::appkit::window::{Window, WindowConfig, WindowController, WindowDelegate};
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
|
||||
struct BasicApp {
|
||||
window: WindowController<MyWindow>
|
||||
|
@ -22,23 +22,16 @@ impl AppDelegate for BasicApp {
|
|||
MenuItem::HideOthers,
|
||||
MenuItem::ShowAll,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Quit
|
||||
MenuItem::Quit,
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![
|
||||
MenuItem::CloseWindow
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![
|
||||
MenuItem::EnterFullScreen
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![MenuItem::CloseWindow]),
|
||||
Menu::new("View", vec![MenuItem::EnterFullScreen]),
|
||||
Menu::new("Window", vec![
|
||||
MenuItem::Minimize,
|
||||
MenuItem::Zoom,
|
||||
MenuItem::Separator,
|
||||
MenuItem::new("Bring All to Front")
|
||||
])
|
||||
MenuItem::new("Bring All to Front"),
|
||||
]),
|
||||
]);
|
||||
|
||||
App::activate();
|
||||
|
@ -70,5 +63,6 @@ impl WindowDelegate for MyWindow {
|
|||
fn main() {
|
||||
App::new("com.test.window-delegate", BasicApp {
|
||||
window: WindowController::with(WindowConfig::default(), MyWindow::default())
|
||||
}).run();
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
//! This example showcases setting up a basic application and window delegate.
|
||||
//! Window Delegate's give you lifecycle methods that you can respond to.
|
||||
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
use cacao::appkit::menu::{Menu, MenuItem};
|
||||
use cacao::appkit::window::{Window, WindowConfig, WindowDelegate};
|
||||
use cacao::appkit::{App, AppDelegate};
|
||||
|
||||
struct BasicApp {
|
||||
window: Window<MyWindow>
|
||||
|
@ -19,27 +19,20 @@ impl AppDelegate for BasicApp {
|
|||
MenuItem::HideOthers,
|
||||
MenuItem::ShowAll,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Quit
|
||||
MenuItem::Quit,
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![
|
||||
MenuItem::CloseWindow
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![
|
||||
MenuItem::EnterFullScreen
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![MenuItem::CloseWindow]),
|
||||
Menu::new("View", vec![MenuItem::EnterFullScreen]),
|
||||
Menu::new("Window", vec![
|
||||
MenuItem::Minimize,
|
||||
MenuItem::Zoom,
|
||||
MenuItem::Separator,
|
||||
MenuItem::new("Bring All to Front")
|
||||
])
|
||||
MenuItem::new("Bring All to Front"),
|
||||
]),
|
||||
]);
|
||||
|
||||
App::activate();
|
||||
|
||||
|
||||
self.window.show();
|
||||
}
|
||||
|
||||
|
@ -80,5 +73,6 @@ impl WindowDelegate for MyWindow {
|
|||
fn main() {
|
||||
App::new("com.test.window-delegate", BasicApp {
|
||||
window: Window::with(WindowConfig::default(), MyWindow::default())
|
||||
}).run();
|
||||
})
|
||||
.run();
|
||||
}
|
||||
|
|
6
rustfmt.toml
Normal file
6
rustfmt.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
max_width = 130
|
||||
match_block_trailing_comma = true
|
||||
|
||||
# Nightly
|
||||
overflow_delimited_expr = true
|
||||
trailing_comma = "Never"
|
|
@ -9,13 +9,13 @@
|
|||
//!
|
||||
//! ```rust
|
||||
//! use cacao::appkit::{App, AppDelegate, Alert};
|
||||
//!
|
||||
//!
|
||||
//! #[derive(Default)]
|
||||
//! struct ExampleApp;
|
||||
//!
|
||||
//! impl AppDelegate {
|
||||
//! fn did_finish_launching(&self) {
|
||||
//!
|
||||
//!
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
|
@ -24,9 +24,9 @@
|
|||
//! }
|
||||
//! ```
|
||||
|
||||
use objc_id::Id;
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, NSString};
|
||||
|
||||
|
@ -45,9 +45,9 @@ impl Alert {
|
|||
|
||||
Alert(unsafe {
|
||||
let alert: id = msg_send![class!(NSAlert), new];
|
||||
let _: () = msg_send![alert, setMessageText:title];
|
||||
let _: () = msg_send![alert, setInformativeText:message];
|
||||
let _: () = msg_send![alert, addButtonWithTitle:ok];
|
||||
let _: () = msg_send![alert, setMessageText: title];
|
||||
let _: () = msg_send![alert, setInformativeText: message];
|
||||
let _: () = msg_send![alert, addButtonWithTitle: ok];
|
||||
Id::from_ptr(alert)
|
||||
})
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ impl Alert {
|
|||
/// Shows this alert as a modal.
|
||||
pub fn show(&self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.0, runModal];
|
||||
let _: () = msg_send![&*self.0, runModal];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@ impl AnimationContext {
|
|||
/// Sets the animation duration.
|
||||
pub fn set_duration(&mut self, duration: f64) {
|
||||
unsafe {
|
||||
let _: () = msg_send![self.0, setDuration:duration];
|
||||
let _: () = msg_send![self.0, setDuration: duration];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Pass it a block, and the changes in that block will be animated, provided they're
|
||||
/// properties that support animation.
|
||||
///
|
||||
|
@ -39,7 +39,7 @@ impl AnimationContext {
|
|||
|
||||
unsafe {
|
||||
//let context: id = msg_send![class!(NSAnimationContext), currentContext];
|
||||
let _: () = msg_send![class!(NSAnimationContext), runAnimationGroup:block];
|
||||
let _: () = msg_send![class!(NSAnimationContext), runAnimationGroup: block];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ impl AnimationContext {
|
|||
|
||||
unsafe {
|
||||
//let context: id = msg_send![class!(NSAnimationContext), currentContext];
|
||||
let _: () = msg_send![class!(NSAnimationContext), runAnimationGroup:block
|
||||
let _: () = msg_send![class!(NSAnimationContext), runAnimationGroup:block
|
||||
completionHandler:completion_block];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::sync::Once;
|
|||
|
||||
use objc::class;
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class};
|
||||
use objc::runtime::Class;
|
||||
|
||||
/// Used for injecting a custom NSApplication. Currently does nothing.
|
||||
pub(crate) fn register_app_class() -> *const Class {
|
||||
|
@ -19,7 +19,5 @@ pub(crate) fn register_app_class() -> *const Class {
|
|||
APP_CLASS = decl.register();
|
||||
});
|
||||
|
||||
unsafe {
|
||||
APP_CLASS
|
||||
}
|
||||
unsafe { APP_CLASS }
|
||||
}
|
||||
|
|
|
@ -7,16 +7,16 @@ use std::sync::Once;
|
|||
|
||||
use block::Block;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use url::Url;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::foundation::{id, nil, to_bool, BOOL, YES, NO, NSUInteger, NSArray, NSString};
|
||||
use crate::appkit::app::{APP_PTR, AppDelegate};
|
||||
use crate::appkit::app::{AppDelegate, APP_PTR};
|
||||
use crate::appkit::printing::PrintSettings;
|
||||
use crate::error::Error;
|
||||
use crate::foundation::{id, nil, to_bool, NSArray, NSString, NSUInteger, BOOL, NO, YES};
|
||||
use crate::user_activity::UserActivity;
|
||||
|
||||
#[cfg(feature = "cloudkit")]
|
||||
|
@ -33,78 +33,78 @@ fn app<T>(this: &Object) -> &T {
|
|||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationWillFinishLaunching` notification.
|
||||
extern fn will_finish_launching<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_finish_launching<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).will_finish_launching();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationDidFinishLaunching` notification.
|
||||
extern fn did_finish_launching<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_finish_launching<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).did_finish_launching();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationWillBecomeActive` notification.
|
||||
extern fn will_become_active<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_become_active<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).will_become_active();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationDidBecomeActive` notification.
|
||||
extern fn did_become_active<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_become_active<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).did_become_active();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationWillResignActive` notification.
|
||||
extern fn will_resign_active<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_resign_active<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).will_resign_active();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationDidResignActive` notification.
|
||||
extern fn did_resign_active<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_resign_active<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).did_resign_active();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a 'applicationShouldTerminate:` notification.
|
||||
extern fn should_terminate<T: AppDelegate>(this: &Object, _: Sel, _: id) -> NSUInteger {
|
||||
extern "C" fn should_terminate<T: AppDelegate>(this: &Object, _: Sel, _: id) -> NSUInteger {
|
||||
app::<T>(this).should_terminate().into()
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationWillTerminate:` notification.
|
||||
extern fn will_terminate<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_terminate<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).will_terminate();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationWillHide:` notification.
|
||||
extern fn will_hide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_hide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).will_hide();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationDidHide:` notification.
|
||||
extern fn did_hide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_hide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).did_hide();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationWillUnhide:` notification.
|
||||
extern fn will_unhide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_unhide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).will_unhide();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationDidUnhide:` notification.
|
||||
extern fn did_unhide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_unhide<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).did_unhide();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationWillUpdate:` notification.
|
||||
extern fn will_update<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_update<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).will_update();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a `applicationDidUpdate:` notification.
|
||||
extern fn did_update<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_update<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).did_update();
|
||||
}
|
||||
|
||||
/// Fires when the Application Delegate receives a
|
||||
/// `applicationShouldHandleReopen:hasVisibleWindows:` notification.
|
||||
extern fn should_handle_reopen<T: AppDelegate>(this: &Object, _: Sel, _: id, has_visible_windows: BOOL) -> BOOL {
|
||||
extern "C" fn should_handle_reopen<T: AppDelegate>(this: &Object, _: Sel, _: id, has_visible_windows: BOOL) -> BOOL {
|
||||
match app::<T>(this).should_handle_reopen(to_bool(has_visible_windows)) {
|
||||
true => YES,
|
||||
false => NO
|
||||
|
@ -113,7 +113,7 @@ extern fn should_handle_reopen<T: AppDelegate>(this: &Object, _: Sel, _: id, has
|
|||
|
||||
/// Fires when the application delegate receives a `applicationDockMenu:` request.
|
||||
// @TODO: Make this return Vec<MenuItem>.
|
||||
extern fn dock_menu<T: AppDelegate>(this: &Object, _: Sel, _: id) -> id {
|
||||
extern "C" fn dock_menu<T: AppDelegate>(this: &Object, _: Sel, _: id) -> id {
|
||||
match app::<T>(this).dock_menu() {
|
||||
Some(mut menu) => &mut *menu.0,
|
||||
None => nil
|
||||
|
@ -121,19 +121,19 @@ extern fn dock_menu<T: AppDelegate>(this: &Object, _: Sel, _: id) -> id {
|
|||
}
|
||||
|
||||
/// Fires when the application delegate receives a `application:willPresentError:` notification.
|
||||
extern fn will_present_error<T: AppDelegate>(this: &Object, _: Sel, _: id, error: id) -> id {
|
||||
extern "C" fn will_present_error<T: AppDelegate>(this: &Object, _: Sel, _: id, error: id) -> id {
|
||||
let error = Error::new(error);
|
||||
app::<T>(this).will_present_error(error).into_nserror()
|
||||
}
|
||||
|
||||
/// Fires when the application receives a `applicationDidChangeScreenParameters:` notification.
|
||||
extern fn did_change_screen_parameters<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_change_screen_parameters<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).did_change_screen_parameters();
|
||||
}
|
||||
|
||||
/// Fires when the application receives a `application:willContinueUserActivityWithType:`
|
||||
/// notification.
|
||||
extern fn will_continue_user_activity_with_type<T: AppDelegate>(this: &Object, _: Sel, _: id, activity_type: id) -> BOOL {
|
||||
extern "C" fn will_continue_user_activity_with_type<T: AppDelegate>(this: &Object, _: Sel, _: id, activity_type: id) -> BOOL {
|
||||
let activity = NSString::retain(activity_type);
|
||||
|
||||
match app::<T>(this).will_continue_user_activity(activity.to_str()) {
|
||||
|
@ -143,15 +143,15 @@ extern fn will_continue_user_activity_with_type<T: AppDelegate>(this: &Object, _
|
|||
}
|
||||
|
||||
/// Fires when the application receives a `application:continueUserActivity:restorationHandler:` notification.
|
||||
extern fn continue_user_activity<T: AppDelegate>(this: &Object, _: Sel, _: id, activity: id, handler: id) -> BOOL {
|
||||
extern "C" fn continue_user_activity<T: AppDelegate>(this: &Object, _: Sel, _: id, activity: id, handler: id) -> BOOL {
|
||||
// @TODO: This needs to support restorable objects, but it involves a larger question about how
|
||||
// much `NSObject` retainping we want to do here. For now, pass the handler for whenever it's
|
||||
// useful.
|
||||
// useful.
|
||||
let activity = UserActivity::with_inner(activity);
|
||||
|
||||
match app::<T>(this).continue_user_activity(activity, || unsafe {
|
||||
let handler = handler as *const Block<(id,), c_void>;
|
||||
(*handler).call((nil,));
|
||||
(*handler).call((nil,));
|
||||
}) {
|
||||
true => YES,
|
||||
false => NO
|
||||
|
@ -160,57 +160,52 @@ extern fn continue_user_activity<T: AppDelegate>(this: &Object, _: Sel, _: id, a
|
|||
|
||||
/// Fires when the application receives a
|
||||
/// `application:didFailToContinueUserActivityWithType:error:` message.
|
||||
extern fn failed_to_continue_user_activity<T: AppDelegate>(this: &Object, _: Sel, _: id, activity_type: id, error: id) {
|
||||
app::<T>(this).failed_to_continue_user_activity(
|
||||
NSString::retain(activity_type).to_str(),
|
||||
Error::new(error)
|
||||
);
|
||||
extern "C" fn failed_to_continue_user_activity<T: AppDelegate>(this: &Object, _: Sel, _: id, activity_type: id, error: id) {
|
||||
app::<T>(this).failed_to_continue_user_activity(NSString::retain(activity_type).to_str(), Error::new(error));
|
||||
}
|
||||
|
||||
/// Fires when the application receives a `application:didUpdateUserActivity:` message.
|
||||
extern fn did_update_user_activity<T: AppDelegate>(this: &Object, _: Sel, _: id, activity: id) {
|
||||
extern "C" fn did_update_user_activity<T: AppDelegate>(this: &Object, _: Sel, _: id, activity: id) {
|
||||
let activity = UserActivity::with_inner(activity);
|
||||
app::<T>(this).updated_user_activity(activity);
|
||||
}
|
||||
|
||||
/// Fires when the application receives a `application:didRegisterForRemoteNotificationsWithDeviceToken:` message.
|
||||
extern fn registered_for_remote_notifications<T: AppDelegate>(_this: &Object, _: Sel, _: id, _: id) {
|
||||
|
||||
}
|
||||
extern "C" fn registered_for_remote_notifications<T: AppDelegate>(_this: &Object, _: Sel, _: id, _: id) {}
|
||||
|
||||
/// Fires when the application receives a `application:didFailToRegisterForRemoteNotificationsWithError:` message.
|
||||
extern fn failed_to_register_for_remote_notifications<T: AppDelegate>(this: &Object, _: Sel, _: id, error: id) {
|
||||
extern "C" fn failed_to_register_for_remote_notifications<T: AppDelegate>(this: &Object, _: Sel, _: id, error: id) {
|
||||
app::<T>(this).failed_to_register_for_remote_notifications(Error::new(error));
|
||||
}
|
||||
|
||||
/// Fires when the application receives a `application:didReceiveRemoteNotification:` message.
|
||||
extern fn did_receive_remote_notification<T: AppDelegate>(_this: &Object, _: Sel, _: id, _: id) {
|
||||
|
||||
}
|
||||
extern "C" fn did_receive_remote_notification<T: AppDelegate>(_this: &Object, _: Sel, _: id, _: id) {}
|
||||
|
||||
/// Fires when the application receives a `application:userDidAcceptCloudKitShareWithMetadata:`
|
||||
/// message.
|
||||
#[cfg(feature = "cloudkit")]
|
||||
extern fn accepted_cloudkit_share<T: AppDelegate>(this: &Object, _: Sel, _: id, metadata: id) {
|
||||
extern "C" fn accepted_cloudkit_share<T: AppDelegate>(this: &Object, _: Sel, _: id, metadata: id) {
|
||||
let share = CKShareMetaData::with_inner(metadata);
|
||||
app::<T>(this).user_accepted_cloudkit_share(share);
|
||||
}
|
||||
|
||||
/// Fires when the application receives an `application:openURLs` message.
|
||||
extern fn open_urls<T: AppDelegate>(this: &Object, _: Sel, _: id, file_urls: id) {
|
||||
let urls = NSArray::retain(file_urls).map(|url| {
|
||||
let uri = NSString::retain(unsafe {
|
||||
msg_send![url, absoluteString]
|
||||
});
|
||||
|
||||
Url::parse(uri.to_str())
|
||||
}).into_iter().filter_map(|url| url.ok()).collect();
|
||||
extern "C" fn open_urls<T: AppDelegate>(this: &Object, _: Sel, _: id, file_urls: id) {
|
||||
let urls = NSArray::retain(file_urls)
|
||||
.map(|url| {
|
||||
let uri = NSString::retain(unsafe { msg_send![url, absoluteString] });
|
||||
|
||||
Url::parse(uri.to_str())
|
||||
})
|
||||
.into_iter()
|
||||
.filter_map(|url| url.ok())
|
||||
.collect();
|
||||
|
||||
app::<T>(this).open_urls(urls);
|
||||
}
|
||||
|
||||
/// Fires when the application receives an `application:openFileWithoutUI:` message.
|
||||
extern fn open_file_without_ui<T: AppDelegate>(this: &Object, _: Sel, _: id, file: id) -> BOOL {
|
||||
extern "C" fn open_file_without_ui<T: AppDelegate>(this: &Object, _: Sel, _: id, file: id) -> BOOL {
|
||||
let filename = NSString::retain(file);
|
||||
|
||||
match app::<T>(this).open_file_without_ui(filename.to_str()) {
|
||||
|
@ -220,7 +215,7 @@ extern fn open_file_without_ui<T: AppDelegate>(this: &Object, _: Sel, _: id, fil
|
|||
}
|
||||
|
||||
/// Fired when the application receives an `applicationShouldOpenUntitledFile:` message.
|
||||
extern fn should_open_untitled_file<T: AppDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
extern "C" fn should_open_untitled_file<T: AppDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
match app::<T>(this).should_open_untitled_file() {
|
||||
true => YES,
|
||||
false => NO
|
||||
|
@ -228,7 +223,7 @@ extern fn should_open_untitled_file<T: AppDelegate>(this: &Object, _: Sel, _: id
|
|||
}
|
||||
|
||||
/// Fired when the application receives an `applicationShouldTerminateAfterLastWindowClosed:` message.
|
||||
extern fn should_terminate_after_last_window_closed<T: AppDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
extern "C" fn should_terminate_after_last_window_closed<T: AppDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
match app::<T>(this).should_terminate_after_last_window_closed() {
|
||||
true => YES,
|
||||
false => NO
|
||||
|
@ -236,7 +231,7 @@ extern fn should_terminate_after_last_window_closed<T: AppDelegate>(this: &Objec
|
|||
}
|
||||
|
||||
/// Fired when the application receives an `applicationOpenUntitledFile:` message.
|
||||
extern fn open_untitled_file<T: AppDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
extern "C" fn open_untitled_file<T: AppDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
match app::<T>(this).open_untitled_file() {
|
||||
true => YES,
|
||||
false => NO
|
||||
|
@ -244,7 +239,7 @@ extern fn open_untitled_file<T: AppDelegate>(this: &Object, _: Sel, _: id) -> BO
|
|||
}
|
||||
|
||||
/// Fired when the application receives an `application:openTempFile:` message.
|
||||
extern fn open_temp_file<T: AppDelegate>(this: &Object, _: Sel, _: id, filename: id) -> BOOL {
|
||||
extern "C" fn open_temp_file<T: AppDelegate>(this: &Object, _: Sel, _: id, filename: id) -> BOOL {
|
||||
let filename = NSString::retain(filename);
|
||||
|
||||
match app::<T>(this).open_temp_file(filename.to_str()) {
|
||||
|
@ -254,7 +249,7 @@ extern fn open_temp_file<T: AppDelegate>(this: &Object, _: Sel, _: id, filename:
|
|||
}
|
||||
|
||||
/// Fired when the application receives an `application:printFile:` message.
|
||||
extern fn print_file<T: AppDelegate>(this: &Object, _: Sel, _: id, file: id) -> BOOL {
|
||||
extern "C" fn print_file<T: AppDelegate>(this: &Object, _: Sel, _: id, file: id) -> BOOL {
|
||||
let filename = NSString::retain(file);
|
||||
|
||||
match app::<T>(this).print_file(filename.to_str()) {
|
||||
|
@ -265,10 +260,15 @@ extern fn print_file<T: AppDelegate>(this: &Object, _: Sel, _: id, file: id) ->
|
|||
|
||||
/// Fired when the application receives an `application:printFiles:withSettings:showPrintPanels:`
|
||||
/// message.
|
||||
extern fn print_files<T: AppDelegate>(this: &Object, _: Sel, _: id, files: id, settings: id, show_print_panels: BOOL) -> NSUInteger {
|
||||
let files = NSArray::retain(files).map(|file| {
|
||||
NSString::retain(file).to_str().to_string()
|
||||
});
|
||||
extern "C" fn print_files<T: AppDelegate>(
|
||||
this: &Object,
|
||||
_: Sel,
|
||||
_: id,
|
||||
files: id,
|
||||
settings: id,
|
||||
show_print_panels: BOOL
|
||||
) -> NSUInteger {
|
||||
let files = NSArray::retain(files).map(|file| NSString::retain(file).to_str().to_string());
|
||||
|
||||
let settings = PrintSettings::with_inner(settings);
|
||||
|
||||
|
@ -276,14 +276,14 @@ extern fn print_files<T: AppDelegate>(this: &Object, _: Sel, _: id, files: id, s
|
|||
}
|
||||
|
||||
/// Called when the application's occlusion state has changed.
|
||||
extern fn did_change_occlusion_state<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_change_occlusion_state<T: AppDelegate>(this: &Object, _: Sel, _: id) {
|
||||
app::<T>(this).occlusion_state_changed();
|
||||
}
|
||||
|
||||
/// Called when the application receives an `application:delegateHandlesKey:` message.
|
||||
/// Note: this may not fire in sandboxed applications. Apple's documentation is unclear on the
|
||||
/// matter.
|
||||
extern fn delegate_handles_key<T: AppDelegate>(this: &Object, _: Sel, _: id, key: id) -> BOOL {
|
||||
extern "C" fn delegate_handles_key<T: AppDelegate>(this: &Object, _: Sel, _: id, key: id) -> BOOL {
|
||||
let key = NSString::retain(key);
|
||||
|
||||
match app::<T>(this).delegate_handles_key(key.to_str()) {
|
||||
|
@ -305,77 +305,165 @@ pub(crate) fn register_app_delegate_class<T: AppDelegate + AppDelegate>() -> *co
|
|||
decl.add_ivar::<usize>(APP_PTR);
|
||||
|
||||
// Launching Applications
|
||||
decl.add_method(sel!(applicationWillFinishLaunching:), will_finish_launching::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationDidFinishLaunching:), did_finish_launching::<T> as extern fn(&Object, _, _));
|
||||
|
||||
decl.add_method(
|
||||
sel!(applicationWillFinishLaunching:),
|
||||
will_finish_launching::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationDidFinishLaunching:),
|
||||
did_finish_launching::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
|
||||
// Managing Active Status
|
||||
decl.add_method(sel!(applicationWillBecomeActive:), will_become_active::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationDidBecomeActive:), did_become_active::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationWillResignActive:), will_resign_active::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationDidResignActive:), did_resign_active::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(applicationWillBecomeActive:),
|
||||
will_become_active::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationDidBecomeActive:),
|
||||
did_become_active::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationWillResignActive:),
|
||||
will_resign_active::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationDidResignActive:),
|
||||
did_resign_active::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
|
||||
// Terminating Applications
|
||||
decl.add_method(sel!(applicationShouldTerminate:), should_terminate::<T> as extern fn(&Object, _, _) -> NSUInteger);
|
||||
decl.add_method(sel!(applicationWillTerminate:), will_terminate::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationShouldTerminateAfterLastWindowClosed:), should_terminate_after_last_window_closed::<T> as extern fn(&Object, _, _) -> BOOL);
|
||||
decl.add_method(
|
||||
sel!(applicationShouldTerminate:),
|
||||
should_terminate::<T> as extern "C" fn(&Object, _, _) -> NSUInteger
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationWillTerminate:),
|
||||
will_terminate::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationShouldTerminateAfterLastWindowClosed:),
|
||||
should_terminate_after_last_window_closed::<T> as extern "C" fn(&Object, _, _) -> BOOL
|
||||
);
|
||||
|
||||
// Hiding Applications
|
||||
decl.add_method(sel!(applicationWillHide:), will_hide::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationDidHide:), did_hide::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationWillUnhide:), will_unhide::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationDidUnhide:), did_unhide::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationWillHide:), will_hide::<T> as extern "C" fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationDidHide:), did_hide::<T> as extern "C" fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationWillUnhide:), will_unhide::<T> as extern "C" fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationDidUnhide:), did_unhide::<T> as extern "C" fn(&Object, _, _));
|
||||
|
||||
// Managing Windows
|
||||
decl.add_method(sel!(applicationWillUpdate:), will_update::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationDidUpdate:), did_update::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationShouldHandleReopen:hasVisibleWindows:), should_handle_reopen::<T> as extern fn(&Object, _, _, BOOL) -> BOOL);
|
||||
decl.add_method(sel!(applicationWillUpdate:), will_update::<T> as extern "C" fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationDidUpdate:), did_update::<T> as extern "C" fn(&Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(applicationShouldHandleReopen:hasVisibleWindows:),
|
||||
should_handle_reopen::<T> as extern "C" fn(&Object, _, _, BOOL) -> BOOL
|
||||
);
|
||||
|
||||
// Dock Menu
|
||||
decl.add_method(sel!(applicationDockMenu:), dock_menu::<T> as extern fn(&Object, _, _) -> id);
|
||||
decl.add_method(
|
||||
sel!(applicationDockMenu:),
|
||||
dock_menu::<T> as extern "C" fn(&Object, _, _) -> id
|
||||
);
|
||||
|
||||
// Displaying Errors
|
||||
decl.add_method(sel!(application:willPresentError:), will_present_error::<T> as extern fn(&Object, _, _, id) -> id);
|
||||
decl.add_method(
|
||||
sel!(application:willPresentError:),
|
||||
will_present_error::<T> as extern "C" fn(&Object, _, _, id) -> id
|
||||
);
|
||||
|
||||
// Managing the Screen
|
||||
decl.add_method(sel!(applicationDidChangeScreenParameters:), did_change_screen_parameters::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(applicationDidChangeOcclusionState:), did_change_occlusion_state::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(applicationDidChangeScreenParameters:),
|
||||
did_change_screen_parameters::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationDidChangeOcclusionState:),
|
||||
did_change_occlusion_state::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
|
||||
// User Activities
|
||||
decl.add_method(sel!(application:willContinueUserActivityWithType:), will_continue_user_activity_with_type::<T> as extern fn(&Object, _, _, id) -> BOOL);
|
||||
decl.add_method(sel!(application:continueUserActivity:restorationHandler:), continue_user_activity::<T> as extern fn(&Object, _, _, id, id) -> BOOL);
|
||||
decl.add_method(sel!(application:didFailToContinueUserActivityWithType:error:), failed_to_continue_user_activity::<T> as extern fn(&Object, _, _, id, id));
|
||||
decl.add_method(sel!(application:didUpdateUserActivity:), did_update_user_activity::<T> as extern fn(&Object, _, _, id));
|
||||
decl.add_method(
|
||||
sel!(application:willContinueUserActivityWithType:),
|
||||
will_continue_user_activity_with_type::<T> as extern "C" fn(&Object, _, _, id) -> BOOL
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(application:continueUserActivity:restorationHandler:),
|
||||
continue_user_activity::<T> as extern "C" fn(&Object, _, _, id, id) -> BOOL
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(application:didFailToContinueUserActivityWithType:error:),
|
||||
failed_to_continue_user_activity::<T> as extern "C" fn(&Object, _, _, id, id)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(application:didUpdateUserActivity:),
|
||||
did_update_user_activity::<T> as extern "C" fn(&Object, _, _, id)
|
||||
);
|
||||
|
||||
// Handling push notifications
|
||||
decl.add_method(sel!(application:didRegisterForRemoteNotificationsWithDeviceToken:), registered_for_remote_notifications::<T> as extern fn(&Object, _, _, id));
|
||||
decl.add_method(sel!(application:didFailToRegisterForRemoteNotificationsWithError:), failed_to_register_for_remote_notifications::<T> as extern fn(&Object, _, _, id));
|
||||
decl.add_method(sel!(application:didReceiveRemoteNotification:), did_receive_remote_notification::<T> as extern fn(&Object, _, _, id));
|
||||
decl.add_method(
|
||||
sel!(application:didRegisterForRemoteNotificationsWithDeviceToken:),
|
||||
registered_for_remote_notifications::<T> as extern "C" fn(&Object, _, _, id)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(application:didFailToRegisterForRemoteNotificationsWithError:),
|
||||
failed_to_register_for_remote_notifications::<T> as extern "C" fn(&Object, _, _, id)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(application:didReceiveRemoteNotification:),
|
||||
did_receive_remote_notification::<T> as extern "C" fn(&Object, _, _, id)
|
||||
);
|
||||
|
||||
// CloudKit
|
||||
#[cfg(feature = "cloudkit")]
|
||||
decl.add_method(sel!(application:userDidAcceptCloudKitShareWithMetadata:), accepted_cloudkit_share::<T> as extern fn(&Object, _, _, id));
|
||||
decl.add_method(
|
||||
sel!(application:userDidAcceptCloudKitShareWithMetadata:),
|
||||
accepted_cloudkit_share::<T> as extern "C" fn(&Object, _, _, id)
|
||||
);
|
||||
|
||||
// Opening Files
|
||||
decl.add_method(sel!(application:openURLs:), open_urls::<T> as extern fn(&Object, _, _, id));
|
||||
decl.add_method(sel!(application:openFileWithoutUI:), open_file_without_ui::<T> as extern fn(&Object, _, _, id) -> BOOL);
|
||||
decl.add_method(sel!(applicationShouldOpenUntitledFile:), should_open_untitled_file::<T> as extern fn(&Object, _, _) -> BOOL);
|
||||
decl.add_method(sel!(applicationOpenUntitledFile:), open_untitled_file::<T> as extern fn(&Object, _, _) -> BOOL);
|
||||
decl.add_method(sel!(application:openTempFile:), open_temp_file::<T> as extern fn(&Object, _, _, id) -> BOOL);
|
||||
decl.add_method(
|
||||
sel!(application:openURLs:),
|
||||
open_urls::<T> as extern "C" fn(&Object, _, _, id)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(application:openFileWithoutUI:),
|
||||
open_file_without_ui::<T> as extern "C" fn(&Object, _, _, id) -> BOOL
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationShouldOpenUntitledFile:),
|
||||
should_open_untitled_file::<T> as extern "C" fn(&Object, _, _) -> BOOL
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationOpenUntitledFile:),
|
||||
open_untitled_file::<T> as extern "C" fn(&Object, _, _) -> BOOL
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(application:openTempFile:),
|
||||
open_temp_file::<T> as extern "C" fn(&Object, _, _, id) -> BOOL
|
||||
);
|
||||
|
||||
// Printing
|
||||
decl.add_method(sel!(application:printFile:), print_file::<T> as extern fn(&Object, _, _, id) -> BOOL);
|
||||
decl.add_method(sel!(application:printFiles:withSettings:showPrintPanels:), print_files::<T> as extern fn(&Object, _, id, id, id, BOOL) -> NSUInteger);
|
||||
decl.add_method(
|
||||
sel!(application:printFile:),
|
||||
print_file::<T> as extern "C" fn(&Object, _, _, id) -> BOOL
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(application:printFiles:withSettings:showPrintPanels:),
|
||||
print_files::<T> as extern "C" fn(&Object, _, id, id, id, BOOL) -> NSUInteger
|
||||
);
|
||||
|
||||
// @TODO: Restoring Application State
|
||||
// Depends on NSCoder support, which is... welp.
|
||||
|
||||
// Scripting
|
||||
decl.add_method(sel!(application:delegateHandlesKey:), delegate_handles_key::<T> as extern fn(&Object, _, _, id) -> BOOL);
|
||||
decl.add_method(
|
||||
sel!(application:delegateHandlesKey:),
|
||||
delegate_handles_key::<T> as extern "C" fn(&Object, _, _, id) -> BOOL
|
||||
);
|
||||
|
||||
DELEGATE_CLASS = decl.register();
|
||||
});
|
||||
|
||||
unsafe {
|
||||
DELEGATE_CLASS
|
||||
}
|
||||
unsafe { DELEGATE_CLASS }
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@ pub enum TerminateResponse {
|
|||
/// App should not be terminated.
|
||||
Cancel,
|
||||
|
||||
/// It might be fine to proceed with termination later. Returning this value causes
|
||||
/// It might be fine to proceed with termination later. Returning this value causes
|
||||
/// Cocoa to run the run loop until `should_terminate()` returns `true` or `false`.
|
||||
///
|
||||
/// This return value is for primarily for cases where you need to provide alerts
|
||||
/// This return value is for primarily for cases where you need to provide alerts
|
||||
/// in order to decide whether to quit.
|
||||
Later
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ impl From<AppDelegateResponse> for NSUInteger {
|
|||
/// - _`AutoHideMenuBar` and `HideMenuBar` are mutually exclusive: You may specify one or the other, but not both._
|
||||
/// - _If you specify `HideMenuBar`, it must be accompanied by `HideDock`._
|
||||
/// - _If you specify `AutoHideMenuBar`, it must be accompanied by either `HideDock` or `AutoHideDock`._
|
||||
/// - _If you specify any of `DisableProcessSwitching`, `DisableForceQuit`, `DisableSessionTermination`, or `DisableMenuBarTransparency`,
|
||||
/// - _If you specify any of `DisableProcessSwitching`, `DisableForceQuit`, `DisableSessionTermination`, or `DisableMenuBarTransparency`,
|
||||
/// it must be accompanied by either `HideDock` or `AutoHideDock`._
|
||||
/// - _`AutoHideToolbar` may be used only when both `FullScreen` and `AutoHideMenuBar` are also set.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -86,10 +86,10 @@ pub enum PresentationOption {
|
|||
|
||||
/// Menubar is entirely disabled.
|
||||
HideMenuBar,
|
||||
|
||||
|
||||
/// All Apple Menu items are disabled.
|
||||
DisableAppleMenu,
|
||||
|
||||
|
||||
/// The process switching user interface (Command + Tab to cycle through apps) is disabled.
|
||||
DisableProcessSwitching,
|
||||
|
||||
|
@ -121,14 +121,14 @@ impl From<PresentationOption> for NSUInteger {
|
|||
PresentationOption::Default => 0,
|
||||
PresentationOption::AutoHideDock => (1 << 0),
|
||||
PresentationOption::HideDock => (1 << 1),
|
||||
PresentationOption::AutoHideMenuBar => (1 << 2),
|
||||
PresentationOption::AutoHideMenuBar => (1 << 2),
|
||||
PresentationOption::HideMenuBar => (1 << 3),
|
||||
PresentationOption::DisableAppleMenu => (1 << 4),
|
||||
PresentationOption::DisableProcessSwitching => (1 << 5),
|
||||
PresentationOption::DisableForceQuit => (1 << 6),
|
||||
PresentationOption::DisableSessionTermination => (1 << 7),
|
||||
PresentationOption::DisableHideApplication => (1 << 8),
|
||||
PresentationOption::DisableMenuBarTransparency => (1 << 9),
|
||||
PresentationOption::DisableForceQuit => (1 << 6),
|
||||
PresentationOption::DisableSessionTermination => (1 << 7),
|
||||
PresentationOption::DisableHideApplication => (1 << 8),
|
||||
PresentationOption::DisableMenuBarTransparency => (1 << 9),
|
||||
PresentationOption::FullScreen => (1 << 10),
|
||||
PresentationOption::AutoHideToolbar => (1 << 11),
|
||||
PresentationOption::DisableCursorLocationAssistance => (1 << 12)
|
||||
|
@ -142,14 +142,14 @@ impl From<&PresentationOption> for NSUInteger {
|
|||
PresentationOption::Default => 0,
|
||||
PresentationOption::AutoHideDock => (1 << 0),
|
||||
PresentationOption::HideDock => (1 << 1),
|
||||
PresentationOption::AutoHideMenuBar => (1 << 2),
|
||||
PresentationOption::AutoHideMenuBar => (1 << 2),
|
||||
PresentationOption::HideMenuBar => (1 << 3),
|
||||
PresentationOption::DisableAppleMenu => (1 << 4),
|
||||
PresentationOption::DisableProcessSwitching => (1 << 5),
|
||||
PresentationOption::DisableForceQuit => (1 << 6),
|
||||
PresentationOption::DisableSessionTermination => (1 << 7),
|
||||
PresentationOption::DisableHideApplication => (1 << 8),
|
||||
PresentationOption::DisableMenuBarTransparency => (1 << 9),
|
||||
PresentationOption::DisableForceQuit => (1 << 6),
|
||||
PresentationOption::DisableSessionTermination => (1 << 7),
|
||||
PresentationOption::DisableHideApplication => (1 << 8),
|
||||
PresentationOption::DisableMenuBarTransparency => (1 << 9),
|
||||
PresentationOption::FullScreen => (1 << 10),
|
||||
PresentationOption::AutoHideToolbar => (1 << 11),
|
||||
PresentationOption::DisableCursorLocationAssistance => (1 << 12)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//! ```rust,no_run
|
||||
//! use cacao::app::{App, AppDelegate};
|
||||
//! use cacao::window::Window;
|
||||
//!
|
||||
//!
|
||||
//! #[derive(Default)]
|
||||
//! struct BasicApp;
|
||||
//!
|
||||
|
@ -20,7 +20,7 @@
|
|||
//! App::new("com.my.app", BasicApp::default()).run();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//! ## Why do I need to do this?
|
||||
//! A good question. Cocoa does many things for you (e.g, setting up and managing a runloop,
|
||||
//! handling the view/window heirarchy, and so on). This requires certain things happen before your
|
||||
|
@ -29,7 +29,7 @@
|
|||
//! - It ensures that the `sharedApplication` is properly initialized with your delegate.
|
||||
//! - It ensures that Cocoa is put into multi-threaded mode, so standard POSIX threads work as they
|
||||
//! should.
|
||||
//!
|
||||
//!
|
||||
//! ### Platform specificity
|
||||
//! Certain lifecycle events are specific to certain platforms. Where this is the case, the
|
||||
//! documentation makes every effort to note.
|
||||
|
@ -39,13 +39,13 @@ use std::sync::{Arc, Mutex};
|
|||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use objc_id::Id;
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, nil, YES, NO, NSUInteger, AutoReleasePool};
|
||||
use crate::invoker::TargetActionHandler;
|
||||
use crate::appkit::menu::Menu;
|
||||
use crate::foundation::{id, nil, AutoReleasePool, NSUInteger, NO, YES};
|
||||
use crate::invoker::TargetActionHandler;
|
||||
use crate::notification_center::Dispatcher;
|
||||
use crate::utils::activate_cocoa_multithreading;
|
||||
|
||||
|
@ -114,7 +114,7 @@ impl<T, M> fmt::Debug for App<T, M> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> App<T> {
|
||||
impl<T> App<T> {
|
||||
/// Kicks off the NSRunLoop for the NSApplication instance. This blocks when called.
|
||||
/// If you're wondering where to go from here... you need an `AppDelegate` that implements
|
||||
/// `did_finish_launching`. :)
|
||||
|
@ -128,22 +128,25 @@ impl<T> App<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> App<T> where T: AppDelegate + 'static {
|
||||
impl<T> App<T>
|
||||
where
|
||||
T: AppDelegate + 'static
|
||||
{
|
||||
/// Creates an NSAutoReleasePool, configures various NSApplication properties (e.g, activation
|
||||
/// policies), injects an `NSObject` delegate wrapper, and retains everything on the
|
||||
/// Objective-C side of things.
|
||||
pub fn new(_bundle_id: &str, delegate: T) -> Self {
|
||||
//set_bundle_id(bundle_id);
|
||||
|
||||
|
||||
activate_cocoa_multithreading();
|
||||
|
||||
|
||||
let pool = AutoReleasePool::new();
|
||||
|
||||
let objc = unsafe {
|
||||
let app: id = msg_send![register_app_class(), sharedApplication];
|
||||
Id::from_ptr(app)
|
||||
};
|
||||
|
||||
|
||||
let app_delegate = Box::new(delegate);
|
||||
|
||||
let objc_delegate = unsafe {
|
||||
|
@ -151,7 +154,7 @@ impl<T> App<T> where T: AppDelegate + 'static {
|
|||
let delegate: id = msg_send![delegate_class, new];
|
||||
let delegate_ptr: *const T = &*app_delegate;
|
||||
(&mut *delegate).set_ivar(APP_PTR, delegate_ptr as usize);
|
||||
let _: () = msg_send![&*objc, setDelegate:delegate];
|
||||
let _: () = msg_send![&*objc, setDelegate: delegate];
|
||||
Id::from_ptr(delegate)
|
||||
};
|
||||
|
||||
|
@ -163,7 +166,7 @@ impl<T> App<T> where T: AppDelegate + 'static {
|
|||
_message: std::marker::PhantomData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is a very basic "dispatch" mechanism. In macOS, it's critical that UI work happen on the
|
||||
// UI ("main") thread. We can hook into the standard mechanism for this by dispatching on
|
||||
|
@ -171,7 +174,7 @@ impl<T> App<T> where T: AppDelegate + 'static {
|
|||
// for the main queue. They automatically forward through to our registered `AppDelegate`.
|
||||
//
|
||||
// One thing I don't like about GCD is that detecting incorrect thread usage has historically been
|
||||
// a bit... annoying. Here, the `Dispatcher` trait explicitly requires implementing two methods -
|
||||
// a bit... annoying. Here, the `Dispatcher` trait explicitly requires implementing two methods -
|
||||
// one for UI messages, and one for background messages. I think that this helps separate intent
|
||||
// on the implementation side, and makes it a bit easier to detect when a message has come in on
|
||||
// the wrong side.
|
||||
|
@ -180,12 +183,16 @@ impl<T> App<T> where T: AppDelegate + 'static {
|
|||
// ObjC and such is fast enough that for a large class of applications this is workable.
|
||||
//
|
||||
// tl;dr: This is all a bit of a hack, and should go away eventually. :)
|
||||
impl<T, M> App<T, M> where M: Send + Sync + 'static, T: AppDelegate + Dispatcher<Message = M> {
|
||||
impl<T, M> App<T, M>
|
||||
where
|
||||
M: Send + Sync + 'static,
|
||||
T: AppDelegate + Dispatcher<Message = M>
|
||||
{
|
||||
/// Dispatches a message by grabbing the `sharedApplication`, getting ahold of the delegate,
|
||||
/// and passing back through there.
|
||||
pub fn dispatch_main(message: M) {
|
||||
let queue = dispatch::Queue::main();
|
||||
|
||||
|
||||
queue.exec_async(move || unsafe {
|
||||
let app: id = msg_send![register_app_class(), sharedApplication];
|
||||
let app_delegate: id = msg_send![app, delegate];
|
||||
|
@ -199,7 +206,7 @@ impl<T, M> App<T, M> where M: Send + Sync + 'static, T: AppDelegate + Dispatcher
|
|||
/// and passing back through there.
|
||||
pub fn dispatch_background(message: M) {
|
||||
let queue = dispatch::Queue::main();
|
||||
|
||||
|
||||
queue.exec_async(move || unsafe {
|
||||
let app: id = msg_send![register_app_class(), sharedApplication];
|
||||
let app_delegate: id = msg_send![app, delegate];
|
||||
|
@ -217,7 +224,7 @@ impl App {
|
|||
let _: () = msg_send![app, registerForRemoteNotifications];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/// Unregisters for remote notifications from APNS.
|
||||
pub fn unregister_for_remote_notifications() {
|
||||
shared_application(|app| unsafe {
|
||||
|
@ -251,7 +258,7 @@ impl App {
|
|||
pub fn reply_to_open_or_print(response: AppDelegateResponse) {
|
||||
shared_application(|app| unsafe {
|
||||
let r: NSUInteger = response.into();
|
||||
let _: () = msg_send![app, replyToOpenOrPrint:r];
|
||||
let _: () = msg_send![app, replyToOpenOrPrint: r];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -267,14 +274,14 @@ impl App {
|
|||
for menu in menus.iter_mut() {
|
||||
let item: id = msg_send![item_cls, new];
|
||||
let _: () = msg_send![item, setSubmenu:&*menu.0];
|
||||
let _: () = msg_send![main_menu, addItem:item];
|
||||
let _: () = msg_send![main_menu, addItem: item];
|
||||
}
|
||||
|
||||
main_menu
|
||||
};
|
||||
|
||||
shared_application(move |app| unsafe {
|
||||
let _: () = msg_send![app, setMainMenu:main_menu];
|
||||
let _: () = msg_send![app, setMainMenu: main_menu];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -296,7 +303,7 @@ impl App {
|
|||
/// This is typically called when the user chooses to quit via the App menu.
|
||||
pub fn terminate() {
|
||||
shared_application(|app| unsafe {
|
||||
let _: () = msg_send![app, terminate:nil];
|
||||
let _: () = msg_send![app, terminate: nil];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,14 +33,18 @@ pub trait AppDelegate {
|
|||
|
||||
/// Fired when the application is about to resign active state.
|
||||
fn will_resign_active(&self) {}
|
||||
|
||||
|
||||
/// Fired when the user is going to continue an activity.
|
||||
fn will_continue_user_activity(&self, _activity_type: &str) -> bool { false }
|
||||
fn will_continue_user_activity(&self, _activity_type: &str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Fired when data for continuing an activity is available. Currently, the
|
||||
/// `restoration_handler` is not used, but there to communicate intent with what this API will
|
||||
/// eventually be doing.
|
||||
fn continue_user_activity<F: Fn()>(&self, _activity: UserActivity, _restoration_handler: F) -> bool { false }
|
||||
fn continue_user_activity<F: Fn()>(&self, _activity: UserActivity, _restoration_handler: F) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Fired when the activity could not be continued.
|
||||
fn failed_to_continue_user_activity(&self, _activity_type: &str, _error: Error) {}
|
||||
|
@ -59,10 +63,10 @@ pub trait AppDelegate {
|
|||
fn failed_to_register_for_remote_notifications(&self, _error: Error) {}
|
||||
|
||||
/// Fires after the user accepted a CloudKit sharing invitation associated with your
|
||||
/// application.
|
||||
/// application.
|
||||
#[cfg(feature = "cloudkit")]
|
||||
fn user_accepted_cloudkit_share(&self, _share_metadata: CKShareMetaData) {}
|
||||
|
||||
|
||||
/// Fired before the application terminates. You can use this to do any required cleanup.
|
||||
fn will_terminate(&self) {}
|
||||
|
||||
|
@ -92,92 +96,112 @@ pub trait AppDelegate {
|
|||
|
||||
/// This is fired after the `Quit` menu item has been selected, or after you've called `App::terminate()`.
|
||||
///
|
||||
/// In most cases you just want `TerminateResponse::Now` here, which enables business as usual. If you need,
|
||||
/// In most cases you just want `TerminateResponse::Now` here, which enables business as usual. If you need,
|
||||
/// though, you can cancel the termination via `TerminateResponse::Cancel` to continue something essential. If
|
||||
/// you do this, you'll need to be sure to call `App::reply_to_termination_request()` to circle
|
||||
/// back.
|
||||
fn should_terminate(&self) -> TerminateResponse { TerminateResponse::Now }
|
||||
fn should_terminate(&self) -> TerminateResponse {
|
||||
TerminateResponse::Now
|
||||
}
|
||||
|
||||
/// Called after closing the last open window. Return `true` here if you want
|
||||
/// the application to terminate.
|
||||
fn should_terminate_after_last_window_closed(&self) -> bool { false }
|
||||
fn should_terminate_after_last_window_closed(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Sent by the application to the delegate prior to default behavior to reopen AppleEvents.
|
||||
///
|
||||
/// `has_visible_windows` indicates whether the Application object found any visible windows in your application.
|
||||
/// You can use this value as an indication of whether the application would do anything if you return `true`.
|
||||
///
|
||||
/// Return `true` if you want the application to perform its normal tasks, or `false` if you want the
|
||||
/// Return `true` if you want the application to perform its normal tasks, or `false` if you want the
|
||||
/// application to do nothing. The default implementation of this method returns `true`.
|
||||
///
|
||||
/// Some finer points of discussion, from Apple documentation:
|
||||
///
|
||||
/// These events are sent whenever the Finder reactivates an already running application because someone
|
||||
/// These events are sent whenever the Finder reactivates an already running application because someone
|
||||
/// double-clicked it again or used the dock to activate it.
|
||||
///
|
||||
/// For most document-based applications, an untitled document will be created.
|
||||
///
|
||||
/// [Read more
|
||||
/// here](https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen?language=objc)
|
||||
fn should_handle_reopen(&self, _has_visible_windows: bool) -> bool { true }
|
||||
fn should_handle_reopen(&self, _has_visible_windows: bool) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Supply a dock menu for the application dynamically. The default implementation for this
|
||||
/// method returns `None`, for no menu.
|
||||
fn dock_menu(&self) -> Option<Menu> { None }
|
||||
fn dock_menu(&self) -> Option<Menu> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Fired before the application presents an error message to the user. If you find the error
|
||||
/// to be... not what you want, you can take it, alter it, and return it anew. The default
|
||||
/// implementation of this method simply returns the error as-is.
|
||||
fn will_present_error(&self, error: Error) -> Error { error }
|
||||
fn will_present_error(&self, error: Error) -> Error {
|
||||
error
|
||||
}
|
||||
|
||||
/// Fired when the screen parameters for the application have changed (e.g, the user changed
|
||||
/// something in their settings).
|
||||
fn did_change_screen_parameters(&self) {}
|
||||
|
||||
|
||||
/// Fired when you have a list of `Url`'s to open. This is best explained by quoting the Apple
|
||||
/// documentation verbatim:
|
||||
///
|
||||
/// _"AppKit calls this method when your app is asked to open one or more URL-based resources.
|
||||
/// You must declare the URL types that your app supports in your `Info.plist` file using the `CFBundleURLTypes` key.
|
||||
/// The list can also include URLs for documents for which your app does not have an associated `NSDocument` class.
|
||||
/// _"AppKit calls this method when your app is asked to open one or more URL-based resources.
|
||||
/// You must declare the URL types that your app supports in your `Info.plist` file using the `CFBundleURLTypes` key.
|
||||
/// The list can also include URLs for documents for which your app does not have an associated `NSDocument` class.
|
||||
/// You configure document types by adding the `CFBundleDocumentTypes` key to your Info.plist
|
||||
/// file."
|
||||
///
|
||||
/// Note that since we have this as the de-facto method of handling resource opens, the system
|
||||
/// will _not_ call `application:openFile:` or `application:openFiles`.
|
||||
fn open_urls(&self, _urls: Vec<Url>) { }
|
||||
fn open_urls(&self, _urls: Vec<Url>) {}
|
||||
|
||||
/// Fired when the file is requested to be opened programmatically. This is not a commonly used
|
||||
/// or implemented method.
|
||||
///
|
||||
/// According to Apple:
|
||||
/// According to Apple:
|
||||
///
|
||||
/// _"The method should open the file without bringing up its application’s user interface—that is,
|
||||
/// _"The method should open the file without bringing up its application’s user interface—that is,
|
||||
/// work with the file is under programmatic control of sender, rather than under keyboard control of the user."_
|
||||
///
|
||||
/// It's unclear how supported this is in sandbox environments, so use at your own risk.
|
||||
fn open_file_without_ui(&self, _filename: &str) -> bool { false }
|
||||
fn open_file_without_ui(&self, _filename: &str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Fired when the application is ready and able to open a temporary file.
|
||||
/// Return `true` or `false` here depending on whether the operation was successful.
|
||||
///
|
||||
/// It's your responsibility to remove the temp file.
|
||||
fn open_temp_file(&self, _filename: &str) -> bool { false }
|
||||
fn open_temp_file(&self, _filename: &str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Fired before attempting to open an untitled file. Return `true` here if you want
|
||||
/// `open_untitled_file` to be called by the system.
|
||||
fn should_open_untitled_file(&self) -> bool { false }
|
||||
fn should_open_untitled_file(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Called when the application has asked you to open a new, untitled file.
|
||||
/// Returns a `bool` indicating whether the file was successfully opened or not.
|
||||
fn open_untitled_file(&self) -> bool { true }
|
||||
fn open_untitled_file(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Sent when the user starts up the application on the command line with the -NSPrint option.
|
||||
/// The application terminates immediately after this method returns. For more information,
|
||||
/// cosnult the official Apple documentation.
|
||||
///
|
||||
/// (You probably never need to implement this, but we support it anyway)
|
||||
fn print_file(&self, _filename: &str) -> bool { false }
|
||||
fn print_file(&self, _filename: &str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Called when the user has requested to print some files.
|
||||
///
|
||||
|
@ -195,13 +219,15 @@ pub trait AppDelegate {
|
|||
|
||||
/// Fired when the occlusion state for the app has changed.
|
||||
///
|
||||
/// From Apple's docs, as there's no other way to describe this better: _upon receiving this method, you can query the
|
||||
/// application for its occlusion state. Note that this only notifies about changes in the state of the occlusion, not
|
||||
/// when the occlusion region changes. You can use this method to increase responsiveness and save power by halting any
|
||||
/// From Apple's docs, as there's no other way to describe this better: _upon receiving this method, you can query the
|
||||
/// application for its occlusion state. Note that this only notifies about changes in the state of the occlusion, not
|
||||
/// when the occlusion region changes. You can use this method to increase responsiveness and save power by halting any
|
||||
/// expensive calculations that the user can not see._
|
||||
fn occlusion_state_changed(&self) {}
|
||||
|
||||
/// Fired when the system wants to know whether your application, via scripting, can handle the
|
||||
/// key specifying operations.
|
||||
fn delegate_handles_key(&self, _key: &str) -> bool { false }
|
||||
fn delegate_handles_key(&self, _key: &str) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, YES, NO};
|
||||
use crate::foundation::{id, NO, YES};
|
||||
|
||||
/// Represents a type of cursor that you can associate with mouse movement.
|
||||
/// @TODO: Loading?
|
||||
|
@ -20,19 +20,19 @@ pub enum CursorType {
|
|||
|
||||
/// A pointing hand, like clicking a link.
|
||||
PointingHand,
|
||||
|
||||
|
||||
/// Indicator that something can be resized to the left.
|
||||
ResizeLeft,
|
||||
|
||||
/// Indicator that something can be resized to the right.
|
||||
ResizeRight,
|
||||
|
||||
|
||||
/// Indicator that something can be resized on the horizontal axis.
|
||||
ResizeLeftRight,
|
||||
|
||||
/// Indicates that something can be resized up.
|
||||
ResizeUp,
|
||||
|
||||
|
||||
/// Indicates that something can be resized down.
|
||||
ResizeDown,
|
||||
|
||||
|
@ -58,7 +58,7 @@ pub enum CursorType {
|
|||
/// Used for drag-and-drop usually, will displayu the standard "+" icon next to the cursor.
|
||||
DragCopy,
|
||||
|
||||
/// Indicates a context menu will open.
|
||||
/// Indicates a context menu will open.
|
||||
ContextMenu
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ impl Cursor {
|
|||
CursorType::DragCopy => msg_send![class!(NSCursor), dragCopyCursor],
|
||||
CursorType::ContextMenu => msg_send![class!(NSCursor), contextualMenuCursor]
|
||||
};
|
||||
|
||||
|
||||
let _: () = msg_send![cursor, push];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
//! Generic enums that don't fit anywhere else yet.
|
||||
|
||||
use crate::foundation::{NSUInteger};
|
||||
use crate::foundation::NSUInteger;
|
||||
|
||||
/// Used to set whether and/or how a view or cell draws a focus ring.
|
||||
#[derive(Debug)]
|
||||
pub enum FocusRingType {
|
||||
/// Whatever the default is.
|
||||
Default,
|
||||
|
||||
|
||||
/// None.
|
||||
None,
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
|
||||
use block::ConcreteBlock;
|
||||
|
||||
use objc::runtime::Object;
|
||||
use objc_id::Id;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, nil, NSString};
|
||||
|
||||
|
@ -23,18 +22,14 @@ pub struct Event(pub Id<Object>);
|
|||
|
||||
impl Event {
|
||||
pub(crate) fn new(objc: id) -> Self {
|
||||
Event(unsafe {
|
||||
Id::from_ptr(objc)
|
||||
})
|
||||
Event(unsafe { Id::from_ptr(objc) })
|
||||
}
|
||||
|
||||
pub fn characters(&self) -> String {
|
||||
// @TODO: Check here if key event, invalid otherwise.
|
||||
// @TODO: Figure out if we can just return &str here, since the Objective-C side
|
||||
// should... make it work, I think.
|
||||
let characters = NSString::retain(unsafe {
|
||||
msg_send![&*self.0, characters]
|
||||
});
|
||||
let characters = NSString::retain(unsafe { msg_send![&*self.0, characters] });
|
||||
|
||||
characters.to_string()
|
||||
}
|
||||
|
|
|
@ -6,23 +6,23 @@ use std::fmt;
|
|||
use std::sync::Once;
|
||||
|
||||
use block::ConcreteBlock;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, nil, NSString, NSUInteger};
|
||||
use crate::events::EventModifierFlag;
|
||||
use crate::foundation::{id, nil, NSString, NSUInteger};
|
||||
|
||||
static BLOCK_PTR: &'static str = "cacaoMenuItemBlockPtr";
|
||||
|
||||
/// An Action is just an indirection layer to get around Rust and optimizing
|
||||
/// zero-sum types; without this, pointers to callbacks will end up being
|
||||
/// 0x1, and all point to whatever is there first (unsure if this is due to
|
||||
/// zero-sum types; without this, pointers to callbacks will end up being
|
||||
/// 0x1, and all point to whatever is there first (unsure if this is due to
|
||||
/// Rust or Cocoa or what).
|
||||
///
|
||||
/// Point is, Button aren't created that much in the grand scheme of things,
|
||||
/// and the heap isn't our enemy in a GUI framework anyway. If someone knows
|
||||
/// and the heap isn't our enemy in a GUI framework anyway. If someone knows
|
||||
/// a better way to do this that doesn't require double-boxing, I'm all ears.
|
||||
pub struct Action(Box<dyn Fn() + 'static>);
|
||||
|
||||
|
@ -30,9 +30,7 @@ impl fmt::Debug for Action {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let ptr = format!("{:p}", self.0);
|
||||
|
||||
f.debug_struct("Action")
|
||||
.field("fn", &ptr)
|
||||
.finish()
|
||||
f.debug_struct("Action").field("fn", &ptr).finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,9 +56,9 @@ fn make_menu_item<S: AsRef<str>>(
|
|||
let alloc: id = msg_send![register_menu_item_class(), alloc];
|
||||
let item = Id::from_retained_ptr(match action {
|
||||
Some(a) => msg_send![alloc, initWithTitle:&*title action:a keyEquivalent:&*key],
|
||||
|
||||
None => msg_send![alloc, initWithTitle:&*title
|
||||
action:sel!(fireBlockAction:)
|
||||
|
||||
None => msg_send![alloc, initWithTitle:&*title
|
||||
action:sel!(fireBlockAction:)
|
||||
keyEquivalent:&*key]
|
||||
});
|
||||
|
||||
|
@ -72,7 +70,7 @@ fn make_menu_item<S: AsRef<str>>(
|
|||
key_mask = key_mask | y;
|
||||
}
|
||||
|
||||
let _: () = msg_send![&*item, setKeyEquivalentModifierMask:key_mask];
|
||||
let _: () = msg_send![&*item, setKeyEquivalentModifierMask: key_mask];
|
||||
}
|
||||
|
||||
item
|
||||
|
@ -115,7 +113,7 @@ pub enum MenuItem {
|
|||
|
||||
/// A menu item for enabling copying (often text) from responders.
|
||||
Copy,
|
||||
|
||||
|
||||
/// A menu item for enabling cutting (often text) from responders.
|
||||
Cut,
|
||||
|
||||
|
@ -126,10 +124,10 @@ pub enum MenuItem {
|
|||
/// An "redo" menu item; particularly useful for supporting the cut/copy/paste/undo lifecycle
|
||||
/// of events.
|
||||
Redo,
|
||||
|
||||
|
||||
/// A menu item for selecting all (often text) from responders.
|
||||
SelectAll,
|
||||
|
||||
|
||||
/// A menu item for pasting (often text) into responders.
|
||||
Paste,
|
||||
|
||||
|
@ -158,7 +156,7 @@ impl MenuItem {
|
|||
pub(crate) unsafe fn to_objc(self) -> Id<Object> {
|
||||
match self {
|
||||
Self::Custom(objc) => objc,
|
||||
|
||||
|
||||
Self::About(app_name) => {
|
||||
let title = format!("About {}", app_name);
|
||||
make_menu_item(&title, None, Some(sel!(orderFrontStandardAboutPanel:)), None)
|
||||
|
@ -172,7 +170,7 @@ impl MenuItem {
|
|||
let item = make_menu_item("Services", None, None, None);
|
||||
let app: id = msg_send![class!(RSTApplication), sharedApplication];
|
||||
let services: id = msg_send![app, servicesMenu];
|
||||
let _: () = msg_send![&*item, setSubmenu:services];
|
||||
let _: () = msg_send![&*item, setSubmenu: services];
|
||||
item
|
||||
},
|
||||
|
||||
|
@ -192,7 +190,7 @@ impl MenuItem {
|
|||
Self::Redo => make_menu_item("Redo", Some("Z"), Some(sel!(redo:)), None),
|
||||
Self::SelectAll => make_menu_item("Select All", Some("a"), Some(sel!(selectAll:)), None),
|
||||
Self::Paste => make_menu_item("Paste", Some("v"), Some(sel!(paste:)), None),
|
||||
|
||||
|
||||
Self::EnterFullScreen => make_menu_item(
|
||||
"Enter Full Screen",
|
||||
Some("f"),
|
||||
|
@ -225,12 +223,12 @@ impl MenuItem {
|
|||
}
|
||||
|
||||
/// Configures the a custom item to have specified key equivalent. This does nothing if called
|
||||
/// on a `MenuItem` type that is not `Custom`,
|
||||
/// on a `MenuItem` type that is not `Custom`,
|
||||
pub fn key(self, key: &str) -> Self {
|
||||
if let MenuItem::Custom(objc) = self {
|
||||
unsafe {
|
||||
let key = NSString::new(key);
|
||||
let _: () = msg_send![&*objc, setKeyEquivalent:key];
|
||||
let _: () = msg_send![&*objc, setKeyEquivalent: key];
|
||||
}
|
||||
|
||||
return MenuItem::Custom(objc);
|
||||
|
@ -251,7 +249,7 @@ impl MenuItem {
|
|||
}
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*objc, setKeyEquivalentModifierMask:key_mask];
|
||||
let _: () = msg_send![&*objc, setKeyEquivalentModifierMask: key_mask];
|
||||
}
|
||||
|
||||
return MenuItem::Custom(objc);
|
||||
|
@ -271,7 +269,7 @@ impl MenuItem {
|
|||
if let MenuItem::Custom(mut objc) = self {
|
||||
let handler = Box::new(Action(Box::new(action)));
|
||||
let ptr = Box::into_raw(handler);
|
||||
|
||||
|
||||
unsafe {
|
||||
(&mut *objc).set_ivar(BLOCK_PTR, ptr as usize);
|
||||
let _: () = msg_send![&*objc, setTarget:&*objc];
|
||||
|
@ -287,11 +285,11 @@ impl MenuItem {
|
|||
/// On the Objective-C side, we need to ensure our handler is dropped when this subclass
|
||||
/// is deallocated. Note that NSMenuItem is seemingly odd outside of ARC contexts, and we
|
||||
/// need to do some extra logic to ensure release calls are properly sent.
|
||||
extern fn dealloc_cacao_menuitem(this: &Object, _: Sel) {
|
||||
extern "C" fn dealloc_cacao_menuitem(this: &Object, _: Sel) {
|
||||
unsafe {
|
||||
let ptr: usize = *this.get_ivar(BLOCK_PTR);
|
||||
let obj = ptr as *mut Action;
|
||||
|
||||
|
||||
if !obj.is_null() {
|
||||
let _handler = Box::from_raw(obj);
|
||||
}
|
||||
|
@ -305,7 +303,7 @@ extern fn dealloc_cacao_menuitem(this: &Object, _: Sel) {
|
|||
}
|
||||
|
||||
/// Called when our custom item needs to fire.
|
||||
extern fn fire_block_action(this: &Object, _: Sel, _item: id) {
|
||||
extern "C" fn fire_block_action(this: &Object, _: Sel, _item: id) {
|
||||
let action = crate::utils::load::<Action>(this, BLOCK_PTR);
|
||||
(action.0)();
|
||||
}
|
||||
|
@ -324,13 +322,11 @@ pub(crate) fn register_menu_item_class() -> *const Class {
|
|||
let mut decl = ClassDecl::new("CacaoMenuItem", superclass).unwrap();
|
||||
decl.add_ivar::<usize>(BLOCK_PTR);
|
||||
|
||||
decl.add_method(sel!(dealloc), dealloc_cacao_menuitem as extern fn(&Object, _));
|
||||
decl.add_method(sel!(fireBlockAction:), fire_block_action as extern fn(&Object, _, id));
|
||||
decl.add_method(sel!(dealloc), dealloc_cacao_menuitem as extern "C" fn(&Object, _));
|
||||
decl.add_method(sel!(fireBlockAction:), fire_block_action as extern "C" fn(&Object, _, id));
|
||||
|
||||
APP_CLASS = decl.register();
|
||||
});
|
||||
|
||||
unsafe {
|
||||
APP_CLASS
|
||||
}
|
||||
unsafe { APP_CLASS }
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use objc_id::{Id, ShareId};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::{Id, ShareId};
|
||||
|
||||
use crate::foundation::{id, NSInteger, NSString};
|
||||
use crate::appkit::menu::item::MenuItem;
|
||||
use crate::foundation::{id, NSInteger, NSString};
|
||||
|
||||
/// A struct that represents an `NSMenu`. It takes ownership of items, and handles instrumenting
|
||||
/// them throughout the application lifecycle.
|
||||
|
@ -55,8 +55,8 @@ impl Menu {
|
|||
|
||||
while count != 0 {
|
||||
count -= 1;
|
||||
let item: id = msg_send![menu, itemAtIndex:count];
|
||||
let _: () = msg_send![menu, removeItemAtIndex:count];
|
||||
let item: id = msg_send![menu, itemAtIndex: count];
|
||||
let _: () = msg_send![menu, removeItemAtIndex: count];
|
||||
let _: () = msg_send![item, release];
|
||||
}
|
||||
}
|
||||
|
@ -82,13 +82,9 @@ impl Menu {
|
|||
MenuItem::HideOthers,
|
||||
MenuItem::ShowAll,
|
||||
MenuItem::Separator,
|
||||
MenuItem::Quit
|
||||
MenuItem::Quit,
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![
|
||||
MenuItem::CloseWindow
|
||||
]),
|
||||
|
||||
Menu::new("File", vec![MenuItem::CloseWindow]),
|
||||
Menu::new("Edit", vec![
|
||||
MenuItem::Undo,
|
||||
MenuItem::Redo,
|
||||
|
@ -97,19 +93,15 @@ impl Menu {
|
|||
MenuItem::Copy,
|
||||
MenuItem::Paste,
|
||||
MenuItem::Separator,
|
||||
MenuItem::SelectAll
|
||||
MenuItem::SelectAll,
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![
|
||||
MenuItem::EnterFullScreen
|
||||
]),
|
||||
|
||||
Menu::new("View", vec![MenuItem::EnterFullScreen]),
|
||||
Menu::new("Window", vec![
|
||||
MenuItem::Minimize,
|
||||
MenuItem::Zoom,
|
||||
MenuItem::Separator,
|
||||
MenuItem::new("Bring All to Front")
|
||||
])
|
||||
MenuItem::new("Bring All to Front"),
|
||||
]),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ pub enum PrintResponse {
|
|||
Failure,
|
||||
|
||||
/// For when the result of printing cannot be returned immediately (e.g, if printing causes a sheet to appear).
|
||||
/// If your method returns PrintResponse::ReplyLater it must always invoke `App::reply_to_open_or_print()` when the
|
||||
/// If your method returns PrintResponse::ReplyLater it must always invoke `App::reply_to_open_or_print()` when the
|
||||
/// entire print operation has been completed, successfully or not.
|
||||
ReplyLater
|
||||
}
|
||||
|
|
|
@ -4,28 +4,18 @@ use std::sync::Once;
|
|||
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::{class, sel, sel_impl, msg_send};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{load_or_register_class, id, BOOL, NSArray, NSString};
|
||||
use crate::appkit::toolbar::{TOOLBAR_PTR, ToolbarDelegate};
|
||||
use crate::appkit::toolbar::{ToolbarDelegate, TOOLBAR_PTR};
|
||||
use crate::foundation::{id, load_or_register_class, NSArray, NSString, BOOL};
|
||||
use crate::utils::load;
|
||||
|
||||
/// Retrieves and passes the allowed item identifiers for this toolbar.
|
||||
extern fn allowed_item_identifiers<T: ToolbarDelegate>(this: &Object, _: Sel, _: id) -> id {
|
||||
extern "C" fn allowed_item_identifiers<T: ToolbarDelegate>(this: &Object, _: Sel, _: id) -> id {
|
||||
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
||||
|
||||
let identifiers: NSArray = toolbar.allowed_item_identifiers().iter().map(|identifier| {
|
||||
identifier.to_nsstring()
|
||||
}).collect::<Vec<id>>().into();
|
||||
|
||||
identifiers.into()
|
||||
}
|
||||
|
||||
/// Retrieves and passes the default item identifiers for this toolbar.
|
||||
extern fn default_item_identifiers<T: ToolbarDelegate>(this: &Object, _: Sel, _: id) -> id {
|
||||
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
||||
|
||||
let identifiers: NSArray = toolbar.default_item_identifiers()
|
||||
let identifiers: NSArray = toolbar
|
||||
.allowed_item_identifiers()
|
||||
.iter()
|
||||
.map(|identifier| identifier.to_nsstring())
|
||||
.collect::<Vec<id>>()
|
||||
|
@ -35,10 +25,25 @@ extern fn default_item_identifiers<T: ToolbarDelegate>(this: &Object, _: Sel, _:
|
|||
}
|
||||
|
||||
/// Retrieves and passes the default item identifiers for this toolbar.
|
||||
extern fn selectable_item_identifiers<T: ToolbarDelegate>(this: &Object, _: Sel, _: id) -> id {
|
||||
extern "C" fn default_item_identifiers<T: ToolbarDelegate>(this: &Object, _: Sel, _: id) -> id {
|
||||
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
||||
|
||||
let identifiers: NSArray = toolbar.selectable_item_identifiers()
|
||||
let identifiers: NSArray = toolbar
|
||||
.default_item_identifiers()
|
||||
.iter()
|
||||
.map(|identifier| identifier.to_nsstring())
|
||||
.collect::<Vec<id>>()
|
||||
.into();
|
||||
|
||||
identifiers.into()
|
||||
}
|
||||
|
||||
/// Retrieves and passes the default item identifiers for this toolbar.
|
||||
extern "C" fn selectable_item_identifiers<T: ToolbarDelegate>(this: &Object, _: Sel, _: id) -> id {
|
||||
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
||||
|
||||
let identifiers: NSArray = toolbar
|
||||
.selectable_item_identifiers()
|
||||
.iter()
|
||||
.map(|identifier| identifier.to_nsstring())
|
||||
.collect::<Vec<id>>()
|
||||
|
@ -49,20 +54,12 @@ extern fn selectable_item_identifiers<T: ToolbarDelegate>(this: &Object, _: Sel,
|
|||
|
||||
/// Loads the controller, grabs whatever item is for this identifier, and returns what the
|
||||
/// Objective-C runtime needs.
|
||||
extern fn item_for_identifier<T: ToolbarDelegate>(
|
||||
this: &Object,
|
||||
_: Sel,
|
||||
_: id,
|
||||
identifier: id,
|
||||
_: BOOL
|
||||
) -> id {
|
||||
extern "C" fn item_for_identifier<T: ToolbarDelegate>(this: &Object, _: Sel, _: id, identifier: id, _: BOOL) -> id {
|
||||
let toolbar = load::<T>(this, TOOLBAR_PTR);
|
||||
let identifier = NSString::from_retained(identifier);
|
||||
|
||||
|
||||
let item = toolbar.item_for(identifier.to_str());
|
||||
unsafe {
|
||||
msg_send![&*item.objc, self]
|
||||
}
|
||||
unsafe { msg_send![&*item.objc, self] }
|
||||
//&mut *item.objc
|
||||
}
|
||||
|
||||
|
@ -76,19 +73,19 @@ pub(crate) fn register_toolbar_class<T: ToolbarDelegate>(instance: &T) -> *const
|
|||
// Add callback methods
|
||||
decl.add_method(
|
||||
sel!(toolbarAllowedItemIdentifiers:),
|
||||
allowed_item_identifiers::<T> as extern fn(&Object, _, _) -> id
|
||||
allowed_item_identifiers::<T> as extern "C" fn(&Object, _, _) -> id
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(toolbarDefaultItemIdentifiers:),
|
||||
default_item_identifiers::<T> as extern fn(&Object, _, _) -> id
|
||||
default_item_identifiers::<T> as extern "C" fn(&Object, _, _) -> id
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(toolbarSelectableItemIdentifiers:),
|
||||
selectable_item_identifiers::<T> as extern fn(&Object, _, _) -> id
|
||||
selectable_item_identifiers::<T> as extern "C" fn(&Object, _, _) -> id
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(toolbar:itemForItemIdentifier:willBeInsertedIntoToolbar:),
|
||||
item_for_identifier::<T> as extern fn(&Object, _, _, _, _) -> id
|
||||
item_for_identifier::<T> as extern "C" fn(&Object, _, _, _, _) -> id
|
||||
);
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3,17 +3,17 @@
|
|||
//!
|
||||
//! UNFORTUNATELY, this is a very old and janky API. So... yeah.
|
||||
|
||||
use std::fmt;
|
||||
use core_graphics::geometry::CGSize;
|
||||
use std::fmt;
|
||||
|
||||
use objc_id::{Id, ShareId};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::{Id, ShareId};
|
||||
|
||||
use crate::foundation::{id, YES, NO, NSString};
|
||||
use crate::invoker::TargetActionHandler;
|
||||
use crate::button::{Button, BezelStyle};
|
||||
use crate::button::{BezelStyle, Button};
|
||||
use crate::foundation::{id, NSString, NO, YES};
|
||||
use crate::image::Image;
|
||||
use crate::invoker::TargetActionHandler;
|
||||
|
||||
/// Wraps `NSToolbarItem`. Enables configuring things like size, view, and so on.
|
||||
#[derive(Debug)]
|
||||
|
@ -34,7 +34,7 @@ impl ToolbarItem {
|
|||
let objc = unsafe {
|
||||
let identifr = NSString::new(&identifier);
|
||||
let alloc: id = msg_send![class!(NSToolbarItem), alloc];
|
||||
let item: id = msg_send![alloc, initWithItemIdentifier:identifr];
|
||||
let item: id = msg_send![alloc, initWithItemIdentifier: identifr];
|
||||
Id::from_ptr(item)
|
||||
};
|
||||
|
||||
|
@ -70,9 +70,9 @@ impl ToolbarItem {
|
|||
button.set_bezel_style(BezelStyle::TexturedRounded);
|
||||
|
||||
button.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![&*self.objc, setView:obj];
|
||||
let _: () = msg_send![&*self.objc, setView: obj];
|
||||
});
|
||||
|
||||
|
||||
self.button = Some(button);
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ impl ToolbarItem {
|
|||
pub fn set_min_size(&mut self, width: f64, height: f64) {
|
||||
unsafe {
|
||||
let size = CGSize::new(width.into(), height.into());
|
||||
let _: () = msg_send![&*self.objc, setMinSize:size];
|
||||
let _: () = msg_send![&*self.objc, setMinSize: size];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ impl ToolbarItem {
|
|||
pub fn set_max_size(&mut self, width: f64, height: f64) {
|
||||
unsafe {
|
||||
let size = CGSize::new(width.into(), height.into());
|
||||
let _: () = msg_send![&*self.objc, setMaxSize:size];
|
||||
let _: () = msg_send![&*self.objc, setMaxSize: size];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::foundation::{id, nil, YES, NO, NSString, NSUInteger};
|
||||
use crate::foundation::{id, nil, NSString, NSUInteger, NO, YES};
|
||||
|
||||
mod class;
|
||||
use class::register_toolbar_class;
|
||||
|
@ -21,11 +21,11 @@ mod traits;
|
|||
pub use traits::ToolbarDelegate;
|
||||
|
||||
mod enums;
|
||||
pub use enums::{ToolbarDisplayMode, ToolbarSizeMode, ItemIdentifier};
|
||||
pub use enums::{ItemIdentifier, ToolbarDisplayMode, ToolbarSizeMode};
|
||||
|
||||
pub(crate) static TOOLBAR_PTR: &str = "cacaoToolbarPtr";
|
||||
|
||||
/// A wrapper for `NSToolbar`. Holds (retains) pointers for the Objective-C runtime
|
||||
/// A wrapper for `NSToolbar`. Holds (retains) pointers for the Objective-C runtime
|
||||
/// where our `NSToolbar` and associated delegate live.
|
||||
pub struct Toolbar<T = ()> {
|
||||
/// An internal identifier used by the toolbar. We cache it here in case users want it.
|
||||
|
@ -41,23 +41,26 @@ pub struct Toolbar<T = ()> {
|
|||
pub delegate: Option<Box<T>>
|
||||
}
|
||||
|
||||
impl<T> Toolbar<T> where T: ToolbarDelegate + 'static {
|
||||
impl<T> Toolbar<T>
|
||||
where
|
||||
T: ToolbarDelegate + 'static
|
||||
{
|
||||
/// Creates a new `NSToolbar` instance, configures it appropriately, sets up the delegate
|
||||
/// chain, and retains it all.
|
||||
pub fn new<S: Into<String>>(identifier: S, delegate: T) -> Self {
|
||||
let identifier = identifier.into();
|
||||
let cls = register_toolbar_class::<T>(&delegate);
|
||||
let mut delegate = Box::new(delegate);
|
||||
|
||||
|
||||
let (objc, objc_delegate) = unsafe {
|
||||
let alloc: id = msg_send![class!(NSToolbar), alloc];
|
||||
let identifier = NSString::new(&identifier);
|
||||
let toolbar: id = msg_send![alloc, initWithIdentifier:identifier];
|
||||
let toolbar: id = msg_send![alloc, initWithIdentifier: identifier];
|
||||
let objc_delegate: id = msg_send![cls, new]; //WithIdentifier:identifier];
|
||||
|
||||
let ptr: *const T = &*delegate;
|
||||
(&mut *objc_delegate).set_ivar(TOOLBAR_PTR, ptr as usize);
|
||||
let _: () = msg_send![toolbar, setDelegate:objc_delegate];
|
||||
let _: () = msg_send![toolbar, setDelegate: objc_delegate];
|
||||
|
||||
(ShareId::from_ptr(toolbar), ShareId::from_ptr(objc_delegate))
|
||||
};
|
||||
|
@ -73,7 +76,7 @@ impl<T> Toolbar<T> where T: ToolbarDelegate + 'static {
|
|||
identifier,
|
||||
objc,
|
||||
objc_delegate,
|
||||
delegate: Some(delegate),
|
||||
delegate: Some(delegate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +98,7 @@ impl<T> Toolbar<T> {
|
|||
let mode: NSUInteger = mode.into();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setDisplayMode:mode];
|
||||
let _: () = msg_send![&*self.objc, setDisplayMode: mode];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +107,7 @@ impl<T> Toolbar<T> {
|
|||
let mode: NSUInteger = mode.into();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setSizeMode:mode];
|
||||
let _: () = msg_send![&*self.objc, setSizeMode: mode];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +153,7 @@ impl<T> Drop for Toolbar<T> {
|
|||
fn drop(&mut self) {
|
||||
if self.delegate.is_some() {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setDelegate:nil];
|
||||
let _: () = msg_send![&*self.objc, setDelegate: nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//! go. Currently a bit incomplete in that we don't support the customizing workflow, but feel free
|
||||
//! to pull request it.
|
||||
|
||||
use crate::appkit::toolbar::{Toolbar, ToolbarItem, ItemIdentifier};
|
||||
use crate::appkit::toolbar::{ItemIdentifier, Toolbar, ToolbarItem};
|
||||
|
||||
/// A trait that you can implement to have your struct/etc act as an `NSToolbarDelegate`.
|
||||
pub trait ToolbarDelegate {
|
||||
|
@ -27,10 +27,12 @@ pub trait ToolbarDelegate {
|
|||
|
||||
/// The default items in this toolbar.
|
||||
fn default_item_identifiers(&self) -> Vec<ItemIdentifier>;
|
||||
|
||||
|
||||
/// The default items in this toolbar. This defaults to a blank `Vec`, and is an optional
|
||||
/// method - mostly useful for Preferences windows.
|
||||
fn selectable_item_identifiers(&self) -> Vec<ItemIdentifier> { vec![] }
|
||||
fn selectable_item_identifiers(&self) -> Vec<ItemIdentifier> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
/// For a given `identifier`, return the `ToolbarItem` that should be displayed.
|
||||
fn item_for(&self, _identifier: &str) -> &ToolbarItem;
|
||||
|
|
|
@ -9,13 +9,13 @@ use objc::declare::ClassDecl;
|
|||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::{class, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{load_or_register_class, id, BOOL, YES, NO, NSUInteger};
|
||||
use crate::utils::{load, CGSize};
|
||||
use crate::appkit::window::{WindowDelegate, WINDOW_DELEGATE_PTR};
|
||||
use crate::foundation::{id, load_or_register_class, NSUInteger, BOOL, NO, YES};
|
||||
use crate::utils::{load, CGSize};
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowWillClose:` event.
|
||||
/// Good place to clean up memory and what not.
|
||||
extern fn should_close<T: WindowDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
extern "C" fn should_close<T: WindowDelegate>(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
|
||||
match window.should_close() {
|
||||
|
@ -26,102 +26,99 @@ extern fn should_close<T: WindowDelegate>(this: &Object, _: Sel, _: id) -> BOOL
|
|||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowWillClose:` event.
|
||||
/// Good place to clean up memory and what not.
|
||||
extern fn will_close<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_close<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.will_close();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowWillMove:` event.
|
||||
extern fn will_move<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_move<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.will_move();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidMove:` event.
|
||||
extern fn did_move<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_move<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_move();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreen:` event.
|
||||
extern fn did_change_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_change_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_change_screen();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreenProfile:` event.
|
||||
extern fn did_change_screen_profile<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_change_screen_profile<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_change_screen_profile();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreen:` event.
|
||||
extern fn will_resize<T: WindowDelegate>(this: &Object, _: Sel, _: id, size: CGSize) -> CGSize {
|
||||
extern "C" fn will_resize<T: WindowDelegate>(this: &Object, _: Sel, _: id, size: CGSize) -> CGSize {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
let s = window.will_resize(size.width as f64, size.height as f64);
|
||||
|
||||
CGSize {
|
||||
|
||||
CGSize {
|
||||
width: s.0 as CGFloat,
|
||||
height: s.1 as CGFloat
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreen:` event.
|
||||
extern fn did_resize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_resize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_resize();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreen:` event.
|
||||
extern fn will_start_live_resize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_start_live_resize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.will_start_live_resize();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreen:` event.
|
||||
extern fn did_end_live_resize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_end_live_resize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_end_live_resize();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreen:` event.
|
||||
extern fn will_miniaturize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_miniaturize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.will_miniaturize();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreen:` event.
|
||||
extern fn did_miniaturize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_miniaturize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_miniaturize();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreen:` event.
|
||||
extern fn did_deminiaturize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_deminiaturize<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_deminiaturize();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreenProfile:` event.
|
||||
extern fn will_enter_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_enter_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.will_enter_full_screen();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreenProfile:` event.
|
||||
extern fn did_enter_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_enter_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_enter_full_screen();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreenProfile:` event.
|
||||
extern fn content_size_for_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id, size: CGSize) -> CGSize {
|
||||
extern "C" fn content_size_for_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id, size: CGSize) -> CGSize {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
|
||||
let (width, height) = window.content_size_for_full_screen(
|
||||
size.width as f64,
|
||||
size.height as f64
|
||||
);
|
||||
let (width, height) = window.content_size_for_full_screen(size.width as f64, size.height as f64);
|
||||
|
||||
CGSize {
|
||||
width: width as CGFloat,
|
||||
|
@ -130,12 +127,12 @@ extern fn content_size_for_full_screen<T: WindowDelegate>(this: &Object, _: Sel,
|
|||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreenProfile:` event.
|
||||
extern fn options_for_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id, options: NSUInteger) -> NSUInteger {
|
||||
extern "C" fn options_for_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id, options: NSUInteger) -> NSUInteger {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
|
||||
let desired_opts = window.presentation_options_for_full_screen();
|
||||
|
||||
if desired_opts.is_none() {
|
||||
|
||||
if desired_opts.is_none() {
|
||||
options
|
||||
} else {
|
||||
let mut opts: NSUInteger = 0;
|
||||
|
@ -148,82 +145,82 @@ extern fn options_for_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: i
|
|||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreenProfile:` event.
|
||||
extern fn will_exit_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn will_exit_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.will_exit_full_screen();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreenProfile:` event.
|
||||
extern fn did_exit_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_exit_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_exit_full_screen();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreenProfile:` event.
|
||||
extern fn did_fail_to_enter_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_fail_to_enter_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_fail_to_enter_full_screen();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeScreenProfile:` event.
|
||||
extern fn did_fail_to_exit_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_fail_to_exit_full_screen<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_fail_to_exit_full_screen();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeBackingProperties:` event.
|
||||
extern fn did_change_backing_properties<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_change_backing_properties<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_change_backing_properties();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidChangeBackingProperties:` event.
|
||||
extern fn did_change_occlusion_state<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_change_occlusion_state<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_change_occlusion_state();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidUpdate:` event.
|
||||
extern fn did_update<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_update<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_update();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidExpose:` event.
|
||||
extern fn did_become_main<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_become_main<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_become_main();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidExpose:` event.
|
||||
extern fn did_resign_main<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_resign_main<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_resign_main();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidExpose:` event.
|
||||
extern fn did_become_key<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_become_key<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_become_key();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidExpose:` event.
|
||||
extern fn did_resign_key<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_resign_key<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_resign_key();
|
||||
}
|
||||
|
||||
/// Called when an `NSWindowDelegate` receives a `windowDidExpose:` event.
|
||||
extern fn did_expose<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn did_expose<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.did_expose();
|
||||
}
|
||||
|
||||
/// Called as part of the responder chain, when, say, the ESC key is hit. If your
|
||||
/// Called as part of the responder chain, when, say, the ESC key is hit. If your
|
||||
/// delegate returns `true` in `should_cancel_on_esc`, then this will allow your
|
||||
/// window to close when the Esc key is hit. This is mostly useful for Sheet-presented
|
||||
/// windows, and so the default response from delegates is `false` and must be opted in to.
|
||||
extern fn cancel<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
extern "C" fn cancel<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||
window.cancel();
|
||||
}
|
||||
|
@ -235,49 +232,112 @@ pub(crate) fn register_window_class_with_delegate<T: WindowDelegate>(instance: &
|
|||
decl.add_ivar::<usize>(WINDOW_DELEGATE_PTR);
|
||||
|
||||
// NSWindowDelegate methods
|
||||
decl.add_method(sel!(windowShouldClose:), should_close::<T> as extern fn(&Object, _, _) -> BOOL);
|
||||
decl.add_method(sel!(windowWillClose:), will_close::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(windowShouldClose:),
|
||||
should_close::<T> as extern "C" fn(&Object, _, _) -> BOOL
|
||||
);
|
||||
decl.add_method(sel!(windowWillClose:), will_close::<T> as extern "C" fn(&Object, _, _));
|
||||
|
||||
// Sizing
|
||||
decl.add_method(sel!(windowWillResize:toSize:), will_resize::<T> as extern fn(&Object, _, _, CGSize) -> CGSize);
|
||||
decl.add_method(sel!(windowDidResize:), did_resize::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowWillStartLiveResize:), will_start_live_resize::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidEndLiveResize:), did_end_live_resize::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(windowWillResize:toSize:),
|
||||
will_resize::<T> as extern "C" fn(&Object, _, _, CGSize) -> CGSize
|
||||
);
|
||||
decl.add_method(sel!(windowDidResize:), did_resize::<T> as extern "C" fn(&Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(windowWillStartLiveResize:),
|
||||
will_start_live_resize::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidEndLiveResize:),
|
||||
did_end_live_resize::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
|
||||
// Minimizing
|
||||
decl.add_method(sel!(windowWillMiniaturize:), will_miniaturize::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidMiniaturize:), did_miniaturize::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidDeminiaturize:), did_deminiaturize::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(windowWillMiniaturize:),
|
||||
will_miniaturize::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidMiniaturize:),
|
||||
did_miniaturize::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidDeminiaturize:),
|
||||
did_deminiaturize::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
|
||||
// Full Screen
|
||||
decl.add_method(sel!(window:willUseFullScreenContentSize:), content_size_for_full_screen::<T> as extern fn(&Object, _, _, CGSize) -> CGSize);
|
||||
decl.add_method(sel!(window:willUseFullScreenPresentationOptions:), options_for_full_screen::<T> as extern fn(&Object, _, _, NSUInteger) -> NSUInteger);
|
||||
decl.add_method(sel!(windowWillEnterFullScreen:), will_enter_full_screen::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidEnterFullScreen:), did_enter_full_screen::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowWillExitFullScreen:), will_exit_full_screen::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidExitFullScreen:), did_exit_full_screen::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidFailToEnterFullScreen:), did_fail_to_enter_full_screen::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidFailToExitFullScreen:), did_fail_to_exit_full_screen::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(window:willUseFullScreenContentSize:),
|
||||
content_size_for_full_screen::<T> as extern "C" fn(&Object, _, _, CGSize) -> CGSize
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(window:willUseFullScreenPresentationOptions:),
|
||||
options_for_full_screen::<T> as extern "C" fn(&Object, _, _, NSUInteger) -> NSUInteger
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowWillEnterFullScreen:),
|
||||
will_enter_full_screen::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidEnterFullScreen:),
|
||||
did_enter_full_screen::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowWillExitFullScreen:),
|
||||
will_exit_full_screen::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidExitFullScreen:),
|
||||
did_exit_full_screen::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidFailToEnterFullScreen:),
|
||||
did_fail_to_enter_full_screen::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidFailToExitFullScreen:),
|
||||
did_fail_to_exit_full_screen::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
|
||||
// Key status
|
||||
decl.add_method(sel!(windowDidBecomeKey:), did_become_key::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidResignKey:), did_resign_key::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidBecomeKey:), did_become_key::<T> as extern "C" fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidResignKey:), did_resign_key::<T> as extern "C" fn(&Object, _, _));
|
||||
|
||||
// Main status
|
||||
decl.add_method(sel!(windowDidBecomeMain:), did_become_main::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidResignMain:), did_resign_main::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(windowDidBecomeMain:),
|
||||
did_become_main::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidResignMain:),
|
||||
did_resign_main::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
|
||||
// Moving Windows
|
||||
decl.add_method(sel!(windowWillMove:), will_move::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidMove:), did_move::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidChangeScreen:), did_change_screen::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidChangeScreenProfile:), did_change_screen_profile::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidChangeBackingProperties:), did_change_backing_properties::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowWillMove:), will_move::<T> as extern "C" fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidMove:), did_move::<T> as extern "C" fn(&Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(windowDidChangeScreen:),
|
||||
did_change_screen::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidChangeScreenProfile:),
|
||||
did_change_screen_profile::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidChangeBackingProperties:),
|
||||
did_change_backing_properties::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
|
||||
// Random
|
||||
decl.add_method(sel!(windowDidChangeOcclusionState:), did_change_occlusion_state::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidExpose:), did_expose::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidUpdate:), did_update::<T> as extern fn(&Object, _, _));
|
||||
decl.add_method(sel!(cancelOperation:), cancel::<T> as extern fn (&Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(windowDidChangeOcclusionState:),
|
||||
did_change_occlusion_state::<T> as extern "C" fn(&Object, _, _)
|
||||
);
|
||||
decl.add_method(sel!(windowDidExpose:), did_expose::<T> as extern "C" fn(&Object, _, _));
|
||||
decl.add_method(sel!(windowDidUpdate:), did_update::<T> as extern "C" fn(&Object, _, _));
|
||||
decl.add_method(sel!(cancelOperation:), cancel::<T> as extern "C" fn(&Object, _, _));
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
//! mask). This configuration object acts as a way to orchestrate enabling customization before the
|
||||
//! window object is created - it's returned in your `WindowDelegate` object.
|
||||
|
||||
use crate::appkit::window::enums::{WindowStyle, WindowToolbarStyle};
|
||||
use crate::foundation::NSUInteger;
|
||||
use crate::geometry::Rect;
|
||||
use crate::appkit::window::enums::{WindowStyle, WindowToolbarStyle};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WindowConfig {
|
||||
|
@ -14,11 +14,11 @@ pub struct WindowConfig {
|
|||
/// The initial dimensions for the window.
|
||||
pub initial_dimensions: Rect,
|
||||
|
||||
/// From the Apple docs:
|
||||
/// From the Apple docs:
|
||||
///
|
||||
/// _"When true, the window server defers creating the window device
|
||||
/// until the window is moved onscreen. All display messages sent to
|
||||
/// the window or its views are postponed until the window is created,
|
||||
/// _"When true, the window server defers creating the window device
|
||||
/// until the window is moved onscreen. All display messages sent to
|
||||
/// the window or its views are postponed until the window is created,
|
||||
/// just before it’s moved onscreen."_
|
||||
///
|
||||
/// You generally just want this to be true, and it's the default for this struct.
|
||||
|
@ -45,8 +45,12 @@ impl Default for WindowConfig {
|
|||
};
|
||||
|
||||
config.set_styles(&[
|
||||
WindowStyle::Resizable, WindowStyle::Miniaturizable, WindowStyle::UnifiedTitleAndToolbar,
|
||||
WindowStyle::Closable, WindowStyle::Titled, WindowStyle::FullSizeContentView
|
||||
WindowStyle::Resizable,
|
||||
WindowStyle::Miniaturizable,
|
||||
WindowStyle::UnifiedTitleAndToolbar,
|
||||
WindowStyle::Closable,
|
||||
WindowStyle::Titled,
|
||||
WindowStyle::FullSizeContentView
|
||||
]);
|
||||
|
||||
config
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
|
||||
use std::sync::Once;
|
||||
|
||||
use objc::class;
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::Class;
|
||||
use objc::class;
|
||||
|
||||
use crate::appkit::window::{WindowDelegate, WINDOW_DELEGATE_PTR};
|
||||
|
||||
|
@ -22,7 +22,5 @@ pub(crate) fn register_window_controller_class<T: WindowDelegate>() -> *const Cl
|
|||
DELEGATE_CLASS = decl.register();
|
||||
});
|
||||
|
||||
unsafe {
|
||||
DELEGATE_CLASS
|
||||
}
|
||||
unsafe { DELEGATE_CLASS }
|
||||
}
|
||||
|
|
|
@ -33,14 +33,14 @@ use objc::runtime::Object;
|
|||
use objc::{msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::appkit::window::{Window, WindowConfig, WindowDelegate, WINDOW_DELEGATE_PTR};
|
||||
use crate::foundation::{id, nil};
|
||||
use crate::utils::Controller;
|
||||
use crate::appkit::window::{Window, WindowConfig, WindowDelegate, WINDOW_DELEGATE_PTR};
|
||||
|
||||
mod class;
|
||||
use class::register_window_controller_class;
|
||||
|
||||
/// A `WindowController` wraps your `WindowDelegate` into an underlying `Window`, and
|
||||
/// A `WindowController` wraps your `WindowDelegate` into an underlying `Window`, and
|
||||
/// provides some extra lifecycle methods.
|
||||
pub struct WindowController<T> {
|
||||
/// A handler to the underlying `NSWindowController`.
|
||||
|
@ -50,7 +50,10 @@ pub struct WindowController<T> {
|
|||
pub window: Window<T>
|
||||
}
|
||||
|
||||
impl<T> WindowController<T> where T: WindowDelegate + 'static {
|
||||
impl<T> WindowController<T>
|
||||
where
|
||||
T: WindowDelegate + 'static
|
||||
{
|
||||
/// Allocates and configures an `NSWindowController` in the Objective-C/Cocoa runtime that maps over
|
||||
/// to your supplied delegate.
|
||||
pub fn with(config: WindowConfig, delegate: T) -> Self {
|
||||
|
@ -84,7 +87,7 @@ impl<T> WindowController<T> where T: WindowDelegate + 'static {
|
|||
/// Shows the window, running a configuration pass if necessary.
|
||||
pub fn show(&self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, showWindow:nil];
|
||||
let _: () = msg_send![&*self.objc, showWindow: nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,8 +101,6 @@ impl<T> WindowController<T> where T: WindowDelegate + 'static {
|
|||
|
||||
impl<T> fmt::Debug for WindowController<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("WindowController")
|
||||
.field("objc", &self.objc)
|
||||
.finish()
|
||||
f.debug_struct("WindowController").field("objc", &self.objc).finish()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
//! resizing, closing, and so on). Note that interaction patterns are different between macOS and
|
||||
//! iOS windows, so your codebase may need to differ quite a bit here.
|
||||
//!
|
||||
//! Of note: on macOS, in places where things are outright deprecated, this framework will opt to
|
||||
//! not bother providing access to them. If you require functionality like that, you're free to use
|
||||
//! Of note: on macOS, in places where things are outright deprecated, this framework will opt to
|
||||
//! not bother providing access to them. If you require functionality like that, you're free to use
|
||||
//! the `objc` field on a `Window` to instrument it with the Objective-C runtime on your own.
|
||||
|
||||
use block::ConcreteBlock;
|
||||
|
@ -13,13 +13,13 @@ use block::ConcreteBlock;
|
|||
use core_graphics::base::CGFloat;
|
||||
use core_graphics::geometry::{CGRect, CGSize};
|
||||
|
||||
use objc::{msg_send, sel, sel_impl, class};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::appkit::toolbar::{Toolbar, ToolbarDelegate};
|
||||
use crate::color::Color;
|
||||
use crate::foundation::{id, nil, to_bool, YES, NO, NSString, NSInteger, NSUInteger};
|
||||
use crate::foundation::{id, nil, to_bool, NSInteger, NSString, NSUInteger, NO, YES};
|
||||
use crate::layout::Layout;
|
||||
use crate::objc_access::ObjcAccess;
|
||||
use crate::utils::{os, Controller};
|
||||
|
@ -69,16 +69,16 @@ impl Window {
|
|||
let objc = unsafe {
|
||||
// This behavior might make sense to keep as default (YES), but I think the majority of
|
||||
// apps that would use this toolkit wouldn't be tab-oriented...
|
||||
let _: () = msg_send![class!(NSWindow), setAllowsAutomaticWindowTabbing:NO];
|
||||
let _: () = msg_send![class!(NSWindow), setAllowsAutomaticWindowTabbing: NO];
|
||||
|
||||
let alloc: id = msg_send![class!(NSWindow), alloc];
|
||||
|
||||
|
||||
// Other types of backing (Retained/NonRetained) are archaic, dating back to the
|
||||
// NeXTSTEP era, and are outright deprecated... so we don't allow setting them.
|
||||
let buffered: NSUInteger = 2;
|
||||
let dimensions: CGRect = config.initial_dimensions.into();
|
||||
let window: id = msg_send![alloc, initWithContentRect:dimensions
|
||||
styleMask:config.style
|
||||
let window: id = msg_send![alloc, initWithContentRect:dimensions
|
||||
styleMask:config.style
|
||||
backing:buffered
|
||||
defer:match config.defer {
|
||||
true => YES,
|
||||
|
@ -92,16 +92,16 @@ impl Window {
|
|||
// to disable, like... this. If we don't set this, we'll segfault entirely because the
|
||||
// Objective-C runtime gets out of sync by releasing the window out from underneath of
|
||||
// us.
|
||||
let _: () = msg_send![window, setReleasedWhenClosed:NO];
|
||||
|
||||
let _: () = msg_send![window, setRestorable:NO];
|
||||
let _: () = msg_send![window, setReleasedWhenClosed: NO];
|
||||
|
||||
let _: () = msg_send![window, setRestorable: NO];
|
||||
|
||||
// This doesn't exist prior to Big Sur, but is important to support for Big Sur.
|
||||
//
|
||||
// Why this isn't a setting on the Toolbar itself I'll never know.
|
||||
if os::is_minimum_version(11) {
|
||||
let toolbar_style: NSUInteger = config.toolbar_style.into();
|
||||
let _: () = msg_send![window, setToolbarStyle:toolbar_style];
|
||||
let _: () = msg_send![window, setToolbarStyle: toolbar_style];
|
||||
}
|
||||
|
||||
ShareId::from_ptr(window)
|
||||
|
@ -114,7 +114,10 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Window<T> where T: WindowDelegate + 'static {
|
||||
impl<T> Window<T>
|
||||
where
|
||||
T: WindowDelegate + 'static
|
||||
{
|
||||
/// Constructs a new Window with a `config` and `delegate`. Using a `WindowDelegate` enables
|
||||
/// you to respond to window lifecycle events - visibility, movement, and so on. It also
|
||||
/// enables easier structure of your codebase, and in a way simulates traditional class based
|
||||
|
@ -122,20 +125,20 @@ impl<T> Window<T> where T: WindowDelegate + 'static {
|
|||
pub fn with(config: WindowConfig, delegate: T) -> Self {
|
||||
let class = register_window_class_with_delegate::<T>(&delegate);
|
||||
let mut delegate = Box::new(delegate);
|
||||
|
||||
|
||||
let objc = unsafe {
|
||||
// This behavior might make sense to keep as default (YES), but I think the majority of
|
||||
// apps that would use this toolkit wouldn't be tab-oriented...
|
||||
let _: () = msg_send![class!(NSWindow), setAllowsAutomaticWindowTabbing:NO];
|
||||
let _: () = msg_send![class!(NSWindow), setAllowsAutomaticWindowTabbing: NO];
|
||||
|
||||
let alloc: id = msg_send![class, alloc];
|
||||
|
||||
|
||||
// Other types of backing (Retained/NonRetained) are archaic, dating back to the
|
||||
// NeXTSTEP era, and are outright deprecated... so we don't allow setting them.
|
||||
let buffered: NSUInteger = 2;
|
||||
let dimensions: CGRect = config.initial_dimensions.into();
|
||||
let window: id = msg_send![alloc, initWithContentRect:dimensions
|
||||
styleMask:config.style
|
||||
let window: id = msg_send![alloc, initWithContentRect:dimensions
|
||||
styleMask:config.style
|
||||
backing:buffered
|
||||
defer:match config.defer {
|
||||
true => YES,
|
||||
|
@ -152,19 +155,19 @@ impl<T> Window<T> where T: WindowDelegate + 'static {
|
|||
// to disable, like... this. If we don't set this, we'll segfault entirely because the
|
||||
// Objective-C runtime gets out of sync by releasing the window out from underneath of
|
||||
// us.
|
||||
let _: () = msg_send![window, setReleasedWhenClosed:NO];
|
||||
let _: () = msg_send![window, setReleasedWhenClosed: NO];
|
||||
|
||||
// We set the window to be its own delegate - this is cleaned up inside `Drop`.
|
||||
let _: () = msg_send![window, setDelegate:window];
|
||||
let _: () = msg_send![window, setDelegate: window];
|
||||
|
||||
let _: () = msg_send![window, setRestorable:NO];
|
||||
let _: () = msg_send![window, setRestorable: NO];
|
||||
|
||||
// This doesn't exist prior to Big Sur, but is important to support for Big Sur.
|
||||
//
|
||||
// Why this isn't a setting on the Toolbar itself I'll never know.
|
||||
if os::is_minimum_version(11) {
|
||||
let toolbar_style: NSUInteger = config.toolbar_style.into();
|
||||
let _: () = msg_send![window, setToolbarStyle:toolbar_style];
|
||||
let _: () = msg_send![window, setToolbarStyle: toolbar_style];
|
||||
}
|
||||
|
||||
ShareId::from_ptr(window)
|
||||
|
@ -190,7 +193,7 @@ impl<T> Window<T> {
|
|||
pub fn set_title(&self, title: &str) {
|
||||
unsafe {
|
||||
let title = NSString::new(title);
|
||||
let _: () = msg_send![&*self.objc, setTitle:title];
|
||||
let _: () = msg_send![&*self.objc, setTitle: title];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,7 +201,7 @@ impl<T> Window<T> {
|
|||
pub fn set_title_visibility(&self, visibility: TitleVisibility) {
|
||||
unsafe {
|
||||
let v = NSInteger::from(visibility);
|
||||
let _: () = msg_send![&*self.objc, setTitleVisibility:v];
|
||||
let _: () = msg_send![&*self.objc, setTitleVisibility: v];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +229,7 @@ impl<T> Window<T> {
|
|||
pub fn set_autosave_name(&self, name: &str) {
|
||||
unsafe {
|
||||
let autosave = NSString::new(name);
|
||||
let _: () = msg_send![&*self.objc, setFrameAutosaveName:autosave];
|
||||
let _: () = msg_send![&*self.objc, setFrameAutosaveName: autosave];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,7 +237,7 @@ impl<T> Window<T> {
|
|||
pub fn set_content_size<F: Into<f64>>(&self, width: F, height: F) {
|
||||
unsafe {
|
||||
let size = CGSize::new(width.into(), height.into());
|
||||
let _: () = msg_send![&*self.objc, setContentSize:size];
|
||||
let _: () = msg_send![&*self.objc, setContentSize: size];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,7 +245,7 @@ impl<T> Window<T> {
|
|||
pub fn set_minimum_content_size<F: Into<f64>>(&self, width: F, height: F) {
|
||||
unsafe {
|
||||
let size = CGSize::new(width.into(), height.into());
|
||||
let _: () = msg_send![&*self.objc, setContentMinSize:size];
|
||||
let _: () = msg_send![&*self.objc, setContentMinSize: size];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,30 +253,30 @@ impl<T> Window<T> {
|
|||
pub fn set_maximum_content_size<F: Into<f64>>(&self, width: F, height: F) {
|
||||
unsafe {
|
||||
let size = CGSize::new(width.into(), height.into());
|
||||
let _: () = msg_send![&*self.objc, setContentMaxSize:size];
|
||||
let _: () = msg_send![&*self.objc, setContentMaxSize: size];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Sets the minimum size this window can shrink to.
|
||||
pub fn set_minimum_size<F: Into<f64>>(&self, width: F, height: F) {
|
||||
unsafe {
|
||||
let size = CGSize::new(width.into(), height.into());
|
||||
let _: () = msg_send![&*self.objc, setMinSize:size];
|
||||
let _: () = msg_send![&*self.objc, setMinSize: size];
|
||||
}
|
||||
}
|
||||
|
||||
/// Used for setting a toolbar on this window.
|
||||
/// Used for setting a toolbar on this window.
|
||||
pub fn set_toolbar<TC: ToolbarDelegate>(&self, toolbar: &Toolbar<TC>) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setToolbar:&*toolbar.objc];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Toggles whether the toolbar is shown for this window. Has no effect if no toolbar exists on
|
||||
/// this window.
|
||||
pub fn toggle_toolbar_shown(&self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, toggleToolbarShown:nil];
|
||||
let _: () = msg_send![&*self.objc, toggleToolbarShown: nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -307,7 +310,7 @@ impl<T> Window<T> {
|
|||
/// Shows the window.
|
||||
pub fn show(&self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, makeKeyAndOrderFront:nil];
|
||||
let _: () = msg_send![&*self.objc, makeKeyAndOrderFront: nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,7 +327,7 @@ impl<T> Window<T> {
|
|||
/// Toggles a Window being full screen or not.
|
||||
pub fn toggle_full_screen(&self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, toggleFullScreen:nil];
|
||||
let _: () = msg_send![&*self.objc, toggleFullScreen: nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,22 +336,18 @@ impl<T> Window<T> {
|
|||
let color: id = color.as_ref().into();
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setBackgroundColor:color];
|
||||
let _: () = msg_send![&*self.objc, setBackgroundColor: color];
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether this window is opaque or not.
|
||||
pub fn is_opaque(&self) -> bool {
|
||||
to_bool(unsafe {
|
||||
msg_send![&*self.objc, isOpaque]
|
||||
})
|
||||
to_bool(unsafe { msg_send![&*self.objc, isOpaque] })
|
||||
}
|
||||
|
||||
/// Returns whether this window is miniaturized or not.
|
||||
pub fn is_miniaturized(&self) -> bool {
|
||||
to_bool(unsafe {
|
||||
msg_send![&*self.objc, isMiniaturized]
|
||||
})
|
||||
to_bool(unsafe { msg_send![&*self.objc, isMiniaturized] })
|
||||
}
|
||||
|
||||
/// Miniaturize this window.
|
||||
|
@ -377,35 +376,27 @@ impl<T> Window<T> {
|
|||
///
|
||||
/// From Apple's documentation:
|
||||
///
|
||||
/// _The value of this property is YES if the window is on the currently active space; otherwise, NO.
|
||||
/// For visible windows, this property indicates whether the window is currently visible on the active
|
||||
/// space. For nonvisible windows, it indicates whether ordering the window onscreen would cause it to
|
||||
/// _The value of this property is YES if the window is on the currently active space; otherwise, NO.
|
||||
/// For visible windows, this property indicates whether the window is currently visible on the active
|
||||
/// space. For nonvisible windows, it indicates whether ordering the window onscreen would cause it to
|
||||
/// be on the active space._
|
||||
pub fn is_on_active_space(&self) -> bool {
|
||||
to_bool(unsafe {
|
||||
msg_send![&*self.objc, isOnActiveSpace]
|
||||
})
|
||||
to_bool(unsafe { msg_send![&*self.objc, isOnActiveSpace] })
|
||||
}
|
||||
|
||||
/// Returns whether this window is visible or not.
|
||||
pub fn is_visible(&self) -> bool {
|
||||
to_bool(unsafe {
|
||||
msg_send![&*self.objc, isVisible]
|
||||
})
|
||||
to_bool(unsafe { msg_send![&*self.objc, isVisible] })
|
||||
}
|
||||
|
||||
/// Returns whether this window is the key or not.
|
||||
pub fn is_key(&self) -> bool {
|
||||
to_bool(unsafe {
|
||||
msg_send![&*self.objc, isKeyWindow]
|
||||
})
|
||||
to_bool(unsafe { msg_send![&*self.objc, isKeyWindow] })
|
||||
}
|
||||
|
||||
/// Returns whether this window can become the key window.
|
||||
pub fn can_become_key(&self) -> bool {
|
||||
to_bool(unsafe {
|
||||
msg_send![&*self.objc, canBecomeKeyWindow]
|
||||
})
|
||||
to_bool(unsafe { msg_send![&*self.objc, canBecomeKeyWindow] })
|
||||
}
|
||||
|
||||
/// Make this window the key window.
|
||||
|
@ -419,22 +410,18 @@ impl<T> Window<T> {
|
|||
/// you.
|
||||
pub fn make_key_and_order_front(&self) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, makeKeyAndOrderFront:nil];
|
||||
let _: () = msg_send![&*self.objc, makeKeyAndOrderFront: nil];
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns if this is the main window or not.
|
||||
pub fn is_main_window(&self) -> bool {
|
||||
to_bool(unsafe {
|
||||
msg_send![&*self.objc, isMainWindow]
|
||||
})
|
||||
to_bool(unsafe { msg_send![&*self.objc, isMainWindow] })
|
||||
}
|
||||
|
||||
/// Returns if this can become the main window.
|
||||
pub fn can_become_main_window(&self) -> bool {
|
||||
to_bool(unsafe {
|
||||
msg_send![&*self.objc, canBecomeMainWindow]
|
||||
})
|
||||
to_bool(unsafe { msg_send![&*self.objc, canBecomeMainWindow] })
|
||||
}
|
||||
|
||||
/// Set whether this window should be excluded from the top-level "Windows" menu.
|
||||
|
@ -450,10 +437,10 @@ impl<T> Window<T> {
|
|||
/// Sets the separator style for this window.
|
||||
pub fn set_titlebar_separator_style(&self, style: crate::foundation::NSInteger) {
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.objc, setTitlebarSeparatorStyle:style];
|
||||
let _: () = msg_send![&*self.objc, setTitlebarSeparatorStyle: style];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns the backing scale (e.g, `1.0` for non retina, `2.0` for retina) used on this
|
||||
/// window.
|
||||
///
|
||||
|
@ -513,7 +500,7 @@ impl<T> Drop for Window<T> {
|
|||
unsafe {
|
||||
// Break the delegate - this shouldn't be an issue, but we should strive to be safe
|
||||
// here anyway.
|
||||
let _: () = msg_send![&*self.objc, setDelegate:nil];
|
||||
let _: () = msg_send![&*self.objc, setDelegate: nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::appkit::window::Window;
|
|||
/// Lifecycle events for anything that `impl Window`'s. These map to the standard Cocoa
|
||||
/// lifecycle methods, but mix in a few extra things to handle offering configuration tools
|
||||
/// in lieu of subclasses.
|
||||
pub trait WindowDelegate {
|
||||
pub trait WindowDelegate {
|
||||
/// Used to cache subclass creations on the Objective-C side.
|
||||
/// You can just set this to be the name of your view type. This
|
||||
/// value *must* be unique per-type.
|
||||
|
@ -28,13 +28,15 @@ pub trait WindowDelegate {
|
|||
|
||||
/// Called when the user has attempted to close the window. NOT called when a user quits the
|
||||
/// application. Return false here if you need to handle the edge case.
|
||||
fn should_close(&self) -> bool { true }
|
||||
fn should_close(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Fires when a window is going to close. You might opt to, say, clean up things here -
|
||||
/// perhaps you have a long running task, or something that should be removed.
|
||||
fn will_close(&self) {}
|
||||
|
||||
/// Fired when the window is about to move.
|
||||
/// Fired when the window is about to move.
|
||||
fn will_move(&self) {}
|
||||
|
||||
/// Fired after the window has moved.
|
||||
|
@ -46,8 +48,10 @@ pub trait WindowDelegate {
|
|||
///
|
||||
/// The default implementation of this method returns `None`, indicating the system should just
|
||||
/// do its thing. If you implement it, you probably want that.
|
||||
fn will_resize(&self, width: f64, height: f64) -> (f64, f64) { (width, height) }
|
||||
|
||||
fn will_resize(&self, width: f64, height: f64) -> (f64, f64) {
|
||||
(width, height)
|
||||
}
|
||||
|
||||
/// Fired after the window has resized.
|
||||
fn did_resize(&self) {}
|
||||
|
||||
|
@ -93,14 +97,16 @@ pub trait WindowDelegate {
|
|||
/// Fires when the system is moving a window to full screen and wants to know what content size
|
||||
/// to use. By default, this just returns the system-provided content size, but you can
|
||||
/// override it if need be.
|
||||
fn content_size_for_full_screen(&self, proposed_width: f64, proposed_height: f64) -> (f64, f64) {
|
||||
fn content_size_for_full_screen(&self, proposed_width: f64, proposed_height: f64) -> (f64, f64) {
|
||||
(proposed_width, proposed_height)
|
||||
}
|
||||
|
||||
/// Specify options for when this window goes full screen.
|
||||
/// By default, this returns `None`, which tells the system to proceed as it normally would
|
||||
/// without customization.
|
||||
fn presentation_options_for_full_screen(&self) -> Option<&[PresentationOption]> { None }
|
||||
fn presentation_options_for_full_screen(&self) -> Option<&[PresentationOption]> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Fires when this window is about to go full screen.
|
||||
fn will_enter_full_screen(&self) {}
|
||||
|
@ -119,7 +125,7 @@ pub trait WindowDelegate {
|
|||
|
||||
/// Fires when this window failed to exit full screen.
|
||||
fn did_fail_to_exit_full_screen(&self) {}
|
||||
|
||||
|
||||
/// Fired when the occlusion state for this window has changed. Similar in nature to the
|
||||
/// app-level event, just for a Window.
|
||||
fn did_change_occlusion_state(&self) {}
|
||||
|
|
|
@ -49,15 +49,15 @@ macro_rules! method_decl_impl {
|
|||
}
|
||||
);
|
||||
($($t:ident),*) => (
|
||||
method_decl_impl!(-T, R, extern fn(&T, Sel $(, $t)*) -> R, $($t),*);
|
||||
method_decl_impl!(-T, R, extern fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
|
||||
method_decl_impl!(-T, R, extern "C" fn(&T, Sel $(, $t)*) -> R, $($t),*);
|
||||
method_decl_impl!(-T, R, extern "C" fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
|
||||
);
|
||||
}
|
||||
|
||||
method_decl_impl!();
|
||||
method_decl_impl!(A);
|
||||
|
||||
extern fn get_bundle_id(this: &Object, s: Sel, v: id) -> id {
|
||||
extern "C" fn get_bundle_id(this: &Object, s: Sel, v: id) -> id {
|
||||
unsafe {
|
||||
let bundle = class!(NSBundle);
|
||||
let main_bundle: id = msg_send![bundle, mainBundle];
|
||||
|
@ -80,7 +80,7 @@ unsafe fn swizzle_bundle_id<F>(bundle_id: &str, func: F) where F: MethodImplemen
|
|||
// let mut cls = class!(NSBundle) as *mut Class;
|
||||
// Class::get("NSBundle").unwrap();
|
||||
// let types = format!("{}{}{}", Encoding::String, <*mut Object>::ENCODING, Sel::ENCODING);
|
||||
|
||||
|
||||
let added = class_addMethod(
|
||||
cls as *mut Class,
|
||||
sel!(__bundleIdentifier),
|
||||
|
@ -95,6 +95,6 @@ unsafe fn swizzle_bundle_id<F>(bundle_id: &str, func: F) where F: MethodImplemen
|
|||
|
||||
pub fn set_bundle_id(bundle_id: &str) {
|
||||
unsafe {
|
||||
swizzle_bundle_id(bundle_id, get_bundle_id as extern fn(&Object, _, _) -> id);
|
||||
swizzle_bundle_id(bundle_id, get_bundle_id as extern "C" fn(&Object, _, _) -> id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ pub enum BezelStyle {
|
|||
/// A textured square style.
|
||||
TexturedSquare,
|
||||
|
||||
/// Any style that's not known by this framework (e.g, if Apple
|
||||
/// Any style that's not known by this framework (e.g, if Apple
|
||||
/// introduces something new).
|
||||
Unknown(NSUInteger)
|
||||
}
|
||||
|
|
|
@ -23,15 +23,15 @@ use std::sync::Once;
|
|||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use objc_id::ShareId;
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Sel};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::color::Color;
|
||||
use crate::control::Control;
|
||||
use crate::foundation::{id, nil, NSString, NSUInteger, BOOL, NO, YES};
|
||||
use crate::image::Image;
|
||||
use crate::foundation::{id, nil, BOOL, YES, NO, NSString, NSUInteger};
|
||||
use crate::invoker::TargetActionHandler;
|
||||
use crate::keys::Key;
|
||||
use crate::layout::Layout;
|
||||
|
@ -40,7 +40,7 @@ use crate::text::{AttributedString, Font};
|
|||
use crate::utils::{load, properties::ObjcProperty};
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
use crate::layout::{LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY};
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
use crate::appkit::FocusRingType;
|
||||
|
@ -52,7 +52,7 @@ pub use enums::*;
|
|||
///
|
||||
/// You'd use this type to create a button that a user can interact with. Buttons can be configured
|
||||
/// a number of ways, and support setting a callback to fire when they're clicked or tapped.
|
||||
///
|
||||
///
|
||||
/// Some properties are platform-specific; see the documentation for further information.
|
||||
///
|
||||
/// ```rust,no_run
|
||||
|
@ -75,7 +75,7 @@ pub struct Button {
|
|||
pub image: Option<Image>,
|
||||
|
||||
handler: Option<TargetActionHandler>,
|
||||
|
||||
|
||||
/// A pointer to the Objective-C runtime top layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub top: LayoutAnchorY,
|
||||
|
@ -129,49 +129,49 @@ impl Button {
|
|||
action:nil
|
||||
];
|
||||
|
||||
let _: () = msg_send![button, setWantsLayer:YES];
|
||||
|
||||
let _: () = msg_send![button, setWantsLayer: YES];
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
let _: () = msg_send![button, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
let _: () = msg_send![button, setTranslatesAutoresizingMaskIntoConstraints: NO];
|
||||
|
||||
button
|
||||
};
|
||||
|
||||
|
||||
Button {
|
||||
handler: None,
|
||||
image: None,
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
top: LayoutAnchorY::top(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
left: LayoutAnchorX::left(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
leading: LayoutAnchorX::leading(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
right: LayoutAnchorX::right(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
trailing: LayoutAnchorX::trailing(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
bottom: LayoutAnchorY::bottom(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
width: LayoutAnchorDimension::width(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
height: LayoutAnchorDimension::height(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
|
||||
objc: ObjcProperty::retain(view),
|
||||
|
||||
objc: ObjcProperty::retain(view)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,9 +188,9 @@ impl Button {
|
|||
#[cfg(feature = "appkit")]
|
||||
pub fn set_bezel_style(&self, bezel_style: BezelStyle) {
|
||||
let style: NSUInteger = bezel_style.into();
|
||||
|
||||
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setBezelStyle:style];
|
||||
let _: () = msg_send![obj, setBezelStyle: style];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -206,11 +206,11 @@ impl Button {
|
|||
/// Call this to set the background color for the backing layer.
|
||||
pub fn set_background_color<C: AsRef<Color>>(&self, color: C) {
|
||||
let color: id = color.as_ref().into();
|
||||
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let cell: id = msg_send![obj, cell];
|
||||
let _: () = msg_send![cell, setBackgroundColor:color];
|
||||
let _: () = msg_send![cell, setBackgroundColor: color];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,7 @@ impl Button {
|
|||
Key::Char(s) => NSString::new(s),
|
||||
Key::Delete => NSString::new("\u{08}")
|
||||
};
|
||||
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![obj, setKeyEquivalent:&*keychar];
|
||||
}
|
||||
|
@ -236,16 +236,16 @@ impl Button {
|
|||
|
||||
/// Sets the text color for this button.
|
||||
///
|
||||
/// On appkit, this is done by way of an `AttributedString` under the hood.
|
||||
/// On appkit, this is done by way of an `AttributedString` under the hood.
|
||||
pub fn set_text_color<C: AsRef<Color>>(&self, color: C) {
|
||||
#[cfg(feature = "appkit")]
|
||||
self.objc.with_mut(move |obj| unsafe {
|
||||
let text: id = msg_send![obj, attributedTitle];
|
||||
let len: isize = msg_send![text, length];
|
||||
|
||||
|
||||
let mut attr_str = AttributedString::wrap(text);
|
||||
attr_str.set_text_color(color.as_ref(), 0..len);
|
||||
|
||||
|
||||
let _: () = msg_send![obj, setAttributedTitle:&*attr_str];
|
||||
});
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ impl Button {
|
|||
let ring_type: NSUInteger = focus_ring_type.into();
|
||||
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setFocusRingType:ring_type];
|
||||
let _: () = msg_send![obj, setFocusRingType: ring_type];
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -326,13 +326,13 @@ impl Drop for Button {
|
|||
// but I'd rather be paranoid and remove them later.
|
||||
fn drop(&mut self) {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setTarget:nil];
|
||||
let _: () = msg_send![obj, setAction:nil];
|
||||
let _: () = msg_send![obj, setTarget: nil];
|
||||
let _: () = msg_send![obj, setAction: nil];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers an `NSButton` subclass, and configures it to hold some ivars
|
||||
/// Registers an `NSButton` subclass, and configures it to hold some ivars
|
||||
/// for various things we need to store.
|
||||
fn register_class() -> *const Class {
|
||||
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
||||
|
@ -340,7 +340,7 @@ fn register_class() -> *const Class {
|
|||
|
||||
INIT.call_once(|| unsafe {
|
||||
let superclass = class!(NSButton);
|
||||
let decl = ClassDecl::new("RSTButton", superclass).unwrap();
|
||||
let decl = ClassDecl::new("RSTButton", superclass).unwrap();
|
||||
VIEW_CLASS = decl.register();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! This module provides a custom NSColor subclass for macOS that mimics the dynamic
|
||||
//! This module provides a custom NSColor subclass for macOS that mimics the dynamic
|
||||
//! UIColor provider found on iOS. Notably, this works with older versions of macOS as
|
||||
//! well; it runs the block on creation and caches the created color instances to avoid
|
||||
//! repeated allocations - this might not be a big thing to worry about as NSColor
|
||||
//! repeated allocations - this might not be a big thing to worry about as NSColor
|
||||
//! changed slightly behind the scenes in 10.15+, so this could be changed down the
|
||||
//! road.
|
||||
//!
|
||||
|
@ -19,7 +19,7 @@ use objc::declare::ClassDecl;
|
|||
use objc::runtime::{Class, Object, Sel, BOOL};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, nil, YES, NO, NSInteger, NSUInteger, NSString, NSArray};
|
||||
use crate::foundation::{id, nil, NSArray, NSInteger, NSString, NSUInteger, NO, YES};
|
||||
use crate::utils::os;
|
||||
|
||||
pub(crate) const AQUA_LIGHT_COLOR_NORMAL_CONTRAST: &'static str = "AQUA_LIGHT_COLOR_NORMAL_CONTRAST";
|
||||
|
@ -82,164 +82,180 @@ fn get_effective_color(this: &Object) -> id {
|
|||
}
|
||||
}
|
||||
|
||||
extern fn color_space(this: &Object, _: Sel) -> id {
|
||||
extern "C" fn color_space(this: &Object, _: Sel) -> id {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, colorSpace] }
|
||||
}
|
||||
|
||||
extern fn color_using_color_space(this: &Object, _: Sel, color_space: id) -> id {
|
||||
extern "C" fn color_using_color_space(this: &Object, _: Sel, color_space: id) -> id {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, colorUsingColorSpace:color_space] }
|
||||
unsafe { msg_send![color, colorUsingColorSpace: color_space] }
|
||||
}
|
||||
|
||||
extern fn color_space_name(this: &Object, _: Sel) -> id {
|
||||
extern "C" fn color_space_name(this: &Object, _: Sel) -> id {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, colorSpaceName] }
|
||||
}
|
||||
|
||||
extern fn color_using_color_space_name(this: &Object, _: Sel, color_space_name: id) -> id {
|
||||
extern "C" fn color_using_color_space_name(this: &Object, _: Sel, color_space_name: id) -> id {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, colorUsingColorSpaceName:color_space_name] }
|
||||
unsafe { msg_send![color, colorUsingColorSpaceName: color_space_name] }
|
||||
}
|
||||
|
||||
extern fn number_of_components(this: &Object, _: Sel) -> NSInteger {
|
||||
extern "C" fn number_of_components(this: &Object, _: Sel) -> NSInteger {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, numberOfComponents] }
|
||||
}
|
||||
|
||||
// @TODO: Confirm this.
|
||||
extern fn get_components(this: &Object, _: Sel, components: CGFloat) {
|
||||
extern "C" fn get_components(this: &Object, _: Sel, components: CGFloat) {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { let _: () = msg_send![color, getComponents:components]; }
|
||||
unsafe {
|
||||
let _: () = msg_send![color, getComponents: components];
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO: Confirm this.
|
||||
extern fn get_rgba(this: &Object, _: Sel, red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
|
||||
extern "C" fn get_rgba(this: &Object, _: Sel, red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { let _: () = msg_send![color, getRed:red green:green blue:blue alpha:alpha]; }
|
||||
unsafe {
|
||||
let _: () = msg_send![color, getRed:red green:green blue:blue alpha:alpha];
|
||||
}
|
||||
}
|
||||
|
||||
extern fn red_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn red_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, redComponent] }
|
||||
}
|
||||
|
||||
extern fn green_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn green_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, greenComponent] }
|
||||
}
|
||||
|
||||
extern fn blue_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn blue_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, blueComponent] }
|
||||
}
|
||||
|
||||
extern fn hue_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn hue_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, hueComponent] }
|
||||
}
|
||||
|
||||
extern fn saturation_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn saturation_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, saturationComponent] }
|
||||
}
|
||||
|
||||
extern fn brightness_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn brightness_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, brightnessComponent] }
|
||||
}
|
||||
|
||||
// @TODO: Confirm this.
|
||||
extern fn get_hsba(this: &Object, _: Sel, hue: CGFloat, sat: CGFloat, brit: CGFloat, alpha: CGFloat) {
|
||||
extern "C" fn get_hsba(this: &Object, _: Sel, hue: CGFloat, sat: CGFloat, brit: CGFloat, alpha: CGFloat) {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { let _: () = msg_send![color, getHue:hue saturation:sat brightness:brit alpha:alpha]; }
|
||||
unsafe {
|
||||
let _: () = msg_send![color, getHue:hue saturation:sat brightness:brit alpha:alpha];
|
||||
}
|
||||
}
|
||||
|
||||
extern fn white_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn white_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, whiteComponent] }
|
||||
}
|
||||
|
||||
// @TODO: Confirm this.
|
||||
extern fn get_white(this: &Object, _: Sel, white: CGFloat, alpha: CGFloat) {
|
||||
extern "C" fn get_white(this: &Object, _: Sel, white: CGFloat, alpha: CGFloat) {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { let _: () = msg_send![color, getWhite:white alpha:alpha]; }
|
||||
unsafe {
|
||||
let _: () = msg_send![color, getWhite:white alpha:alpha];
|
||||
}
|
||||
}
|
||||
|
||||
extern fn cyan_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn cyan_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, cyanComponent] }
|
||||
}
|
||||
|
||||
extern fn magenta_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn magenta_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, magentaComponent] }
|
||||
}
|
||||
|
||||
extern fn yellow_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn yellow_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, yellowComponent] }
|
||||
}
|
||||
|
||||
extern fn black_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn black_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, blackComponent] }
|
||||
}
|
||||
|
||||
// @TODO: Confirm this.
|
||||
extern fn get_cmyk(this: &Object, _: Sel, c: CGFloat, m: CGFloat, y: CGFloat, k: CGFloat, a: CGFloat) {
|
||||
extern "C" fn get_cmyk(this: &Object, _: Sel, c: CGFloat, m: CGFloat, y: CGFloat, k: CGFloat, a: CGFloat) {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { let _: () = msg_send![color, getCyan:c magenta:m yellow:y black:k alpha:a]; }
|
||||
unsafe {
|
||||
let _: () = msg_send![color, getCyan:c magenta:m yellow:y black:k alpha:a];
|
||||
}
|
||||
}
|
||||
|
||||
extern fn alpha_component(this: &Object, _: Sel) -> CGFloat {
|
||||
extern "C" fn alpha_component(this: &Object, _: Sel) -> CGFloat {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, alphaComponent] }
|
||||
}
|
||||
|
||||
extern fn cg_color(this: &Object, _: Sel) -> id {
|
||||
extern "C" fn cg_color(this: &Object, _: Sel) -> id {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, CGColor] }
|
||||
}
|
||||
|
||||
extern fn set_stroke(this: &Object, _: Sel) {
|
||||
extern "C" fn set_stroke(this: &Object, _: Sel) {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { let _: () = msg_send![color, setStroke]; }
|
||||
unsafe {
|
||||
let _: () = msg_send![color, setStroke];
|
||||
}
|
||||
}
|
||||
|
||||
extern fn set_fill(this: &Object, _: Sel) {
|
||||
extern "C" fn set_fill(this: &Object, _: Sel) {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { let _: () = msg_send![color, setFill]; }
|
||||
unsafe {
|
||||
let _: () = msg_send![color, setFill];
|
||||
}
|
||||
}
|
||||
|
||||
extern fn call_set(this: &Object, _: Sel) {
|
||||
extern "C" fn call_set(this: &Object, _: Sel) {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { let _: () = msg_send![color, set]; }
|
||||
unsafe {
|
||||
let _: () = msg_send![color, set];
|
||||
}
|
||||
}
|
||||
|
||||
extern fn highlight_with_level(this: &Object, _: Sel, level: CGFloat) -> id {
|
||||
extern "C" fn highlight_with_level(this: &Object, _: Sel, level: CGFloat) -> id {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, highlightWithLevel:level] }
|
||||
unsafe { msg_send![color, highlightWithLevel: level] }
|
||||
}
|
||||
|
||||
extern fn shadow_with_level(this: &Object, _: Sel, level: CGFloat) -> id {
|
||||
extern "C" fn shadow_with_level(this: &Object, _: Sel, level: CGFloat) -> id {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, shadowWithLevel:level] }
|
||||
unsafe { msg_send![color, shadowWithLevel: level] }
|
||||
}
|
||||
|
||||
extern fn color_with_alpha_component(this: &Object, _: Sel, alpha: CGFloat) -> id {
|
||||
extern "C" fn color_with_alpha_component(this: &Object, _: Sel, alpha: CGFloat) -> id {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, colorWithAlphaComponent:alpha] }
|
||||
unsafe { msg_send![color, colorWithAlphaComponent: alpha] }
|
||||
}
|
||||
|
||||
extern fn blended_color(this: &Object, _: Sel, fraction: CGFloat, with_color: id) -> id {
|
||||
extern "C" fn blended_color(this: &Object, _: Sel, fraction: CGFloat, with_color: id) -> id {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, blendedColorWithFraction:fraction ofColor:with_color] }
|
||||
}
|
||||
|
||||
extern fn color_with_system_effect(this: &Object, _: Sel, effect: NSInteger) -> id {
|
||||
extern "C" fn color_with_system_effect(this: &Object, _: Sel, effect: NSInteger) -> id {
|
||||
let color = get_effective_color(this);
|
||||
unsafe { msg_send![color, colorWithSystemEffect:effect] }
|
||||
unsafe { msg_send![color, colorWithSystemEffect: effect] }
|
||||
}
|
||||
|
||||
pub(crate) fn register_class() -> *const Class {
|
||||
|
@ -251,51 +267,99 @@ pub(crate) fn register_class() -> *const Class {
|
|||
let mut decl = ClassDecl::new("CacaoDynamicColor", superclass).unwrap();
|
||||
|
||||
// These methods all need to be forwarded, so let's hook them up.
|
||||
decl.add_method(sel!(colorSpace), color_space as extern fn(&Object, _) -> id);
|
||||
decl.add_method(sel!(colorUsingColorSpace:), color_using_color_space as extern fn(&Object, _, id) -> id);
|
||||
decl.add_method(sel!(colorSpaceName), color_space_name as extern fn(&Object, _) -> id);
|
||||
decl.add_method(sel!(colorUsingColorSpaceName:), color_using_color_space_name as extern fn(&Object, _, id) -> id);
|
||||
decl.add_method(sel!(numberOfComponents), number_of_components as extern fn(&Object, _) -> NSInteger);
|
||||
decl.add_method(sel!(colorSpace), color_space as extern "C" fn(&Object, _) -> id);
|
||||
decl.add_method(
|
||||
sel!(colorUsingColorSpace:),
|
||||
color_using_color_space as extern "C" fn(&Object, _, id) -> id
|
||||
);
|
||||
decl.add_method(sel!(colorSpaceName), color_space_name as extern "C" fn(&Object, _) -> id);
|
||||
decl.add_method(
|
||||
sel!(colorUsingColorSpaceName:),
|
||||
color_using_color_space_name as extern "C" fn(&Object, _, id) -> id
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(numberOfComponents),
|
||||
number_of_components as extern "C" fn(&Object, _) -> NSInteger
|
||||
);
|
||||
|
||||
decl.add_method(sel!(getComponents:), get_components as extern fn(&Object, _, CGFloat));
|
||||
decl.add_method(sel!(getRed:green:blue:alpha:), get_rgba as extern fn(&Object, _, CGFloat, CGFloat, CGFloat, CGFloat));
|
||||
decl.add_method(sel!(redComponent), red_component as extern fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(greenComponent), green_component as extern fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(blueComponent), blue_component as extern fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(getComponents:), get_components as extern "C" fn(&Object, _, CGFloat));
|
||||
decl.add_method(
|
||||
sel!(getRed:green:blue:alpha:),
|
||||
get_rgba as extern "C" fn(&Object, _, CGFloat, CGFloat, CGFloat, CGFloat)
|
||||
);
|
||||
decl.add_method(sel!(redComponent), red_component as extern "C" fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(greenComponent), green_component as extern "C" fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(blueComponent), blue_component as extern "C" fn(&Object, _) -> CGFloat);
|
||||
|
||||
decl.add_method(sel!(hueComponent), hue_component as extern fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(saturationComponent), saturation_component as extern fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(brightnessComponent), brightness_component as extern fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(getHue:saturation:brightness:alpha:), get_hsba as extern fn(&Object, _, CGFloat, CGFloat, CGFloat, CGFloat));
|
||||
decl.add_method(sel!(hueComponent), hue_component as extern "C" fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(
|
||||
sel!(saturationComponent),
|
||||
saturation_component as extern "C" fn(&Object, _) -> CGFloat
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(brightnessComponent),
|
||||
brightness_component as extern "C" fn(&Object, _) -> CGFloat
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(getHue:saturation:brightness:alpha:),
|
||||
get_hsba as extern "C" fn(&Object, _, CGFloat, CGFloat, CGFloat, CGFloat)
|
||||
);
|
||||
|
||||
decl.add_method(sel!(whiteComponent), white_component as extern fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(getWhite:alpha:), get_white as extern fn(&Object, _, CGFloat, CGFloat));
|
||||
decl.add_method(sel!(whiteComponent), white_component as extern "C" fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(
|
||||
sel!(getWhite:alpha:),
|
||||
get_white as extern "C" fn(&Object, _, CGFloat, CGFloat)
|
||||
);
|
||||
|
||||
decl.add_method(sel!(cyanComponent), cyan_component as extern fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(magentaComponent), magenta_component as extern fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(yellowComponent), yellow_component as extern fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(blackComponent), black_component as extern fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(sel!(getCyan:magenta:yellow:black:alpha:), get_cmyk as extern fn(&Object, _, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat));
|
||||
decl.add_method(sel!(cyanComponent), cyan_component as extern "C" fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(
|
||||
sel!(magentaComponent),
|
||||
magenta_component as extern "C" fn(&Object, _) -> CGFloat
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(yellowComponent),
|
||||
yellow_component as extern "C" fn(&Object, _) -> CGFloat
|
||||
);
|
||||
decl.add_method(sel!(blackComponent), black_component as extern "C" fn(&Object, _) -> CGFloat);
|
||||
decl.add_method(
|
||||
sel!(getCyan:magenta:yellow:black:alpha:),
|
||||
get_cmyk as extern "C" fn(&Object, _, CGFloat, CGFloat, CGFloat, CGFloat, CGFloat)
|
||||
);
|
||||
|
||||
decl.add_method(sel!(alphaComponent), alpha_component as extern fn(&Object, _) -> CGFloat);
|
||||
|
||||
decl.add_method(sel!(CGColor), cg_color as extern fn(&Object, _) -> id);
|
||||
decl.add_method(sel!(setStroke), set_stroke as extern fn(&Object, _));
|
||||
decl.add_method(sel!(setFill), set_fill as extern fn(&Object, _));
|
||||
decl.add_method(sel!(set), call_set as extern fn(&Object, _));
|
||||
decl.add_method(sel!(alphaComponent), alpha_component as extern "C" fn(&Object, _) -> CGFloat);
|
||||
|
||||
decl.add_method(sel!(highlightWithLevel:), highlight_with_level as extern fn(&Object, _, CGFloat) -> id);
|
||||
decl.add_method(sel!(shadowWithLevel:), shadow_with_level as extern fn(&Object, _, CGFloat) -> id);
|
||||
decl.add_method(sel!(CGColor), cg_color as extern "C" fn(&Object, _) -> id);
|
||||
decl.add_method(sel!(setStroke), set_stroke as extern "C" fn(&Object, _));
|
||||
decl.add_method(sel!(setFill), set_fill as extern "C" fn(&Object, _));
|
||||
decl.add_method(sel!(set), call_set as extern "C" fn(&Object, _));
|
||||
|
||||
decl.add_method(sel!(colorWithAlphaComponent:), color_with_alpha_component as extern fn(&Object, _, CGFloat) -> id);
|
||||
decl.add_method(sel!(blendedColorWithFraction:ofColor:), blended_color as extern fn(&Object, _, CGFloat, id) -> id);
|
||||
decl.add_method(sel!(colorWithSystemEffect:), color_with_system_effect as extern fn(&Object, _, NSInteger) -> id);
|
||||
decl.add_method(
|
||||
sel!(highlightWithLevel:),
|
||||
highlight_with_level as extern "C" fn(&Object, _, CGFloat) -> id
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(shadowWithLevel:),
|
||||
shadow_with_level as extern "C" fn(&Object, _, CGFloat) -> id
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(colorWithAlphaComponent:),
|
||||
color_with_alpha_component as extern "C" fn(&Object, _, CGFloat) -> id
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(blendedColorWithFraction:ofColor:),
|
||||
blended_color as extern "C" fn(&Object, _, CGFloat, id) -> id
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(colorWithSystemEffect:),
|
||||
color_with_system_effect as extern "C" fn(&Object, _, NSInteger) -> id
|
||||
);
|
||||
|
||||
decl.add_ivar::<id>(AQUA_LIGHT_COLOR_NORMAL_CONTRAST);
|
||||
decl.add_ivar::<id>(AQUA_LIGHT_COLOR_HIGH_CONTRAST);
|
||||
decl.add_ivar::<id>(AQUA_DARK_COLOR_NORMAL_CONTRAST);
|
||||
decl.add_ivar::<id>(AQUA_DARK_COLOR_HIGH_CONTRAST);
|
||||
|
||||
|
||||
VIEW_CLASS = decl.register();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! Implements a wrapper type for `NSColor` and `UIColor`. It attempts to map
|
||||
//! to a common shared API, but it's important to note that the platforms
|
||||
//! Implements a wrapper type for `NSColor` and `UIColor`. It attempts to map
|
||||
//! to a common shared API, but it's important to note that the platforms
|
||||
//! themselves have differing levels of support for color work. Where possible,
|
||||
//! we expose some platform-specific methods for creating and working with these.
|
||||
//!
|
||||
|
@ -13,26 +13,25 @@
|
|||
/// fallbacks, specify the `color_fallbacks` target_os in your `Cargo.toml`.
|
||||
///
|
||||
/// @TODO: bundle iOS/tvOS support.
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use core_graphics::base::CGFloat;
|
||||
use core_graphics::color::CGColor;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::id;
|
||||
use crate::utils::os;
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
mod appkit_dynamic_color;
|
||||
mod appkit_dynamic_color;
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
use appkit_dynamic_color::{
|
||||
AQUA_LIGHT_COLOR_NORMAL_CONTRAST, AQUA_LIGHT_COLOR_HIGH_CONTRAST,
|
||||
AQUA_DARK_COLOR_NORMAL_CONTRAST, AQUA_DARK_COLOR_HIGH_CONTRAST
|
||||
AQUA_DARK_COLOR_HIGH_CONTRAST, AQUA_DARK_COLOR_NORMAL_CONTRAST, AQUA_LIGHT_COLOR_HIGH_CONTRAST,
|
||||
AQUA_LIGHT_COLOR_NORMAL_CONTRAST
|
||||
};
|
||||
|
||||
/// Represents a rendering style - dark mode or light mode.
|
||||
|
@ -49,7 +48,7 @@ pub enum Theme {
|
|||
Dark
|
||||
}
|
||||
|
||||
/// Represents the contrast level for a rendering context.
|
||||
/// Represents the contrast level for a rendering context.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Contrast {
|
||||
/// The default contrast level for the system.
|
||||
|
@ -71,7 +70,7 @@ pub struct Style {
|
|||
pub contrast: Contrast
|
||||
}
|
||||
|
||||
/// Represents a Color. You can create custom colors using the various
|
||||
/// Represents a Color. You can create custom colors using the various
|
||||
/// initializers, or opt to use a system-provided color. The system provided
|
||||
/// colors will automatically switch to the "correct" colors/shades depending on whether
|
||||
/// the user is in light or dark mode; to support this with custom colors, you can create a
|
||||
|
@ -84,7 +83,7 @@ pub enum Color {
|
|||
/// Represents an `NSColor` on macOS, and a `UIColor` everywhere else. You typically
|
||||
/// don't create this variant yourself; use the initializers found on this enum.
|
||||
///
|
||||
/// If you need to do custom work not covered by this enum, you can drop to
|
||||
/// If you need to do custom work not covered by this enum, you can drop to
|
||||
/// the Objective-C level yourself and wrap your color in this.
|
||||
Custom(Arc<RwLock<Id<Object>>>),
|
||||
|
||||
|
@ -136,7 +135,7 @@ pub enum Color {
|
|||
/// The system-provided base gray color.
|
||||
/// This value automatically switches to the correct variant depending on light or dark mode.
|
||||
SystemGray,
|
||||
|
||||
|
||||
/// The system-provided secondary-level gray color.
|
||||
/// This value automatically switches to the correct variant depending on light or dark mode.
|
||||
SystemGray2,
|
||||
|
@ -148,11 +147,11 @@ pub enum Color {
|
|||
/// The system-provided fourth-level gray color.
|
||||
/// This value automatically switches to the correct variant depending on light or dark mode.
|
||||
SystemGray4,
|
||||
|
||||
|
||||
/// The system-provided fifth-level gray color.
|
||||
/// This value automatically switches to the correct variant depending on light or dark mode.
|
||||
SystemGray5,
|
||||
|
||||
|
||||
/// The system-provided sixth-level gray color.
|
||||
/// This value automatically switches to the correct variant depending on light or dark mode.
|
||||
SystemGray6,
|
||||
|
@ -184,11 +183,11 @@ pub enum Color {
|
|||
/// The default system second-level fill color.
|
||||
/// This value automatically switches to the correct variant depending on light or dark mode.
|
||||
SystemFillSecondary,
|
||||
|
||||
|
||||
/// The default system third-level fill color.
|
||||
/// This value automatically switches to the correct variant depending on light or dark mode.
|
||||
SystemFillTertiary,
|
||||
|
||||
|
||||
/// The default system fourth-level fill color.
|
||||
/// This value automatically switches to the correct variant depending on light or dark mode.
|
||||
SystemFillQuaternary,
|
||||
|
@ -204,7 +203,7 @@ pub enum Color {
|
|||
/// The default system second-level background color.
|
||||
/// This value automatically switches to the correct variant depending on light or dark mode.
|
||||
SystemBackgroundSecondary,
|
||||
|
||||
|
||||
/// The default system third-level background color.
|
||||
/// This value automatically switches to the correct variant depending on light or dark mode.
|
||||
SystemBackgroundTertiary,
|
||||
|
@ -250,13 +249,17 @@ impl Color {
|
|||
|
||||
Color::Custom(Arc::new(RwLock::new(unsafe {
|
||||
#[cfg(feature = "appkit")]
|
||||
{ Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedRed:r green:g blue:b alpha:a]) }
|
||||
{
|
||||
Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedRed:r green:g blue:b alpha:a])
|
||||
}
|
||||
|
||||
#[cfg(feature = "uikit")]
|
||||
{ Id::from_ptr(msg_send![class!(UIColor), colorWithRed:r green:g blue:b alpha:a]) }
|
||||
{
|
||||
Id::from_ptr(msg_send![class!(UIColor), colorWithRed:r green:g blue:b alpha:a])
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
/// Creates and returns a color in the RGB space, with the alpha level
|
||||
/// set to `255` by default. Shorthand for `rgba`.
|
||||
pub fn rgb(red: u8, green: u8, blue: u8) -> Self {
|
||||
|
@ -273,13 +276,17 @@ impl Color {
|
|||
|
||||
Color::Custom(Arc::new(RwLock::new(unsafe {
|
||||
#[cfg(feature = "appkit")]
|
||||
{ Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedHue:h saturation:s brightness:b alpha:a]) }
|
||||
{
|
||||
Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedHue:h saturation:s brightness:b alpha:a])
|
||||
}
|
||||
|
||||
#[cfg(feature = "uikit")]
|
||||
{ Id::from_ptr(msg_send![class!(UIColor), colorWithHue:h saturation:s brightness:b alpha:a]) }
|
||||
{
|
||||
Id::from_ptr(msg_send![class!(UIColor), colorWithHue:h saturation:s brightness:b alpha:a])
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
/// Creates and returns a color in the RGB space, with the alpha level
|
||||
/// set to `255` by default. Shorthand for `hsba`.
|
||||
pub fn hsb(hue: u8, saturation: u8, brightness: u8) -> Self {
|
||||
|
@ -291,10 +298,14 @@ impl Color {
|
|||
pub fn white_alpha(level: CGFloat, alpha: CGFloat) -> Self {
|
||||
Color::Custom(Arc::new(RwLock::new(unsafe {
|
||||
#[cfg(feature = "appkit")]
|
||||
{ Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedWhite:level alpha:alpha]) }
|
||||
|
||||
{
|
||||
Id::from_ptr(msg_send![class!(NSColor), colorWithCalibratedWhite:level alpha:alpha])
|
||||
}
|
||||
|
||||
#[cfg(feature = "uikit")]
|
||||
{ Id::from_ptr(msg_send![class!(UIColor), colorWithWhite:level alpha:alpha]) }
|
||||
{
|
||||
Id::from_ptr(msg_send![class!(UIColor), colorWithWhite:level alpha:alpha])
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
|
@ -303,7 +314,7 @@ impl Color {
|
|||
pub fn white(level: CGFloat) -> Self {
|
||||
Color::white_alpha(level, 1.0)
|
||||
}
|
||||
|
||||
|
||||
/// Given a hex code and alpha level, returns a `Color` in the RGB space.
|
||||
///
|
||||
/// This method is not an ideal one to use, but is offered as a convenience method for those
|
||||
|
@ -346,7 +357,8 @@ impl Color {
|
|||
let color: id = handler(Style {
|
||||
theme: Theme::Light,
|
||||
contrast: Contrast::Normal
|
||||
}).into();
|
||||
})
|
||||
.into();
|
||||
|
||||
color
|
||||
});
|
||||
|
@ -355,7 +367,8 @@ impl Color {
|
|||
let color: id = handler(Style {
|
||||
theme: Theme::Light,
|
||||
contrast: Contrast::High
|
||||
}).into();
|
||||
})
|
||||
.into();
|
||||
|
||||
color
|
||||
});
|
||||
|
@ -364,7 +377,8 @@ impl Color {
|
|||
let color: id = handler(Style {
|
||||
theme: Theme::Dark,
|
||||
contrast: Contrast::Normal
|
||||
}).into();
|
||||
})
|
||||
.into();
|
||||
|
||||
color
|
||||
});
|
||||
|
@ -373,19 +387,20 @@ impl Color {
|
|||
let color: id = handler(Style {
|
||||
theme: Theme::Light,
|
||||
contrast: Contrast::Normal
|
||||
}).into();
|
||||
})
|
||||
.into();
|
||||
|
||||
color
|
||||
});
|
||||
|
||||
|
||||
Id::from_ptr(color)
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
/// Returns a CGColor, which can be used in Core Graphics calls as well as other areas.
|
||||
///
|
||||
/// Note that CGColor is _not_ a context-aware color, unlike our `NSColor` and `UIColor`
|
||||
/// objects. If you're painting in a context that requires dark mode support, make sure
|
||||
/// objects. If you're painting in a context that requires dark mode support, make sure
|
||||
/// you're not using a cached version of this unless you explicitly want the _same_ color
|
||||
/// in every context it's used in.
|
||||
pub fn cg_color(&self) -> CGColor {
|
||||
|
@ -421,7 +436,7 @@ impl From<&Color> for id {
|
|||
|
||||
/// Handles color fallback for system-provided colors.
|
||||
macro_rules! system_color_with_fallback {
|
||||
($class:ident, $color:ident, $fallback:ident) => ({
|
||||
($class:ident, $color:ident, $fallback:ident) => {{
|
||||
#[cfg(feature = "appkit")]
|
||||
{
|
||||
#[cfg(feature = "color-fallbacks")]
|
||||
|
@ -439,7 +454,7 @@ macro_rules! system_color_with_fallback {
|
|||
{
|
||||
msg_send![$class, $color]
|
||||
}
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
/// This function maps enum types to system-provided colors, or our stored NS/UIColor objects.
|
||||
|
@ -471,7 +486,7 @@ unsafe fn to_objc(obj: &Color) -> id {
|
|||
Color::SystemGreen => system_color_with_fallback!(color, systemGreenColor, greenColor),
|
||||
Color::SystemIndigo => system_color_with_fallback!(color, systemIndigoColor, magentaColor),
|
||||
Color::SystemOrange => system_color_with_fallback!(color, systemOrangeColor, orangeColor),
|
||||
Color::SystemPink => system_color_with_fallback!(color, systemPinkColor, pinkColor),
|
||||
Color::SystemPink => system_color_with_fallback!(color, systemPinkColor, pinkColor),
|
||||
Color::SystemPurple => system_color_with_fallback!(color, systemPurpleColor, purpleColor),
|
||||
Color::SystemRed => system_color_with_fallback!(color, systemRedColor, redColor),
|
||||
Color::SystemTeal => system_color_with_fallback!(color, systemTealColor, blueColor),
|
||||
|
@ -496,18 +511,18 @@ unsafe fn to_objc(obj: &Color) -> id {
|
|||
Color::SystemBackgroundSecondary => system_color_with_fallback!(color, secondarySystemBackgroundColor, clearColor),
|
||||
Color::SystemBackgroundTertiary => system_color_with_fallback!(color, tertiarySystemBackgroundColor, clearColor),
|
||||
Color::Separator => system_color_with_fallback!(color, separatorColor, lightGrayColor),
|
||||
|
||||
|
||||
#[cfg(feature = "uikit")]
|
||||
Color::OpaqueSeparator => system_color_with_fallback!(color, opaqueSeparatorColor, darkGrayColor),
|
||||
|
||||
Color::Link => system_color_with_fallback!(color, linkColor, blueColor),
|
||||
Color::DarkText => system_color_with_fallback!(color, darkTextColor, blackColor),
|
||||
Color::LightText => system_color_with_fallback!(color, lightTextColor, whiteColor),
|
||||
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
Color::MacOSWindowBackgroundColor => system_color_with_fallback!(color, windowBackgroundColor, clearColor),
|
||||
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
Color::MacOSUnderPageBackgroundColor => system_color_with_fallback!(color, underPageBackgroundColor, clearColor),
|
||||
Color::MacOSUnderPageBackgroundColor => system_color_with_fallback!(color, underPageBackgroundColor, clearColor)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use crate::foundation::{id, YES, NO, NSUInteger};
|
||||
use crate::foundation::{id, NSUInteger, NO, YES};
|
||||
use crate::objc_access::ObjcAccess;
|
||||
|
||||
/// Use this enum for specifying NSControl size types.
|
||||
|
@ -13,7 +12,7 @@ pub enum ControlSize {
|
|||
|
||||
/// A smaller control size.
|
||||
Small,
|
||||
|
||||
|
||||
/// The default, regular, size.
|
||||
Regular,
|
||||
|
||||
|
@ -36,7 +35,7 @@ pub trait Control: ObjcAccess {
|
|||
});
|
||||
}
|
||||
|
||||
/// Sets the underlying control size.
|
||||
/// Sets the underlying control size.
|
||||
fn set_control_size(&self, size: ControlSize) {
|
||||
let control_size: NSUInteger = match size {
|
||||
ControlSize::Mini => 2,
|
||||
|
@ -50,7 +49,7 @@ pub trait Control: ObjcAccess {
|
|||
};
|
||||
|
||||
self.with_backing_obj_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setControlSize:control_size];
|
||||
let _: () = msg_send![obj, setControlSize: control_size];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,14 +33,11 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{
|
||||
id, nil, to_bool, YES, NO, BOOL,
|
||||
NSData, NSString, NSMutableDictionary, NSNumber
|
||||
};
|
||||
use crate::foundation::{id, nil, to_bool, NSData, NSMutableDictionary, NSNumber, NSString, BOOL, NO, YES};
|
||||
|
||||
mod value;
|
||||
pub use value::Value;
|
||||
|
@ -61,7 +58,7 @@ impl Default for UserDefaults {
|
|||
|
||||
impl UserDefaults {
|
||||
/// Returns the `standardUserDefaults`, which is... exactly what it sounds like.
|
||||
///
|
||||
///
|
||||
/// _Note that if you're planning to share preferences across things (e.g, an app and an
|
||||
/// extension) you *probably* want to use `suite()` instead!_
|
||||
///
|
||||
|
@ -69,13 +66,11 @@ impl UserDefaults {
|
|||
/// use cacao::defaults::UserDefaults;
|
||||
///
|
||||
/// let defaults = UserDefaults::standard();
|
||||
///
|
||||
///
|
||||
/// let _ = defaults.get("test");
|
||||
/// ```
|
||||
pub fn standard() -> Self {
|
||||
UserDefaults(unsafe {
|
||||
Id::from_ptr(msg_send![class!(NSUserDefaults), standardUserDefaults])
|
||||
})
|
||||
UserDefaults(unsafe { Id::from_ptr(msg_send![class!(NSUserDefaults), standardUserDefaults]) })
|
||||
}
|
||||
|
||||
/// Returns a user defaults instance for the given suite name. You typically use this to share
|
||||
|
@ -108,7 +103,7 @@ impl UserDefaults {
|
|||
/// use cacao::defaults::{UserDefaults, Value};
|
||||
///
|
||||
/// let mut defaults = UserDefaults::standard();
|
||||
///
|
||||
///
|
||||
/// defaults.register({
|
||||
/// let mut map = HashMap::new();
|
||||
/// map.insert("test", Value::Bool(true));
|
||||
|
@ -117,7 +112,7 @@ impl UserDefaults {
|
|||
/// ```
|
||||
pub fn register<K: AsRef<str>>(&mut self, values: HashMap<K, Value>) {
|
||||
let dictionary = NSMutableDictionary::from(values);
|
||||
|
||||
|
||||
unsafe {
|
||||
let _: () = msg_send![&*self.0, registerDefaults:&*dictionary];
|
||||
}
|
||||
|
@ -140,7 +135,7 @@ impl UserDefaults {
|
|||
let _: () = msg_send![&*self.0, setObject:value forKey:key];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Remove the default associated with the key. If the key doesn't exist, this is a noop.
|
||||
///
|
||||
/// ```rust
|
||||
|
@ -177,9 +172,7 @@ impl UserDefaults {
|
|||
pub fn get<K: AsRef<str>>(&self, key: K) -> Option<Value> {
|
||||
let key = NSString::new(key.as_ref());
|
||||
|
||||
let result: id = unsafe {
|
||||
msg_send![&*self.0, objectForKey:&*key]
|
||||
};
|
||||
let result: id = unsafe { msg_send![&*self.0, objectForKey:&*key] };
|
||||
|
||||
if result == nil {
|
||||
return None;
|
||||
|
@ -206,7 +199,7 @@ impl UserDefaults {
|
|||
// For context: https://nshipster.com/type-encodings/
|
||||
if NSNumber::is(result) {
|
||||
let number = NSNumber::wrap(result);
|
||||
|
||||
|
||||
return match number.objc_type() {
|
||||
"c" => Some(Value::Bool(number.as_bool())),
|
||||
"d" => Some(Value::Float(number.as_f64())),
|
||||
|
@ -227,8 +220,8 @@ impl UserDefaults {
|
|||
|
||||
/// Returns a boolean value if the object stored for the specified key is managed by an
|
||||
/// administrator. This is rarely used - mostly in managed environments, e.g a classroom.
|
||||
///
|
||||
/// For managed keys, the application should disable any user interface that allows the
|
||||
///
|
||||
/// For managed keys, the application should disable any user interface that allows the
|
||||
/// user to modify the value of key.
|
||||
///
|
||||
/// ```rust
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::foundation::{id, NSData, NSMutableDictionary, NSString, NSNumber};
|
||||
use crate::foundation::{id, NSData, NSMutableDictionary, NSNumber, NSString};
|
||||
|
||||
/// Represents a Value that can be stored or queried with `UserDefaults`.
|
||||
///
|
||||
|
@ -31,7 +31,7 @@ impl Value {
|
|||
pub fn string<S: Into<String>>(value: S) -> Self {
|
||||
Value::String(value.into())
|
||||
}
|
||||
|
||||
|
||||
/// Returns `true` if the value is a boolean value. Returns `false` otherwise.
|
||||
pub fn is_boolean(&self) -> bool {
|
||||
match self {
|
||||
|
@ -47,7 +47,7 @@ impl Value {
|
|||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns `true` if the value is a string. Returns `false` otherwise.
|
||||
pub fn is_string(&self) -> bool {
|
||||
match self {
|
||||
|
@ -63,7 +63,7 @@ impl Value {
|
|||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns `true` if the value is a float. Returns `false` otherwise.
|
||||
pub fn is_integer(&self) -> bool {
|
||||
match self {
|
||||
|
@ -154,7 +154,7 @@ where
|
|||
let mut dictionary = NSMutableDictionary::new();
|
||||
|
||||
for (key, value) in map.into_iter() {
|
||||
let k = NSString::new(key.as_ref());
|
||||
let k = NSString::new(key.as_ref());
|
||||
dictionary.insert(k, value.into());
|
||||
}
|
||||
|
||||
|
|
|
@ -31,11 +31,9 @@ pub enum DragOperation {
|
|||
Move,
|
||||
|
||||
/// The data can be deleted.
|
||||
Delete,
|
||||
|
||||
// All of the above.
|
||||
// @TODO: NSUIntegerMax, a tricky beast
|
||||
// Every
|
||||
Delete // All of the above.
|
||||
// @TODO: NSUIntegerMax, a tricky beast
|
||||
// Every
|
||||
}
|
||||
|
||||
impl From<DragOperation> for NSUInteger {
|
||||
|
@ -65,8 +63,6 @@ impl DragInfo {
|
|||
///
|
||||
/// Note: in general, you should not store pasteboards.
|
||||
pub fn get_pasteboard(&self) -> Pasteboard {
|
||||
unsafe {
|
||||
Pasteboard::with(msg_send![&*self.info, draggingPasteboard])
|
||||
}
|
||||
unsafe { Pasteboard::with(msg_send![&*self.info, draggingPasteboard]) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,9 @@ impl From<NSInteger> for ModalResponse {
|
|||
// @TODO: Definitely don't panic here, wtf was I thinking?
|
||||
// Probably make this a ModalResponse::Unknown or something so a user can
|
||||
// gracefully handle.
|
||||
e => { panic!("Unknown NSModalResponse sent back! {}", e); }
|
||||
e => {
|
||||
panic!("Unknown NSModalResponse sent back! {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +158,7 @@ pub enum SearchPathDirectory {
|
|||
/// (_/Library/PreferencePanes_)
|
||||
PreferencePanes,
|
||||
|
||||
/// The user scripts folder for the calling application
|
||||
/// The user scripts folder for the calling application
|
||||
/// (_~/Library/Application Scripts/<code-signing-id>_).
|
||||
ApplicationScripts,
|
||||
|
||||
|
|
|
@ -4,18 +4,18 @@
|
|||
use std::error::Error;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use objc_id::Id;
|
||||
use objc::runtime::{BOOL, Object};
|
||||
use objc::runtime::{Object, BOOL};
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
use url::Url;
|
||||
|
||||
use crate::foundation::{id, nil, NO, NSString, NSUInteger};
|
||||
use crate::error::Error as AppKitError;
|
||||
use crate::filesystem::enums::{SearchPathDirectory, SearchPathDomainMask};
|
||||
use crate::foundation::{id, nil, NSString, NSUInteger, NO};
|
||||
|
||||
/// A FileManager can be used for file operations (moving files, etc).
|
||||
///
|
||||
/// If your app is not sandboxed, you can use your favorite Rust library -
|
||||
/// If your app is not sandboxed, you can use your favorite Rust library -
|
||||
/// but if you _are_ operating in the sandbox, there's a good chance you'll want to use this.
|
||||
///
|
||||
/// @TODO: Couldn't this just be a ShareId?
|
||||
|
@ -45,17 +45,13 @@ impl FileManager {
|
|||
/// Given a directory/domain combination, will attempt to get the directory that matches.
|
||||
/// Returns a PathBuf that wraps the given location. If there's an error on the Objective-C
|
||||
/// side, we attempt to catch it and bubble it up.
|
||||
pub fn get_directory(
|
||||
&self,
|
||||
directory: SearchPathDirectory,
|
||||
in_domain: SearchPathDomainMask
|
||||
) -> Result<Url, Box<dyn Error>> {
|
||||
pub fn get_directory(&self, directory: SearchPathDirectory, in_domain: SearchPathDomainMask) -> Result<Url, Box<dyn Error>> {
|
||||
let dir: NSUInteger = directory.into();
|
||||
let mask: NSUInteger = in_domain.into();
|
||||
|
||||
let directory = unsafe {
|
||||
let manager = self.0.read().unwrap();
|
||||
let dir: id = msg_send![&**manager, URLForDirectory:dir
|
||||
let dir: id = msg_send![&**manager, URLForDirectory:dir
|
||||
inDomain:mask
|
||||
appropriateForURL:nil
|
||||
create:NO
|
||||
|
@ -63,7 +59,7 @@ impl FileManager {
|
|||
|
||||
NSString::retain(msg_send![dir, absoluteString])
|
||||
};
|
||||
|
||||
|
||||
Url::parse(directory.to_str()).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
pub mod enums;
|
||||
pub use enums::*;
|
||||
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
use block::ConcreteBlock;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::foundation::{id, nil, YES, NO, NSInteger, NSString};
|
||||
use crate::foundation::{id, nil, NSInteger, NSString, NO, YES};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileSavePanel {
|
||||
|
@ -39,9 +39,7 @@ impl FileSavePanel {
|
|||
ShareId::from_ptr(x)
|
||||
},
|
||||
|
||||
delegate: unsafe {
|
||||
ShareId::from_ptr(msg_send![class!(NSObject), new])
|
||||
},
|
||||
delegate: unsafe { ShareId::from_ptr(msg_send![class!(NSObject), new]) },
|
||||
|
||||
can_create_directories: true
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ use std::path::PathBuf;
|
|||
|
||||
use block::ConcreteBlock;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::foundation::{id, nil, YES, NO, NSInteger, NSString, NSURL};
|
||||
use crate::filesystem::enums::ModalResponse;
|
||||
use crate::foundation::{id, nil, NSInteger, NSString, NO, NSURL, YES};
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
use crate::appkit::window::{Window, WindowDelegate};
|
||||
|
@ -30,13 +30,13 @@ pub struct FileSelectPanel {
|
|||
/// Whether the user can choose directories. Defaults to `false`.
|
||||
pub can_choose_directories: bool,
|
||||
|
||||
/// When the value of this property is true, dropping an alias on the panel or asking
|
||||
/// for filenames or URLs returns the resolved aliases. The default value of this property
|
||||
/// is true. When this value is false, selecting an alias returns the alias instead of the
|
||||
/// When the value of this property is true, dropping an alias on the panel or asking
|
||||
/// for filenames or URLs returns the resolved aliases. The default value of this property
|
||||
/// is true. When this value is false, selecting an alias returns the alias instead of the
|
||||
/// file or directory it represents.
|
||||
pub resolves_aliases: bool,
|
||||
|
||||
/// When the value of this property is true, the user may select multiple items from the
|
||||
/// When the value of this property is true, the user may select multiple items from the
|
||||
/// browser. Defaults to `false`.
|
||||
pub allows_multiple_selection: bool
|
||||
}
|
||||
|
@ -58,9 +58,7 @@ impl FileSelectPanel {
|
|||
ShareId::from_ptr(x)
|
||||
},
|
||||
|
||||
delegate: unsafe {
|
||||
ShareId::from_ptr(msg_send![class!(NSObject), new])
|
||||
},
|
||||
delegate: unsafe { ShareId::from_ptr(msg_send![class!(NSObject), new]) },
|
||||
|
||||
can_choose_files: true,
|
||||
can_choose_directories: false,
|
||||
|
@ -202,8 +200,8 @@ fn get_urls(panel: &Object) -> Vec<NSURL> {
|
|||
let urls: id = msg_send![&*panel, URLs];
|
||||
let count: usize = msg_send![urls, count];
|
||||
|
||||
(0..count).map(|index| {
|
||||
NSURL::retain(msg_send![urls, objectAtIndex:index])
|
||||
}).collect()
|
||||
(0..count)
|
||||
.map(|index| NSURL::retain(msg_send![urls, objectAtIndex: index]))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,17 +11,17 @@ pub trait OpenSaveController {
|
|||
/// Notifies you that the user changed directories.
|
||||
fn did_change_to_directory(&self, _url: &str) {}
|
||||
|
||||
/// Notifies you that the Save panel is about to expand or collapse because the user
|
||||
/// Notifies you that the Save panel is about to expand or collapse because the user
|
||||
/// clicked the disclosure triangle that displays or hides the file browser.
|
||||
fn will_expand(&self, _expanding: bool) {}
|
||||
|
||||
/// Determine whether the specified URL should be enabled in the Open panel.
|
||||
fn should_enable_url(&self, _url: &str) -> bool { true }
|
||||
fn should_enable_url(&self, _url: &str) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait you can implement for working with the underlying filesystem. This is important,
|
||||
/// notably, because sandboxed applications have different working restrictions surrounding what
|
||||
/// they can access.
|
||||
pub trait FileManagerController {
|
||||
|
||||
}
|
||||
pub trait FileManagerController {}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::id;
|
||||
|
@ -27,17 +27,13 @@ impl NSArray {
|
|||
/// In some cases, we're vended an `NSArray` by the system that we need to call retain on.
|
||||
/// This handles that case.
|
||||
pub fn retain(array: id) -> Self {
|
||||
NSArray(unsafe {
|
||||
Id::from_ptr(array)
|
||||
})
|
||||
NSArray(unsafe { Id::from_ptr(array) })
|
||||
}
|
||||
|
||||
/// In some cases, we're vended an `NSArray` by the system, and it's ideal to not retain that.
|
||||
/// This handles that edge case.
|
||||
pub fn from_retained(array: id) -> Self {
|
||||
NSArray(unsafe {
|
||||
Id::from_retained_ptr(array)
|
||||
})
|
||||
NSArray(unsafe { Id::from_retained_ptr(array) })
|
||||
}
|
||||
|
||||
/// Returns the `count` (`len()` equivalent) for the backing `NSArray`.
|
||||
|
@ -54,10 +50,12 @@ impl NSArray {
|
|||
|
||||
// I don't know if it's worth trying to get in with NSFastEnumeration here. I'm content to
|
||||
// just rely on Rust, but someone is free to profile it if they want.
|
||||
(0..count).map(|index| {
|
||||
let item: id = unsafe { msg_send![objc, objectAtIndex:index] };
|
||||
transform(item)
|
||||
}).collect()
|
||||
(0..count)
|
||||
.map(|index| {
|
||||
let item: id = unsafe { msg_send![objc, objectAtIndex: index] };
|
||||
transform(item)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,8 +63,8 @@ impl From<Vec<&Object>> for NSArray {
|
|||
/// Given a set of `Object`s, creates an `NSArray` that holds them.
|
||||
fn from(objects: Vec<&Object>) -> Self {
|
||||
NSArray(unsafe {
|
||||
Id::from_ptr(msg_send![class!(NSArray),
|
||||
arrayWithObjects:objects.as_ptr()
|
||||
Id::from_ptr(msg_send![class!(NSArray),
|
||||
arrayWithObjects:objects.as_ptr()
|
||||
count:objects.len()
|
||||
])
|
||||
})
|
||||
|
@ -77,7 +75,7 @@ impl From<Vec<id>> for NSArray {
|
|||
/// Given a set of `*mut Object`s, creates an `NSArray` that holds them.
|
||||
fn from(objects: Vec<id>) -> Self {
|
||||
NSArray(unsafe {
|
||||
Id::from_ptr(msg_send![class!(NSArray),
|
||||
Id::from_ptr(msg_send![class!(NSArray),
|
||||
arrayWithObjects:objects.as_ptr()
|
||||
count:objects.len()
|
||||
])
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
/// A wrapper around `NSAutoReleasePool`. The core `App` structures create and manage one of these,
|
||||
|
@ -14,9 +14,7 @@ impl AutoReleasePool {
|
|||
/// Creates and returns a new `AutoReleasePool`. You need to take care to keep this alive for
|
||||
/// as long as you need it.
|
||||
pub fn new() -> Self {
|
||||
AutoReleasePool(unsafe {
|
||||
Id::from_retained_ptr(msg_send![class!(NSAutoreleasePool), new])
|
||||
})
|
||||
AutoReleasePool(unsafe { Id::from_retained_ptr(msg_send![class!(NSAutoreleasePool), new]) })
|
||||
}
|
||||
|
||||
/// Drains the underlying AutoreleasePool.
|
||||
|
|
|
@ -39,7 +39,7 @@ impl ClassMap {
|
|||
let mut map = HashMap::new();
|
||||
|
||||
// Top-level classes, like `NSView`, we cache here. The reasoning is that if a subclass
|
||||
// is being created, we can avoid querying the runtime for the superclass - i.e, many
|
||||
// is being created, we can avoid querying the runtime for the superclass - i.e, many
|
||||
// subclasses will have `NSView` as their superclass.
|
||||
map.insert("_supers", HashMap::new());
|
||||
|
||||
|
@ -48,11 +48,7 @@ impl ClassMap {
|
|||
}
|
||||
|
||||
/// Attempts to load a previously registered subclass.
|
||||
pub fn load_subclass(
|
||||
&self,
|
||||
subclass_name: &'static str,
|
||||
superclass_name: &'static str
|
||||
) -> Option<*const Class> {
|
||||
pub fn load_subclass(&self, subclass_name: &'static str, superclass_name: &'static str) -> Option<*const Class> {
|
||||
let reader = self.0.read().unwrap();
|
||||
|
||||
if let Some(inner) = (*reader).get(subclass_name) {
|
||||
|
@ -65,12 +61,7 @@ impl ClassMap {
|
|||
}
|
||||
|
||||
/// Store a newly created subclass type.
|
||||
pub fn store_subclass(
|
||||
&self,
|
||||
subclass_name: &'static str,
|
||||
superclass_name: &'static str,
|
||||
class: *const Class,
|
||||
) {
|
||||
pub fn store_subclass(&self, subclass_name: &'static str, superclass_name: &'static str, class: *const Class) {
|
||||
let mut writer = self.0.write().unwrap();
|
||||
|
||||
if let Some(map) = (*writer).get_mut(subclass_name) {
|
||||
|
@ -96,9 +87,7 @@ impl ClassMap {
|
|||
}
|
||||
|
||||
let objc_superclass_name = CString::new(name).unwrap();
|
||||
let superclass = unsafe {
|
||||
objc_getClass(objc_superclass_name.as_ptr() as *const _)
|
||||
};
|
||||
let superclass = unsafe { objc_getClass(objc_superclass_name.as_ptr() as *const _) };
|
||||
|
||||
// This should not happen, for our use-cases, but it's conceivable that this could actually
|
||||
// be expected, so just return None and let the caller panic if so desired.
|
||||
|
@ -118,24 +107,20 @@ impl ClassMap {
|
|||
}
|
||||
|
||||
/// Attempts to load a subclass, given a `superclass_name` and subclass_name. If
|
||||
/// the subclass cannot be loaded, it's dynamically created and injected into
|
||||
/// the subclass cannot be loaded, it's dynamically created and injected into
|
||||
/// the runtime, and then returned. The returned value can be used for allocating new instances of
|
||||
/// this class in the Objective-C runtime.
|
||||
///
|
||||
/// The `config` block can be used to customize the Class declaration before it's registered with
|
||||
/// the runtime. This is useful for adding method handlers and ivar storage.
|
||||
///
|
||||
/// If the superclass cannot be loaded, this will panic. If the subclass cannot be
|
||||
/// created, this will panic. In general, this is expected to work, and if it doesn't,
|
||||
/// If the superclass cannot be loaded, this will panic. If the subclass cannot be
|
||||
/// created, this will panic. In general, this is expected to work, and if it doesn't,
|
||||
/// the entire framework will not really work.
|
||||
///
|
||||
/// There's definitely room to optimize here, but it works for now.
|
||||
#[inline(always)]
|
||||
pub fn load_or_register_class<F>(
|
||||
superclass_name: &'static str,
|
||||
subclass_name: &'static str,
|
||||
config: F
|
||||
) -> *const Class
|
||||
pub fn load_or_register_class<F>(superclass_name: &'static str, subclass_name: &'static str, config: F) -> *const Class
|
||||
where
|
||||
F: Fn(&mut ClassDecl) + 'static
|
||||
{
|
||||
|
@ -155,11 +140,10 @@ where
|
|||
return class;
|
||||
},
|
||||
|
||||
None => {
|
||||
None => {
|
||||
panic!(
|
||||
"Subclass of type {}_{} could not be allocated.",
|
||||
subclass_name,
|
||||
superclass_name
|
||||
subclass_name, superclass_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -167,7 +151,6 @@ where
|
|||
|
||||
panic!(
|
||||
"Attempted to create subclass for {}, but unable to load superclass of type {}.",
|
||||
subclass_name,
|
||||
superclass_name
|
||||
subclass_name, superclass_name
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@ use std::slice;
|
|||
|
||||
use block::{Block, ConcreteBlock};
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, to_bool, BOOL, YES, NO, NSUInteger};
|
||||
use crate::foundation::{id, to_bool, NSUInteger, BOOL, NO, YES};
|
||||
|
||||
/// Wrapper for a retained `NSData` object.
|
||||
///
|
||||
|
@ -49,7 +49,7 @@ impl NSData {
|
|||
NSData(Id::from_ptr(obj))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Given a slice of bytes, creates, retains, and returns a wrapped `NSData`.
|
||||
///
|
||||
/// This method is borrowed straight out of [objc-foundation](objc-foundation) by the amazing
|
||||
|
@ -67,24 +67,18 @@ impl NSData {
|
|||
|
||||
/// Given a (presumably) `NSData`, wraps and retains it.
|
||||
pub fn retain(data: id) -> Self {
|
||||
NSData(unsafe {
|
||||
Id::from_ptr(data)
|
||||
})
|
||||
NSData(unsafe { Id::from_ptr(data) })
|
||||
}
|
||||
|
||||
/// If we're vended an NSData from a method (e.g, a push notification token) we might want to
|
||||
/// wrap it while we figure out what to do with it. This does that.
|
||||
pub fn from_retained(data: id) -> Self {
|
||||
NSData(unsafe {
|
||||
Id::from_retained_ptr(data)
|
||||
})
|
||||
NSData(unsafe { Id::from_retained_ptr(data) })
|
||||
}
|
||||
|
||||
/// A helper method for determining if a given `NSObject` is an `NSData`.
|
||||
pub fn is(obj: id) -> bool {
|
||||
let result: BOOL = unsafe {
|
||||
msg_send![obj, isKindOfClass:class!(NSData)]
|
||||
};
|
||||
let result: BOOL = unsafe { msg_send![obj, isKindOfClass: class!(NSData)] };
|
||||
|
||||
to_bool(result)
|
||||
}
|
||||
|
@ -96,7 +90,7 @@ impl NSData {
|
|||
x as usize
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns a reference to the underlying bytes for the wrapped `NSData`.
|
||||
///
|
||||
/// This, like `NSData::new()`, is cribbed from [objc-foundation](objc-foundation).
|
||||
|
@ -104,19 +98,17 @@ impl NSData {
|
|||
/// [objc-foundation](https://crates.io/crates/objc-foundation)
|
||||
pub fn bytes(&self) -> &[u8] {
|
||||
let ptr: *const c_void = unsafe { msg_send![&*self.0, bytes] };
|
||||
|
||||
|
||||
// The bytes pointer may be null for length zero
|
||||
let (ptr, len) = if ptr.is_null() {
|
||||
(0x1 as *const u8, 0)
|
||||
} else {
|
||||
(ptr as *const u8, self.len())
|
||||
};
|
||||
|
||||
unsafe {
|
||||
slice::from_raw_parts(ptr, len)
|
||||
}
|
||||
|
||||
unsafe { slice::from_raw_parts(ptr, len) }
|
||||
}
|
||||
|
||||
|
||||
/// Creates a new Vec, copies the NSData (safely, but quickly) bytes into that Vec, and
|
||||
/// consumes the NSData enabling it to release (provided nothing in Cocoa is using it).
|
||||
///
|
||||
|
@ -126,11 +118,11 @@ impl NSData {
|
|||
// often, but still... open to ideas.
|
||||
pub fn into_vec(self) -> Vec<u8> {
|
||||
let mut data = Vec::new();
|
||||
|
||||
|
||||
let bytes = self.bytes();
|
||||
data.resize(bytes.len(), 0);
|
||||
data.copy_from_slice(bytes);
|
||||
|
||||
|
||||
data
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::collections::HashMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, NSString};
|
||||
|
@ -26,9 +26,7 @@ impl NSMutableDictionary {
|
|||
/// object model. You can, of course, bypass it and `msg_send![]` yourself, but it'd require an
|
||||
/// `unsafe {}` block... so you'll know you're in special territory then.
|
||||
pub fn new() -> Self {
|
||||
NSMutableDictionary(unsafe {
|
||||
Id::from_ptr(msg_send![class!(NSMutableDictionary), new])
|
||||
})
|
||||
NSMutableDictionary(unsafe { Id::from_ptr(msg_send![class!(NSMutableDictionary), new]) })
|
||||
}
|
||||
|
||||
/// Inserts an object into the backing NSMutablyDictionary.
|
||||
|
|
|
@ -44,7 +44,7 @@ pub use string::NSString;
|
|||
|
||||
// Separate named module to not conflict with the `url` crate. Go figure.
|
||||
mod urls;
|
||||
pub use urls::{NSURL, NSURLBookmarkCreationOption, NSURLBookmarkResolutionOption};
|
||||
pub use urls::{NSURLBookmarkCreationOption, NSURLBookmarkResolutionOption, NSURL};
|
||||
|
||||
/// Bool mapping types differ between ARM and x64. There's a number of places that we need to check
|
||||
/// against BOOL results throughout the framework, and this just simplifies some mismatches.
|
||||
|
@ -53,10 +53,12 @@ pub fn to_bool(result: BOOL) -> bool {
|
|||
match result {
|
||||
YES => true,
|
||||
NO => false,
|
||||
|
||||
|
||||
//#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
_ => { std::unreachable!(); }
|
||||
_ => {
|
||||
std::unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, to_bool, BOOL, YES, NO, NSInteger};
|
||||
use crate::foundation::{id, to_bool, NSInteger, BOOL, NO, YES};
|
||||
|
||||
/// Wrapper for a `NSNumber` object.
|
||||
///
|
||||
|
@ -18,17 +18,13 @@ impl NSNumber {
|
|||
/// If we're vended an NSNumber from a method (e.g, `NSUserDefaults` querying) we might want to
|
||||
/// wrap (and retain) it while we figure out what to do with it. This does that.
|
||||
pub fn retain(data: id) -> Self {
|
||||
NSNumber(unsafe {
|
||||
Id::from_ptr(data)
|
||||
})
|
||||
NSNumber(unsafe { Id::from_ptr(data) })
|
||||
}
|
||||
|
||||
|
||||
/// If we're vended an NSNumber from a method (e.g, `NSUserDefaults` querying) we might want to
|
||||
/// wrap it while we figure out what to do with it. This does that.
|
||||
pub fn wrap(data: id) -> Self {
|
||||
NSNumber(unsafe {
|
||||
Id::from_retained_ptr(data)
|
||||
})
|
||||
NSNumber(unsafe { Id::from_retained_ptr(data) })
|
||||
}
|
||||
|
||||
/// Constructs a `numberWithBool` instance of `NSNumber` and retains it.
|
||||
|
@ -43,16 +39,12 @@ impl NSNumber {
|
|||
|
||||
/// Constructs a `numberWithInteger` instance of `NSNumber` and retains it.
|
||||
pub fn integer(value: i64) -> Self {
|
||||
NSNumber(unsafe {
|
||||
Id::from_retained_ptr(msg_send![class!(NSNumber), numberWithInteger:value as NSInteger])
|
||||
})
|
||||
NSNumber(unsafe { Id::from_retained_ptr(msg_send![class!(NSNumber), numberWithInteger: value as NSInteger]) })
|
||||
}
|
||||
|
||||
/// Constructs a `numberWithDouble` instance of `NSNumber` and retains it.
|
||||
pub fn float(value: f64) -> Self {
|
||||
NSNumber(unsafe {
|
||||
Id::from_retained_ptr(msg_send![class!(NSNumber), numberWithDouble:value])
|
||||
})
|
||||
NSNumber(unsafe { Id::from_retained_ptr(msg_send![class!(NSNumber), numberWithDouble: value]) })
|
||||
}
|
||||
|
||||
/// Returns the `objCType` of the underlying `NSNumber` as a Rust `&str`. This flag can be used
|
||||
|
@ -84,9 +76,7 @@ impl NSNumber {
|
|||
/// Note that this _does not check_ if the underlying type is actually this. You are
|
||||
/// responsible for doing so via the `objc_type()` method.
|
||||
pub fn as_f64(&self) -> f64 {
|
||||
unsafe {
|
||||
msg_send![&*self.0, doubleValue]
|
||||
}
|
||||
unsafe { msg_send![&*self.0, doubleValue] }
|
||||
}
|
||||
|
||||
/// Pulls the underlying `BOOL` value out and passes it back as a `bool`.
|
||||
|
@ -94,18 +84,14 @@ impl NSNumber {
|
|||
/// Note that this _does not check_ if the underlying type is actually this. You are
|
||||
/// responsible for doing so via the `objc_type()` method.
|
||||
pub fn as_bool(&self) -> bool {
|
||||
let result: BOOL = unsafe {
|
||||
msg_send![&*self.0, boolValue]
|
||||
};
|
||||
let result: BOOL = unsafe { msg_send![&*self.0, boolValue] };
|
||||
|
||||
to_bool(result)
|
||||
}
|
||||
|
||||
/// A helper method for determining if a given `NSObject` is an `NSNumber`.
|
||||
pub fn is(obj: id) -> bool {
|
||||
let result: BOOL = unsafe {
|
||||
msg_send![obj, isKindOfClass:class!(NSNumber)]
|
||||
};
|
||||
let result: BOOL = unsafe { msg_send![obj, isKindOfClass: class!(NSNumber)] };
|
||||
|
||||
to_bool(result)
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
use std::{fmt, slice, str};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_char;
|
||||
use std::{fmt, slice, str};
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, to_bool, BOOL, YES, NO};
|
||||
use crate::foundation::{id, to_bool, BOOL, NO, YES};
|
||||
|
||||
const UTF8_ENCODING: usize = 4;
|
||||
|
||||
/// A wrapper for `NSString`.
|
||||
///
|
||||
/// We can make a few safety guarantees in this module as the UTF8 code on the Foundation
|
||||
/// We can make a few safety guarantees in this module as the UTF8 code on the Foundation
|
||||
/// side is fairly battle tested.
|
||||
#[derive(Debug)]
|
||||
pub struct NSString<'a> {
|
||||
|
@ -37,7 +37,7 @@ impl<'a> NSString<'a> {
|
|||
phantom: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Creates a new `NSString` without copying the bytes for the passed-in string.
|
||||
pub fn no_copy(s: &'a str) -> Self {
|
||||
NSString {
|
||||
|
@ -73,7 +73,7 @@ impl<'a> NSString<'a> {
|
|||
|
||||
/// Utility method for checking whether an `NSObject` is an `NSString`.
|
||||
pub fn is(obj: id) -> bool {
|
||||
let result: BOOL = unsafe { msg_send![obj, isKindOfClass:class!(NSString)] };
|
||||
let result: BOOL = unsafe { msg_send![obj, isKindOfClass: class!(NSString)] };
|
||||
to_bool(result)
|
||||
}
|
||||
|
||||
|
@ -87,16 +87,14 @@ impl<'a> NSString<'a> {
|
|||
|
||||
/// Helper method for grabbing the proper byte length for this `NSString` (the UTF8 variant).
|
||||
fn bytes_len(&self) -> usize {
|
||||
unsafe {
|
||||
msg_send![&*self.objc, lengthOfBytesUsingEncoding:UTF8_ENCODING]
|
||||
}
|
||||
unsafe { msg_send![&*self.objc, lengthOfBytesUsingEncoding: UTF8_ENCODING] }
|
||||
}
|
||||
|
||||
/// A utility method for taking an `NSString` and bridging it to a Rust `&str`.
|
||||
pub fn to_str(&self) -> &str {
|
||||
let bytes = self.bytes();
|
||||
let len = self.bytes_len();
|
||||
|
||||
|
||||
unsafe {
|
||||
let bytes = slice::from_raw_parts(bytes, len);
|
||||
str::from_utf8(bytes).unwrap()
|
||||
|
|
|
@ -9,12 +9,12 @@ pub enum NSURLBookmarkCreationOption {
|
|||
/// Specifies that the bookmark data should include properties required to create Finder alias files.
|
||||
SuitableForBookmarkFile,
|
||||
|
||||
/// Specifies that you want to create a security-scoped bookmark that, when resolved, provides a
|
||||
/// Specifies that you want to create a security-scoped bookmark that, when resolved, provides a
|
||||
/// security-scoped URL allowing read/write access to a file-system resource.
|
||||
SecurityScoped,
|
||||
|
||||
/// When combined with the NSURLBookmarkCreationOptions::SecurityScoped option, specifies that you
|
||||
/// want to create a security-scoped bookmark that, when resolved, provides a security-scoped URL allowing
|
||||
/// When combined with the NSURLBookmarkCreationOptions::SecurityScoped option, specifies that you
|
||||
/// want to create a security-scoped bookmark that, when resolved, provides a security-scoped URL allowing
|
||||
/// read-only access to a file-system resource.
|
||||
SecurityScopedReadOnly
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ impl From<NSURLBookmarkCreationOption> for NSUInteger {
|
|||
fn from(flag: NSURLBookmarkCreationOption) -> NSUInteger {
|
||||
match flag {
|
||||
NSURLBookmarkCreationOption::Minimal => 1u64 << 9,
|
||||
NSURLBookmarkCreationOption::SuitableForBookmarkFile => 1u64 << 10,
|
||||
NSURLBookmarkCreationOption::SuitableForBookmarkFile => 1u64 << 10,
|
||||
NSURLBookmarkCreationOption::SecurityScoped => 1 << 11,
|
||||
NSURLBookmarkCreationOption::SecurityScopedReadOnly => 1 << 12
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ impl From<&NSURLBookmarkCreationOption> for NSUInteger {
|
|||
fn from(flag: &NSURLBookmarkCreationOption) -> NSUInteger {
|
||||
match flag {
|
||||
NSURLBookmarkCreationOption::Minimal => 1u64 << 9,
|
||||
NSURLBookmarkCreationOption::SuitableForBookmarkFile => 1u64 << 10,
|
||||
NSURLBookmarkCreationOption::SuitableForBookmarkFile => 1u64 << 10,
|
||||
NSURLBookmarkCreationOption::SecurityScoped => 1 << 11,
|
||||
NSURLBookmarkCreationOption::SecurityScopedReadOnly => 1 << 12
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ pub enum NSURLBookmarkResolutionOption {
|
|||
/// Specifies that no volume should be mounted during resolution of the bookmark data.
|
||||
WithoutMounting,
|
||||
|
||||
/// Specifies that the security scope, applied to the bookmark when it was created, should
|
||||
/// Specifies that the security scope, applied to the bookmark when it was created, should
|
||||
/// be used during resolution of the bookmark data.
|
||||
SecurityScoped
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ use std::marker::PhantomData;
|
|||
use std::ops::{Deref, DerefMut};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc::runtime::Object;
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::foundation::{id, nil, NSData, NSString, NSUInteger};
|
||||
|
@ -13,11 +13,11 @@ mod bookmark_options;
|
|||
pub use bookmark_options::{NSURLBookmarkCreationOption, NSURLBookmarkResolutionOption};
|
||||
|
||||
mod resource_keys;
|
||||
pub use resource_keys::{NSURLResourceKey, NSURLFileResource, NSUbiquitousItemDownloadingStatus};
|
||||
pub use resource_keys::{NSURLFileResource, NSURLResourceKey, NSUbiquitousItemDownloadingStatus};
|
||||
|
||||
/// Wraps `NSURL` for use throughout the framework.
|
||||
///
|
||||
/// This type may also be returned to users in some callbacks (e.g, file manager/selectors) as it's
|
||||
/// This type may also be returned to users in some callbacks (e.g, file manager/selectors) as it's
|
||||
/// a core part of the macOS/iOS experience and bridging around it is arguably blocking people from
|
||||
/// being able to actually build useful things.
|
||||
///
|
||||
|
@ -52,28 +52,24 @@ impl<'a> NSURL<'a> {
|
|||
phantom: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Creates and returns a URL object by calling through to `[NSURL URLWithString]`.
|
||||
pub fn with_str(url: &str) -> Self {
|
||||
let url = NSString::new(url);
|
||||
|
||||
|
||||
Self {
|
||||
objc: unsafe {
|
||||
ShareId::from_ptr(msg_send![class!(NSURL), URLWithString:&*url])
|
||||
},
|
||||
objc: unsafe { ShareId::from_ptr(msg_send![class!(NSURL), URLWithString:&*url]) },
|
||||
|
||||
phantom: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns the absolute string path that this URL points to.
|
||||
///
|
||||
/// Note that if the underlying file moved, this won't be accurate - you likely want to
|
||||
/// research URL bookmarks.
|
||||
pub fn absolute_string(&self) -> String {
|
||||
let abs_str = NSString::retain(unsafe {
|
||||
msg_send![&*self.objc, absoluteString]
|
||||
});
|
||||
let abs_str = NSString::retain(unsafe { msg_send![&*self.objc, absoluteString] });
|
||||
|
||||
abs_str.to_string()
|
||||
}
|
||||
|
@ -81,9 +77,7 @@ impl<'a> NSURL<'a> {
|
|||
/// Creates and returns a Rust `PathBuf`, for users who don't need the extra pieces of NSURL
|
||||
/// and just want to write Rust code.
|
||||
pub fn pathbuf(&self) -> PathBuf {
|
||||
let path = NSString::retain(unsafe {
|
||||
msg_send![&*self.objc, path]
|
||||
});
|
||||
let path = NSString::retain(unsafe { msg_send![&*self.objc, path] });
|
||||
|
||||
path.to_str().into()
|
||||
}
|
||||
|
@ -145,7 +139,7 @@ impl<'a> NSURL<'a> {
|
|||
|
||||
/// In an app that has adopted App Sandbox, makes the resource pointed to by a security-scoped URL available to the app.
|
||||
///
|
||||
/// More information can be found at:
|
||||
/// More information can be found at:
|
||||
/// [https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso?language=objc]
|
||||
pub fn start_accessing_security_scoped_resource(&self) {
|
||||
unsafe {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Wrapper methods for various geometry types (rects, sizes, ec).
|
||||
|
||||
use core_graphics::geometry::{CGRect, CGPoint, CGSize};
|
||||
use core_graphics::geometry::{CGPoint, CGRect, CGSize};
|
||||
|
||||
/// A struct that represents a box - top, left, width and height. You might use this for, say,
|
||||
/// setting the initial frame of a view.
|
||||
|
@ -8,7 +8,7 @@ use core_graphics::geometry::{CGRect, CGPoint, CGSize};
|
|||
pub struct Rect {
|
||||
/// Distance from the top, in points.
|
||||
pub top: f64,
|
||||
|
||||
|
||||
/// Distance from the left, in points.
|
||||
pub left: f64,
|
||||
|
||||
|
@ -22,7 +22,12 @@ pub struct Rect {
|
|||
impl Rect {
|
||||
/// Returns a new `Rect` initialized with the values specified.
|
||||
pub fn new(top: f64, left: f64, width: f64, height: f64) -> Self {
|
||||
Rect { top: top, left: left, width: width, height: height }
|
||||
Rect {
|
||||
top: top,
|
||||
left: left,
|
||||
width: width,
|
||||
height: height
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a zero'd out Rect, with f64 (32-bit is mostly dead on Cocoa, so... this is "okay").
|
||||
|
@ -38,10 +43,7 @@ impl Rect {
|
|||
|
||||
impl From<Rect> for CGRect {
|
||||
fn from(rect: Rect) -> CGRect {
|
||||
CGRect::new(
|
||||
&CGPoint::new(rect.left, rect.top),
|
||||
&CGSize::new(rect.width, rect.height)
|
||||
)
|
||||
CGRect::new(&CGPoint::new(rect.left, rect.top), &CGSize::new(rect.width, rect.height))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@ use objc::runtime::{Class, Object, Sel, BOOL};
|
|||
use objc::{class, sel, sel_impl};
|
||||
use objc_id::Id;
|
||||
|
||||
use crate::foundation::{id, YES, NO, NSUInteger};
|
||||
use crate::dragdrop::DragInfo;
|
||||
use crate::view::{VIEW_DELEGATE_PTR, ViewDelegate};
|
||||
use crate::foundation::{id, NSUInteger, NO, YES};
|
||||
use crate::utils::load;
|
||||
use crate::view::{ViewDelegate, VIEW_DELEGATE_PTR};
|
||||
|
||||
/// Injects an `NSView` subclass. This is used for the default views that don't use delegates - we
|
||||
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
||||
|
@ -30,8 +30,8 @@ pub(crate) fn register_image_view_class() -> *const Class {
|
|||
let superclass = class!(NSImageView);
|
||||
let decl = ClassDecl::new("RSTImageView", superclass).unwrap();
|
||||
|
||||
//decl.add_method(sel!(isFlipped), enforce_normalcy as extern fn(&Object, _) -> BOOL);
|
||||
|
||||
//decl.add_method(sel!(isFlipped), enforce_normalcy as extern "C" fn(&Object, _) -> BOOL);
|
||||
|
||||
VIEW_CLASS = decl.register();
|
||||
});
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use objc_id::ShareId;
|
||||
use objc::runtime::Object;
|
||||
|
||||
/// Views get passed these, and can
|
||||
/// Views get passed these, and can
|
||||
#[derive(Debug)]
|
||||
pub struct ViewHandle<T> {
|
||||
/// A pointer to the Objective-C runtime view controller.
|
||||
|
@ -37,7 +37,7 @@ pub struct ViewHandle<T> {
|
|||
|
||||
impl<T> TextControl for ViewHandle<T>
|
||||
where
|
||||
T:
|
||||
T:
|
||||
|
||||
impl<T> Layout for ViewHandle<T> {
|
||||
fn get_backing_node(&self) -> ShareId<Object> {
|
||||
|
|
|
@ -60,14 +60,14 @@ impl MacSystemIcon {
|
|||
|
||||
/// Maps system icons to their SFSymbols-counterparts for use on 11.0+.
|
||||
pub fn to_sfsymbol_str(&self) -> &'static str {
|
||||
match self {
|
||||
match self {
|
||||
MacSystemIcon::PreferencesGeneral => SFSymbol::GearShape.to_str(),
|
||||
MacSystemIcon::PreferencesAdvanced => SFSymbol::SliderVertical3.to_str(),
|
||||
MacSystemIcon::PreferencesUserAccounts => SFSymbol::AtSymbol.to_str(),
|
||||
MacSystemIcon::Add => SFSymbol::Plus.to_str(),
|
||||
MacSystemIcon::Remove => SFSymbol::Minus.to_str(),
|
||||
MacSystemIcon::Folder => SFSymbol::FolderFilled.to_str()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
use objc_id::ShareId;
|
||||
use objc::runtime::Object;
|
||||
use objc_id::ShareId;
|
||||
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
use block::ConcreteBlock;
|
||||
|
||||
use core_graphics::{
|
||||
base::{CGFloat},
|
||||
geometry::{CGRect, CGPoint, CGSize}
|
||||
};
|
||||
use core_graphics::context::{CGContext, CGContextRef};
|
||||
use core_graphics::{
|
||||
base::CGFloat,
|
||||
geometry::{CGPoint, CGRect, CGSize}
|
||||
};
|
||||
|
||||
use crate::foundation::{id, YES, NO, NSString, NSData};
|
||||
use crate::utils::os;
|
||||
use super::icons::*;
|
||||
use crate::foundation::{id, NSData, NSString, NO, YES};
|
||||
use crate::utils::os;
|
||||
|
||||
/// Specifies resizing behavior for image drawing.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -32,7 +32,9 @@ pub enum ResizeBehavior {
|
|||
}
|
||||
|
||||
fn max_cgfloat(x: CGFloat, y: CGFloat) -> CGFloat {
|
||||
if x == y { return x; }
|
||||
if x == y {
|
||||
return x;
|
||||
}
|
||||
|
||||
match x > y {
|
||||
true => x,
|
||||
|
@ -41,7 +43,9 @@ fn max_cgfloat(x: CGFloat, y: CGFloat) -> CGFloat {
|
|||
}
|
||||
|
||||
fn min_cgfloat(x: CGFloat, y: CGFloat) -> CGFloat {
|
||||
if x == y { return x; }
|
||||
if x == y {
|
||||
return x;
|
||||
}
|
||||
|
||||
match x < y {
|
||||
true => x,
|
||||
|
@ -54,21 +58,15 @@ impl ResizeBehavior {
|
|||
/// the resizing properties of this enum.
|
||||
pub fn apply(&self, source: CGRect, target: CGRect) -> CGRect {
|
||||
// if equal, just return source
|
||||
if
|
||||
source.origin.x == target.origin.x &&
|
||||
source.origin.y == target.origin.y &&
|
||||
source.size.width == target.size.width &&
|
||||
source.size.height == target.size.height
|
||||
if source.origin.x == target.origin.x
|
||||
&& source.origin.y == target.origin.y
|
||||
&& source.size.width == target.size.width
|
||||
&& source.size.height == target.size.height
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
if
|
||||
source.origin.x == 0. &&
|
||||
source.origin.y == 0. &&
|
||||
source.size.width == 0. &&
|
||||
source.size.height == 0.
|
||||
{
|
||||
if source.origin.x == 0. && source.origin.y == 0. && source.size.width == 0. && source.size.height == 0. {
|
||||
return source;
|
||||
}
|
||||
|
||||
|
@ -126,9 +124,7 @@ pub struct Image(pub ShareId<Object>);
|
|||
impl Image {
|
||||
/// Wraps a system-returned image, e.g from QuickLook previews.
|
||||
pub fn with(image: id) -> Self {
|
||||
Image(unsafe {
|
||||
ShareId::from_ptr(image)
|
||||
})
|
||||
Image(unsafe { ShareId::from_ptr(image) })
|
||||
}
|
||||
|
||||
/// Loads an image from the specified path.
|
||||
|
@ -137,7 +133,7 @@ impl Image {
|
|||
|
||||
Image(unsafe {
|
||||
let alloc: id = msg_send![class!(NSImage), alloc];
|
||||
ShareId::from_ptr(msg_send![alloc, initWithContentsOfFile:file_path])
|
||||
ShareId::from_ptr(msg_send![alloc, initWithContentsOfFile: file_path])
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -148,7 +144,7 @@ impl Image {
|
|||
|
||||
Image(unsafe {
|
||||
let alloc: id = msg_send![class!(NSImage), alloc];
|
||||
ShareId::from_ptr(msg_send![alloc, initWithData:data])
|
||||
ShareId::from_ptr(msg_send![alloc, initWithData: data])
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -161,7 +157,7 @@ impl Image {
|
|||
Image(unsafe {
|
||||
ShareId::from_ptr({
|
||||
let icon = icon.to_id();
|
||||
msg_send![class!(NSImage), imageNamed:icon]
|
||||
msg_send![class!(NSImage), imageNamed: icon]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -189,13 +185,13 @@ impl Image {
|
|||
|
||||
false => {
|
||||
let icon = icon.to_id();
|
||||
msg_send![class!(NSImage), imageNamed:icon]
|
||||
msg_send![class!(NSImage), imageNamed: icon]
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates and returns an Image with the specified `SFSymbol`. Note that `SFSymbol` is
|
||||
/// Creates and returns an Image with the specified `SFSymbol`. Note that `SFSymbol` is
|
||||
/// supported on 11.0+; as such, this will panic if called on a lower system. Take care to
|
||||
/// provide a fallback image or user experience if you need to support an older OS.
|
||||
pub fn symbol(symbol: SFSymbol, accessibility_description: &str) -> Self {
|
||||
|
@ -221,15 +217,9 @@ impl Image {
|
|||
where
|
||||
F: Fn(CGRect, &CGContextRef) -> bool + 'static
|
||||
{
|
||||
let source_frame = CGRect::new(
|
||||
&CGPoint::new(0., 0.),
|
||||
&CGSize::new(config.source.0, config.source.1)
|
||||
);
|
||||
let source_frame = CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(config.source.0, config.source.1));
|
||||
|
||||
let target_frame = CGRect::new(
|
||||
&CGPoint::new(0., 0.),
|
||||
&CGSize::new(config.target.0, config.target.1)
|
||||
);
|
||||
let target_frame = CGRect::new(&CGPoint::new(0., 0.), &CGSize::new(config.target.0, config.target.1));
|
||||
|
||||
let resized_frame = config.resize.apply(source_frame, target_frame);
|
||||
|
||||
|
@ -246,7 +236,7 @@ impl Image {
|
|||
);
|
||||
|
||||
let result = handler(resized_frame, &context);
|
||||
|
||||
|
||||
let _: () = msg_send![class!(NSGraphicsContext), restoreGraphicsState];
|
||||
|
||||
match result {
|
||||
|
@ -257,8 +247,8 @@ impl Image {
|
|||
let block = block.copy();
|
||||
|
||||
Image(unsafe {
|
||||
let img: id = msg_send![class!(NSImage), imageWithSize:target_frame.size
|
||||
flipped:YES
|
||||
let img: id = msg_send![class!(NSImage), imageWithSize:target_frame.size
|
||||
flipped:YES
|
||||
drawingHandler:block
|
||||
];
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
use objc_id::ShareId;
|
||||
use objc::runtime::{Class, Object};
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use objc_id::ShareId;
|
||||
|
||||
use crate::foundation::{id, nil, YES, NO, NSArray, NSString};
|
||||
use crate::color::Color;
|
||||
use crate::foundation::{id, nil, NSArray, NSString, NO, YES};
|
||||
use crate::layout::Layout;
|
||||
use crate::objc_access::ObjcAccess;
|
||||
use crate::utils::properties::ObjcProperty;
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
use crate::layout::{LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::layout::{LayoutAnchorDimension, LayoutAnchorX, LayoutAnchorY};
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
mod appkit;
|
||||
|
@ -24,23 +24,23 @@ use appkit::register_image_view_class;
|
|||
//use uikit::register_image_view_class;
|
||||
|
||||
mod image;
|
||||
pub use image::{Image, DrawConfig, ResizeBehavior};
|
||||
pub use image::{DrawConfig, Image, ResizeBehavior};
|
||||
|
||||
mod icons;
|
||||
pub use icons::*;
|
||||
|
||||
/// A helper method for instantiating view classes and applying default settings to them.
|
||||
fn allocate_view(registration_fn: fn() -> *const Class) -> id {
|
||||
fn allocate_view(registration_fn: fn() -> *const Class) -> id {
|
||||
unsafe {
|
||||
let view: id = msg_send![registration_fn(), new];
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints: NO];
|
||||
|
||||
#[cfg(feature = "appkit")]
|
||||
let _: () = msg_send![view, setWantsLayer:YES];
|
||||
let _: () = msg_send![view, setWantsLayer: YES];
|
||||
|
||||
view
|
||||
view
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ fn allocate_view(registration_fn: fn() -> *const Class) -> id {
|
|||
pub struct ImageView {
|
||||
/// A pointer to the Objective-C runtime view controller.
|
||||
pub objc: ObjcProperty,
|
||||
|
||||
|
||||
/// A pointer to the Objective-C runtime top layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub top: LayoutAnchorY,
|
||||
|
@ -100,42 +100,42 @@ impl Default for ImageView {
|
|||
}
|
||||
|
||||
impl ImageView {
|
||||
/// Returns a default `View`, suitable for
|
||||
/// Returns a default `View`, suitable for
|
||||
pub fn new() -> Self {
|
||||
let view = allocate_view(register_image_view_class);
|
||||
|
||||
ImageView {
|
||||
#[cfg(feature = "autolayout")]
|
||||
top: LayoutAnchorY::top(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
left: LayoutAnchorX::left(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
leading: LayoutAnchorX::leading(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
right: LayoutAnchorX::right(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
trailing: LayoutAnchorX::trailing(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
bottom: LayoutAnchorY::bottom(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
width: LayoutAnchorDimension::width(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
height: LayoutAnchorDimension::height(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_y: LayoutAnchorY::center(view),
|
||||
|
||||
objc: ObjcProperty::retain(view),
|
||||
|
||||
objc: ObjcProperty::retain(view)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,7 +144,7 @@ impl ImageView {
|
|||
self.objc.with_mut(|obj| unsafe {
|
||||
let cg = color.as_ref().cg_color();
|
||||
let layer: id = msg_send![obj, layer];
|
||||
let _: () = msg_send![layer, setBackgroundColor:cg];
|
||||
let _: () = msg_send![layer, setBackgroundColor: cg];
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ pub trait ViewDelegate {
|
|||
|
||||
/// Invoked when the dragged image enters destination bounds or frame; returns dragging operation to perform.
|
||||
fn dragging_entered(&self, _info: DragInfo) -> DragOperation { DragOperation::None }
|
||||
|
||||
|
||||
/// Invoked when the image is released, allowing the receiver to agree to or refuse drag operation.
|
||||
fn prepare_for_drag_operation(&self, _info: DragInfo) -> bool { false }
|
||||
|
||||
|
@ -33,7 +33,7 @@ pub trait ViewDelegate {
|
|||
/// Invoked when the dragging operation is complete, signaling the receiver to perform any necessary clean-up.
|
||||
fn conclude_drag_operation(&self, _info: DragInfo) {}
|
||||
|
||||
/// Invoked when the dragged image exits the destination’s bounds rectangle (in the case of a view) or its frame
|
||||
/// Invoked when the dragged image exits the destination’s bounds rectangle (in the case of a view) or its frame
|
||||
/// rectangle (in the case of a window object).
|
||||
fn dragging_exited(&self, _info: DragInfo) {}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use objc::{class, msg_send, sel, sel_impl};
|
|||
use objc_id::Id;
|
||||
|
||||
use crate::dragdrop::DragInfo;
|
||||
use crate::foundation::{load_or_register_class, id, NSString, NSUInteger, NO, YES};
|
||||
use crate::foundation::{id, load_or_register_class, NSString, NSUInteger, NO, YES};
|
||||
use crate::input::{TextFieldDelegate, TEXTFIELD_DELEGATE_PTR};
|
||||
use crate::utils::load;
|
||||
|
||||
|
@ -29,25 +29,17 @@ extern "C" fn text_did_change<T: TextFieldDelegate>(this: &mut Object, _: Sel, _
|
|||
view.text_did_change(s.to_str());
|
||||
}
|
||||
|
||||
extern "C" fn text_should_begin_editing<T: TextFieldDelegate>(
|
||||
this: &mut Object,
|
||||
_: Sel,
|
||||
_info: id,
|
||||
) -> BOOL {
|
||||
extern "C" fn text_should_begin_editing<T: TextFieldDelegate>(this: &mut Object, _: Sel, _info: id) -> BOOL {
|
||||
let view = load::<T>(this, TEXTFIELD_DELEGATE_PTR);
|
||||
let s = NSString::retain(unsafe { msg_send![this, stringValue] });
|
||||
|
||||
|
||||
match view.text_should_begin_editing(s.to_str()) {
|
||||
true => YES,
|
||||
false => NO
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn text_should_end_editing<T: TextFieldDelegate>(
|
||||
this: &mut Object,
|
||||
_: Sel,
|
||||
_info: id,
|
||||
) -> BOOL {
|
||||
extern "C" fn text_should_end_editing<T: TextFieldDelegate>(this: &mut Object, _: Sel, _info: id) -> BOOL {
|
||||
let view = load::<T>(this, TEXTFIELD_DELEGATE_PTR);
|
||||
let s = NSString::retain(unsafe { msg_send![this, stringValue] });
|
||||
match view.text_should_end_editing(s.to_str()) {
|
||||
|
@ -82,23 +74,20 @@ pub(crate) fn register_view_class_with_delegate<T: TextFieldDelegate>(instance:
|
|||
|
||||
decl.add_method(
|
||||
sel!(textDidEndEditing:),
|
||||
text_did_end_editing::<T> as extern "C" fn(&mut Object, _, _),
|
||||
text_did_end_editing::<T> as extern "C" fn(&mut Object, _, _)
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(textDidBeginEditing:),
|
||||
text_did_begin_editing::<T> as extern "C" fn(&mut Object, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(textDidChange:),
|
||||
text_did_change::<T> as extern "C" fn(&mut Object, _, _),
|
||||
text_did_begin_editing::<T> as extern "C" fn(&mut Object, _, _)
|
||||
);
|
||||
decl.add_method(sel!(textDidChange:), text_did_change::<T> as extern "C" fn(&mut Object, _, _));
|
||||
decl.add_method(
|
||||
sel!(textShouldBeginEditing:),
|
||||
text_should_begin_editing::<T> as extern "C" fn(&mut Object, Sel, id) -> BOOL,
|
||||
text_should_begin_editing::<T> as extern "C" fn(&mut Object, Sel, id) -> BOOL
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(textShouldEndEditing:),
|
||||
text_should_end_editing::<T> as extern "C" fn(&mut Object, Sel, id) -> BOOL,
|
||||
text_should_end_editing::<T> as extern "C" fn(&mut Object, Sel, id) -> BOOL
|
||||
);
|
||||
})
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
//! self.label.set_background_color(rgb(224, 82, 99));
|
||||
//! self.label.set_text("LOL");
|
||||
//! self.content.add_subview(&self.red);
|
||||
//!
|
||||
//!
|
||||
//! self.window.set_content_view(&self.content);
|
||||
//!
|
||||
//! LayoutConstraint::activate(&[
|
||||
|
@ -96,7 +96,7 @@ pub struct TextField<T = ()> {
|
|||
|
||||
/// A pointer to the delegate for this view.
|
||||
pub delegate: Option<Box<T>>,
|
||||
|
||||
|
||||
/// A pointer to the Objective-C runtime top layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub top: LayoutAnchorY,
|
||||
|
@ -135,7 +135,7 @@ pub struct TextField<T = ()> {
|
|||
|
||||
/// A pointer to the Objective-C runtime center Y layout constraint.
|
||||
#[cfg(feature = "autolayout")]
|
||||
pub center_y: LayoutAnchorY,
|
||||
pub center_y: LayoutAnchorY
|
||||
}
|
||||
|
||||
impl Default for TextField {
|
||||
|
@ -153,43 +153,43 @@ impl TextField {
|
|||
TextField {
|
||||
delegate: None,
|
||||
objc: ObjcProperty::retain(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
top: LayoutAnchorY::top(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
left: LayoutAnchorX::left(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
leading: LayoutAnchorX::leading(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
right: LayoutAnchorX::right(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
trailing: LayoutAnchorX::trailing(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
bottom: LayoutAnchorY::bottom(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
width: LayoutAnchorDimension::width(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
height: LayoutAnchorDimension::height(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_x: LayoutAnchorX::center(view),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_y: LayoutAnchorY::center(view)
|
||||
center_y: LayoutAnchorY::center(view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TextField<T>
|
||||
where
|
||||
T: TextFieldDelegate + 'static,
|
||||
T: TextFieldDelegate + 'static
|
||||
{
|
||||
/// Initializes a new TextField with a given `TextFieldDelegate`. This enables you to respond to events
|
||||
/// and customize the view as a module, similar to class-based systems.
|
||||
|
@ -206,36 +206,36 @@ where
|
|||
let mut label = TextField {
|
||||
delegate: None,
|
||||
objc: ObjcProperty::retain(label),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
top: LayoutAnchorY::top(label),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
left: LayoutAnchorX::left(label),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
leading: LayoutAnchorX::leading(label),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
right: LayoutAnchorX::right(label),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
trailing: LayoutAnchorX::trailing(label),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
bottom: LayoutAnchorY::bottom(label),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
width: LayoutAnchorDimension::width(label),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
height: LayoutAnchorDimension::height(label),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_x: LayoutAnchorX::center(label),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_y: LayoutAnchorY::center(label),
|
||||
center_y: LayoutAnchorY::center(label)
|
||||
};
|
||||
|
||||
(&mut delegate).did_load(label.clone_as_handle());
|
||||
|
@ -253,44 +253,43 @@ impl<T> TextField<T> {
|
|||
TextField {
|
||||
delegate: None,
|
||||
objc: self.objc.clone(),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
top: self.top.clone(),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
leading: self.leading.clone(),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
left: self.left.clone(),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
trailing: self.trailing.clone(),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
right: self.right.clone(),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
bottom: self.bottom.clone(),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
width: self.width.clone(),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
height: self.height.clone(),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_x: self.center_x.clone(),
|
||||
|
||||
|
||||
#[cfg(feature = "autolayout")]
|
||||
center_y: self.center_y.clone(),
|
||||
center_y: self.center_y.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// Grabs the value from the textfield and returns it as an owned String.
|
||||
pub fn get_value(&self) -> String {
|
||||
self.objc.get(|obj| unsafe {
|
||||
NSString::retain(msg_send![obj, stringValue]).to_string()
|
||||
})
|
||||
self.objc
|
||||
.get(|obj| unsafe { NSString::retain(msg_send![obj, stringValue]).to_string() })
|
||||
}
|
||||
|
||||
/// Call this to set the background color for the backing layer.
|
||||
|
@ -353,7 +352,7 @@ impl<T> TextField<T> {
|
|||
/// Sets the maximum number of lines.
|
||||
pub fn set_max_number_of_lines(&self, num: NSInteger) {
|
||||
self.objc.with_mut(|obj| unsafe {
|
||||
let _: () = msg_send![obj, setMaximumNumberOfLines:num];
|
||||
let _: () = msg_send![obj, setMaximumNumberOfLines: num];
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue