Disable NSDocument's "Revert To" & "Duplicate" menu item - objective-c

I am creating a Mac app to read a XML document and save it. Everything is working fine except "Revert To" & "Duplicate" menu items. Till i find a solution for that i want to disable both of them, but i didn't found any solution for it, Please let me know how can i disable both the options so that they end user cannot click on them.
I already looked into Menu's from .xib so that i can disable them but i don't see any options.
I tried to somehow manipulate below code, but i didn't found any answers.
override func duplicate() throws -> NSDocument {
return self
}

The general way to disable a menu item in Cocoa is returning false in validateMenuItem(_:) (or validateUserInterfaceItem(_:).)
In this case, put the following code in your NSDocument subclass.
override func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
guard let action = menuItem.action else { return false }
switch action {
case #selector(duplicate(_:)):
return false
case #selector(revertToSaved(_:)):
return false
default: break
}
return super.validateMenuItem(menuItem)
}
However, according to the Apple's Human Interface Guidelines, you should not leave menu items which are not used. So, if your app doesn't support duplication and revert features at all, I prefer to remove the items rather than to disable.

Related

Jetpack Compose - ModalBottomSheet comes up with soft keyboard

I have a problem with ModalBottomSheet and it's on my work computer so I can't record it to you right now. So basically, after I give focus to one of my TextFields, my keyboard comes up and pushes all the content upwards so I can see the TextField that I'm writing to. When I'm hiding my keyboard I can see that my ModalBottomSheet hides too, but I never set it to come up.
So if you are familiar with this bug, please let me know your solutions.
My coworker, so he inserted a boolean that checks if keyboard is up or not and if it is, dont put ap modal bottom sheet.
You can use this method until this problem is fixed with an additional update.
You can use LaunchedEffect for this. Here is an example for you.
The important thing here is to disable the ModalBottomSheetDialog when the keyboard is opened and re-enable it half a second after the keyboard is closed.
You can trigger the required function by assigning a value to this variable when the keyboard is turned on, and then changing and checking this value when the keyboard is closed.
/*Change this value to "keyboard_on" when the keyboard is turned on and "keyboard_off" when the keyboard is closed again. You can give different names for different usage areas. That's why we're using a string, not a Boolean.*/
var taskCodeValue = remember { mutableStateOf("keyboard_off") }
var sheetOpener by remember { mutableStateOf(true) }
if (taskCodeValue.value == "keyboard_off"){
LaunchedEffect(taskCodeValue.value == "keyboard_off"){
delay(500)
sheetOpener = true
}
}else {
sheetOpener = false
}
/*
By adding the Scaffold, which includes ModalBottomSheet and other compose
elements, into a box, we enable them to work independently of each other.
*/
Box(modifier = Modifier.fillMaxSize()) {
Scaffold(
content = {}
)
if (sheetOpener){
ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {}
) {}
}
}

How to switch to another XML layout after click on button?

I would like to have one kotlin file with the logic and I would like to allow users to switch between two different XLM layouts (logic of program is still the same, but layout of buttons shall be changed when clicking on button).
I simply add setContentView function to setOnClickListener for this button in order to load activity_main_second_layout.xml layout.
PS. activity_main_second_layout.xml is almost the same like activity_main.xml, I only changed the position of elements (not the names of elements)
button_switch_to_the_second_design.setOnClickListener {
setContentView(R.layout.activity_main_second_layout);
}
When clicking on the button, voala, the layout really changes to the second one.
BUT the functionality of the program is not working any more, the logic disappear. It seems that I need to resume running of the program somehow to make the code working again without interuption including loss of variables.
There is a lot of ways to do that.
In my opinion you should not try to change layout in runtime - it's possible, but you have to override setContentView and rebind all views and all listeners (or do it in other method, which will be called after changing the layout).
So... Sth like this:
fun sth() {
setContentView(R.layout.activity_main_second_layout)
rebindLayout(R.layout.activity_main_second_layout)
}
fun rebindLayout(#LayoutRes layoutId: Int) {
when (layoutId) {
R.layout.activity_main_first_layout -> { /* rebind views here */ }
R.layout.activity_main_second_layout -> { /* rebind views here */ }
}
}
The other's, better I think is to create independent fragments and change fragment via fragmentManager.
Others approches - ViewAnimator, ViewSwitcher.

How do I intercept downloads from a WKWebView?

I am making a Cocoa app that involves users selecting pictures from online for use within the app. I am also looking to collect contextual metadata from those downloads, such as the host the image came from, the website the user was visiting, the exact MIME type in the response headers, etc.
Basically I want to curate my user across the internet, downloading images and metadata into that user's account as I go. Until today I thought this would be impossible.
But just recently I was toying with a WKWebView and I tried right clicking on an image. I saw this...
Is there any way I could connect to that Download Image button, and get notifications when its clicked?
I did figure out how to make "Download Linked File" work and its a doozy that will not fit in a SO answer: https://github.com/kfix/MacPin/commit/9e5f925819f7f54ef29baff1e90783b820e683a3
However implementing those private delegate functions doesn't seem to allow "Download Image" to signal my WkWebView app in any way.
You can intercept those non-working "Download Image" and "Download Linked File" menu items by subclassing the WKWebView class and implementing the willOpenMenu method like this:
class MyWebView: WKWebView {
override func willOpenMenu(_ menu: NSMenu, with event: NSEvent) {
for menuItem in menu.items {
if menuItem.identifier == "WKMenuItemIdentifierDownloadImage" ||
menuItem.identifier == "WKMenuItemIdentifierDownloadLinkedFile" {
menuItem.action = #selector(menuClick(sender:))
menuItem.target = self
}
}
}
func menuClick(sender: AnyObject) {
if let menuItem = sender as? NSMenuItem {
Swift.print("Menu \(menuItem.title) clicked")
}
}
}
Instead of this you can also simply hide the menu items with menuItem.isHidden = true
Detecting the chosen menu item is one thing, but knowing what the user actually clicked in the WKWebView control is the next challenge :)

tvOS: Focus first button as preferred focus view when buttons are in horizontal order

I have been trying to focus the first button but focus engine takes the third button right below the tab bar as the first item to be focussed. I tried with preferred focus view but found that when i place the buttons in vertical order then preferred takes the preferred view to be focussed but when i placed all the buttons in horizontal plane it always takes the third button.The other approach i can think of if Focus Guide but i wonder how that will work in this scenario?
override weak var preferredFocusedView: UIView? {
get {
return thrirdButton
}
}
It happens because focus engine takes the nearest possible focus element as 1st element as you can see from the picture attached.I have attached the context screenshot for the view controller. Any help or clue to solve this will be appreciated.
Solution :
We need to add focus guide just above button 3 and redirect it to button one when the focus guide is focussed.
private var focusGuide = UIFocusGuide()
self.view.addLayoutGuide(focusGuide)
self.focusGuide.bottomAnchor.constraintEqualToAnchor(self.button3.topAnchor).active = true
self.focusGuide.leftAnchor.constraintEqualToAnchor(self.button3.leftAnchor).active = true
// Width and height
self.focusGuide.widthAnchor.constraintEqualToAnchor(self.button3.widthAnchor).active = true
self.focusGuide.heightAnchor.constraintEqualToAnchor(self.button3.heightAnchor).active = true
focusGuide.centerXAnchor.constraintEqualToAnchor(self.button3.centerXAnchor).active = true
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
self.focusGuide.preferredFocusedView = button1
}
Try adding a UIFocusGuide just above the button rows. In that case, before reaching the button, it will hit your focus guide in which you can redirect the preferredFocusedView to that particular button. The code to redirect is to be done by overriding didUpdateFocusInContext method.
Assuming you have setup the focus guide in viewDidload, the following is a sample,
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
super.didUpdateFocusInContext(context, withAnimationCoordinator: coordinator)
self.focusGuide.preferredFocusedView = self.mypreferredbutton
}
TO initialise a focus guide ( Do the addition of guide to view in viewDidLoad)
var focusGuide = UIFocusGuide()
self.view.addLayoutGuide(self.sidefocusGuide)
self.focusGuide.leftAnchor.constraintEqualToAnchor(self.placerLabel.leftAnchor).active = true
self.focusGuide.rightAnchor.constraintEqualToAnchor(self.placerLabel.rightAnchor).active = true
self.focusGuide.topAnchor.constraintEqualToAnchor(self.audioTable.topAnchor).active = true
I too faced a similar issue but somehow able to do it through this. The preferredFocusedView approach will also work, but you have to do lot of circus by updating some reference variable and then calling setneedsfocusupdate for that view. Try the focus guide way. Hope it helps.
EDITED:
I have added code how to setup the guide. In this case I have put the guide on the right side of my view. So, whenever your focus hits the guide, it redirects to the preferredfocusguide view that you want the focus to go to in the didUpdateFocusInContext. "There is only one view in focus at anytime.", remember thes ? So, the moment it hits the guide, the overriden method gets hit which in turn moves your focus to the guide's preferred view. For examples, you can refer to Apple's UIKitCatlog app for tvOS and here is one link explaining the same.
The best way to give the initial focus to your preferred view is by using preferredFocusEnivronments.
override var preferredFocusEnvironments: [UIFocusEnvironment] {
return [firstButton]
}
preferredFocusEnvironments will be called before didUpdateFocus, that way you can inform the system, where you want the focus to be redirected.

How can I get a specific button running on tvOS (AppleTV) to take focus? (Objective C)

The project have some views with different buttons. When I hide a view and show the other view, I can't get the focus on my button.
I think is related to setNeedsFocusUpdate. I have read the Apple doc. There is not any example.
Does anyone know how to do it and put an example (Objective C)?
You need to override preferredFocusedView, and when you are hiding one view and showing there call this method setNeedsFocusUpdate, your preferredFocusedView implementation should be something like this
- (UIView *)preferredFocusedView
{
// Add your logic here, it could be more complicated then what is below
if (view1.hidden)
{
return _button;
}
else
{
return _button2
}
}
And if you want to make custom view get focus, override canBecomeFocused method and return true
Edit
You can use add a breakpoint and execute this command po [buttonYouWantToFocus _whyIsThisViewNotFocusable] it will tell you why its not focusable.
If you are adding a sub view programmatically, maybe this is what you want:
- (UIView *)preferredFocusedView {
return [view1 preferredFocusedView];
}
I realize your question is specific to Objective-C but here is a way to solve for this in Swift. You need to override the preferredFocusedView property.
override var preferredFocusedView: UIView? {
guard primaryView.hidden == false else {
return secondaryView
}
return primaryView
}
Then just call setNeedsFocusUpdate() whenever an event happens that causes your views to be hidden. Hope this helps...
Another option (if you don't want to use preferredFocusedView) is, instead of setting your view to be hidden, simply remove it from it's superview, like so:
myView.removeFromSuperview()
This automatically takes the focus away from the button that is removed and gives it to another one that is still on screen.