-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AppKitNavigation - Navigation #213
base: main
Are you sure you want to change the base?
AppKitNavigation - Navigation #213
Conversation
@Mx-Iris The other branch was merged so we should be able to review this one as soon as the conflicts are resolved. Can you ping us when it's ready? Hopefully it's just a matter of cherry-picking your last commit back over |
@stephencelis The conflict has been resolved and is ready for review |
struct AssociatedKeys { | ||
var keys: [AnyHashableMetatype: UnsafeMutableRawPointer] = [:] | ||
|
||
mutating func key<T>(of type: T.Type) -> UnsafeMutableRawPointer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I can tell this is a helper that sets up an associated object. Is it doing anything else? If not, I'm inclined to be consistent with the pattern used in UIKitNavigation, where we define dedicated local keys where needed instead of leveraging a dynamic helper. How's that sound?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again because there is more than one type of object that can navigate, this is mainly used for Observer generic types like Sheet, the classes that can execute it are NSWindow, NSSavePanel, NSAlert, NSPrintPanel.... They are not necessarily of the same type, and each class has a different way of executing a sheet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because associated keys are local to each object, does the type need to be encoded into the operation? Wouldn't it work the same to use the same static key instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private func sheetObserver<Content: SheetContent>() -> SheetObserver<Self, Content> {
if let observer = objc_getAssociatedObject(self, sheetObserverKeys.key(of: Content.self)) as? SheetObserver<Self, Content> {
return observer
} else {
let observer = SheetObserver<Self, Content>(owner: self)
objc_setAssociatedObject(self, sheetObserverKeys.key(of: Content.self), observer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return observer
}
}
Because of the one-to-many relationship, a class that executes a sheet may have more than one observer (this class is mainly used to store the object that has been executed sheet), the object that is executed by the sheet is indeterminate, it may be NSAlert or NSWindow. Currently, storing the object that is executed by the sheet is mainly to perform cleanup work, if you have a If you have a better idea, let me know! 😄
import AppKit | ||
|
||
@MainActor | ||
public protocol SheetContent: NavigationContent { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can everything in this file be internal? They all seem to be helpers for driving navigation, but probably shouldn't be public APIs that folks encounter when they import AppKitNavigation.
|
||
@MainActor | ||
public protocol ModalSessionContent: ModalContent { | ||
func appKitNavigationBeginModalSession() -> NSApplication.ModalSession |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think it's possible to avoid these protocols? In UIKitNavigation we got by using the classes themselves, but maybe I'm missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The controls that can perform ModalSession are not only NSWindow, but also NSAlert, NSSavePanel, etc. The same applies to other Sheet protocols.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try to set aside some time later this week to explore the APIs and get a better understanding of how they all tie together then. Thanks for your patience!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few comments about public protocols that might be a little excessive for the public API, if they greatly simplify things internally I think there might be an option to hide them using @_spi
🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maximkrouk I tried it and found that it doesn't work. If you use this protocol for spi, all methods that use this protocol need to be spi-compatible.
import Foundation | ||
|
||
@MainActor | ||
public protocol NavigationContent: AnyObject { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly are these protocols needed, and if so do they need to be public? It'd be good to avoid adding public APIs that aren't intended for use outside the library when folks import AppKitNavigation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This protocol abstracts a lot of navigation methods. UIKit's navigation is relatively homogeneous, but AppKit's navigation is messy and complex, and if you don't pull it out there will be a lot of duplicate code.
@Mx-Iris Thanks for continuing to contribute 😄 I did a quick initial review and commented on things that stood out to me. I think the main thing we'd like to aim for is consistency with other parts of the library, e.g. UIKitNavigation. In particular:
Let us know what you think! |
Co-authored-by: Stephen Celis <[email protected]>
Co-authored-by: Stephen Celis <[email protected]>
Co-authored-by: Stephen Celis <[email protected]>
@stephencelis Thanks for the review, I replied to some of the issues you mentioned because AppKit is very different from UIKit, it's very cluttered and can't be viewed in the same way as UIKit. |
@Mx-Iris Thanks again for your patience! @mbrandonw and I took another look today and we think it's a fantastic start but we're not yet comfortable enough with the code to bring it in and maintain it as is. We think there are opportunities to remove some of the public and internal abstractions and expose helpers that mirror the platform's built-in helpers in a similar way to the UIKitNavigation module. We think there are a couple paths forward depending on your interest:
Do you have a preference here? We don't want to keep pushing back on your work if it feels annoying or nitpicky, but are happy to continue working with you if you're up for it. Otherwise, we hope you're OK with us taking on the next steps of this PR when we have a chance. Thoughts? |
@stephencelis Hey, Discussing the navigation separately indeed makes things clearer. Since I've been quite busy with work recently, I may not have much time to deal with these matters. Should we leave it here for now? I'll come back to finish it when I have time. |
@Mx-Iris Yup no rush at all! Thanks for the update. If you want to break things down to a smaller PR when you have time that would be helpful and instructive, and if Brandon and I have time we may push some commits ourselves 😄 |
@stephencelis I've set all protocols to internal to only expose explicit types externally, this would be a lot of boilerplate code, but does reduce complexity a lot, if you can, follow up by introducing macros to solve the boilerplate code issue, see the extension in the Modal navigation section to see if it addresses your concerns! |
This PR provides AppKit navigation with present, sheet, modal, modal session