Present a sheet from a menu item - macos

I have a Cocoa app that calls a sheet from the main window. This works fine from an NSButton on the main window. However, when I call from a MainMenu item, the sheet shows as a separate window. Is this expected behavior, or just expected from me :) I have studied this question ..
I call the sheet with this code:
-(IBAction) showSettingsSheet:(id)sender {
NSLog(#"%s", __FUNCTION__);
settingsSheetController = [[SettingsSheetController alloc] initWithWindowNibName:#"SettingsSheet"];
[settingsSheetController beginSheetModalForWindow:self.window completionHandler:^(NSUInteger returnCode) {
if (returnCode == kSettingsSheetReturnedOk) {
NSLog(#"Settings Returned ok");
} else if (returnCode == kSettingsSheetReturnedCancel) {
NSLog(#"Settings Returned cancel");
} else {
//self.categoryDisplayString = #"Oops!";
NSLog(#" Ooops");
}
}];
}
UPDATE
This is how the Attributes Inspector is set:

Try like this, if you want to display sheet in a mainmenu then try the below steps:-
Uncheck the visible at launch option inside attribute inspector of window which you want to display as a sheet attached the screenshot as well

Related

NSTableView (view based) with custom text colors and correct editing text color

Changing the text color in a view based NSTableView can be accomplished by using a custom table cell view and implement setBackgroundStyle:
- (void)setBackgroundStyle: (NSBackgroundStyle)backgroundStyle {
[super setBackgroundStyle: backgroundStyle];
UICoverageElement *element = self.objectValue;
if (backgroundStyle == NSBackgroundStyleEmphasized) {
self.textField.textColor = NSColor.highlightColor;
} else {
if ([element.value isEqualToString: #"<no name>"]) {
self.textField.textColor = NSColor.tertiaryLabelColor;
} else if ([element.value hasPrefix: #"UI"]) {
self.textField.textColor = typeColor;
} else if ([element.value hasPrefix: #"["] || [element.value hasPrefix: #"{"]) {
self.textField.textColor = objectColor;
} else {
self.textField.textColor = NSColor.textColor;
}
}
}
This works nice and well:
but causes trouble when editing a cell. In this case the field editor obviously takes the current manually set text color (which is white for a selected row) and shows that in a field editor with white background:
Now the question is: how can I set the correct text color when a cell view is being edited?
setBackgroundStyle is not called when editing starts, which makes it impossible to fix that problem in this function. I tried various methods that indicate start of the editing process, but none is called (but are called for standalone text fields). When I do not set the highlightColor then the editor color is correct but the highlight color of a selected row is wrong then.
Honestly, this is one of those things that you'd think would be really simple and straightforward, and it's really unfortunately not.
The only ways to affect the color in the field editor, are to either:
a) Set the color of your text field to the desired color before NSCell's selectWithFrame:... method is called
b) Change the color the text placed into the field editor after selectWithFrame:... is called.
So generally:
a) subclass NSTextFieldCell and set the field's text color back to the usual default before the field editor is set up.
- (void)selectWithFrame:(NSRect)rect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)delegate start:(NSInteger)selStart length:(NSInteger)selLength
{
self.textColorWhenNotEditing = self.textColor;
self.textColor = NSColor.controlTextColor;
[super selectWithFrame:rect inView:controlView editor:textObj delegate:delegate start:selStart length:selLength];
}
- (void)endEditing:(NSText *)textObj
{
[super endEditing:textObj];
self.textColor = self.textColorWhenNotEditing;
}
b) Change the field editor directly
- (void)selectWithFrame:(NSRect)rect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)delegate start:(NSInteger)selStart length:(NSInteger)selLength
{
[super selectWithFrame:rect inView:controlView editor:textObj delegate:delegate start:selStart length:selLength];
NSMutableDictionary * attribs = [((NSTextView *)textObj).typingAttributes mutableCopy];
attribs[NSForegroundColorAttributeName] = NSColor.controlTextColor;
[((NSTextView *)textObj).textStorage setAttributes:attribs range:NSMakeRange(0, textObj.string.length)];
((NSTextView *)textObj).typingAttributes = attribs;
}
I had previously answered a related question. Not sure if this should be marked as duplicate:
https://stackoverflow.com/a/54217318/217306
The gist is that the text editing mode is handled by a separate object called field editor. You should create a new instance and use it to customize the look during editing for your table.
windowWillReturnFieldEditor delegate method of NSWindow asks which editor to use for editing a client. You create such editor once for your table and return it when the delegate asks for an editor for your table.
- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client {
if (/* client is a textfield or subview in your table */) {
// Create customEditor elsewhere once
// Get row number or data that corresponds to client view
// Cusomize customEditor colors accordingly
return customEditor;
}
return nil; // Use default editor
}

Fullscreen NSWindow on Secondary Monitor - Swift

I have seen many posts on this in Obj C. Not too many in swift, and I just can't get it to work. I want to be able to make a window fullscreen on a specific NSScreen. The 'ToggleFullscreen' method isn't the best way because there aren't many options (for external displays). I tried:
// screen is my variable already set
outputWindow!.window!.setFrame(screen.frame, display: true, animate: true)
outputWindow!.window!.styleMask = NSFullScreenWindowMask
outputWindow!.window!.level = Int(CGShieldingWindowLevel())
// the above one doesn't make it fullscreen.
// it has a title bar and shows the menu on the screen.
// then i tried....
fullscreenWindow = NSWindow(contentRect: screen.frame, styleMask:NSBorderlessWindowMask, backing: NSBackingStoreType.Buffered, defer: false, screen: screen)
fullscreenWindow.level = Int(CGShieldingWindowLevel())
fullscreenWindow.makeKeyAndOrderFront(nil)
//that one works on my main display (somewhat). and does nothing on externals.
One thing I noticed with making my own fullscreen is that I get stuck into it. It's not like the OS X fullscreen where you can press esc to escape it. Are there any tricks to this? Thanks
I struggled with getting stuck on fullscreen as well. The reason behind that is using NSBorderlessWindowMask prevents the window from becoming key regardless of sending the makeKeyAndOrderFront: or makeKeyWindow messages to it. In Objective-C, the solution was to implement canBecomeKeyWindow in the subclassed NSWindow.
- (BOOL)canBecomeKeyWindow {
return YES;
}
Now it receives key events after going full screen as well.
This is a pseudo answer:
[in a inherited class of NSWondowController]
var fullscreen:Bool {
set {
if newValue == true {
if fullscreen == true {
return
}
frameOrig = self.window?.frame
self.window?.setFrame((NSScreen.main()?.frame)!, display: true)
}
else {
self.window?.setFrame(frameOrig!, display: true)
}
}
get {
return self.window?.frame == NSScreen.main()?.frame
}
}
I also trapped key events on both the calling-window and fullscreen window and point them to the above function so that toggling is possible. Like so:
[in some viewcontroller]:
override func keyUp(with event: NSEvent) {
switch event.keyCode {
case 53:
debugPrint("ESC - randomviewcontroller")
(randomView.window?.windowController as! RandomWindowController).fullscreen = !(RandomView.window?.windowController as! RandomWindowController).fullscreen;
break
default:
super.keyUp(with: event)
}
}
I think the above can be done much better & smarter... (picked up Swift a month ago...) but as the saying goes: "It worked on my computer!"
Hope it helps.

Get currently selected text in active application in Cocoa

I have a status-menu app that can be started using a system wide shortcut. When the app gets active, it would be great if I could somehow get the text that is selected in the currently running application.
So for example I type something in my text-editor, select the text, hit my global shortcut, my app comes up and I would now love to know the selected text from the text-editor.
What I have so far is the following (adopted code from How to get global screen coordinates of currently selected text via Accessibility APIs.)
AXUIElementRef systemWideElement = AXUIElementCreateSystemWide();
AXUIElementRef focussedElement = NULL;
AXError error = AXUIElementCopyAttributeValue(systemWideElement, kAXFocusedUIElementAttribute, (CFTypeRef *)&focussedElement);
if (error != kAXErrorSuccess) {
NSLog(#"Could not get focussed element");
} else {
AXValueRef selectedTextValue = NULL;
AXError getSelectedTextError = AXUIElementCopyAttributeValue(focussedElement, kAXSelectedTextAttribute, (CFTypeRef *)&selectedTextValue);
if (getSelectedTextError == kAXErrorSuccess) {
selectedText = (__bridge NSString *)(selectedTextValue);
NSLog(#"%#", selectedText);
} else {
NSLog(#"Could not get selected text");
}
}
if (focussedElement != NULL) CFRelease(focussedElement);
CFRelease(systemWideElement);
The problem here is that it does not work with apps like Safari and Mail...
Thanks
This is actually very easy, kAXSelectedTextAttribute is your friend.
extension AXUIElement {
var selectedText: String? {
rawValue(for: kAXSelectedTextAttribute) as? String
}
func rawValue(for attribute: String) -> AnyObject? {
var rawValue: AnyObject?
let error = AXUIElementCopyAttributeValue(self, attribute as CFString, &rawValue)
return error == .success ? rawValue : nil
}
}
This is not technically a solution to your exact question because the user would have to trigger this from the Services menu rather than it simply happening when they trigger your menu bar app.
You could use a System Service. You create a service for your app that sends the currently selected text to your menu bar app via a Pasteboard.

Disable menu items on ApplescriptObjc

I am coding with ApplescriptObjc.
I want to disable a menu item "Preference..." in a function, but I can't do it.
I can disable buttons. Here is the code:
myButton's setEnabled_(false)
So I tried to disable a menu item like a button:
myMenuItem's setEnabled_(false)
I looked at an Apple Reference (https://developer.apple.com/library/mac/documentation/cocoa/reference/ApplicationKit/Protocols/NSMenuValidation_Protocol/Reference/Reference.html), but I couldn't use this reference because I don't understand how to disable it indeed.
- (BOOL)validateMenuItem:(NSMenuItem *)item {
int row = [tableView selectedRow];
if ([item action] == #selector(nextRecord) &&
(row == [countryKeys indexOfObject:[countryKeys lastObject]])) {
return NO;
}
if ([item action] == #selector(priorRecord) && row == 0) {
return NO;
}
return YES;
}
I thought I can use this function in this way:
on validateMenuItem_(myMenuItem)
if myMenuItemIsEnabled = true then
return true
else
return false
end validateMenuItem_
But this doesn't work (no response). How can I disable menu items?
After connecting the menu item to an IB property, use the following code to disable it:
set (myMenuItem's enabled) to false
Where myMenuItem is a property connected to the menu item you wish to disable. You must make sure to uncheck the menu item's menu option called "Auto Enables Items".

Binding selected radiobox to enabling checkbox in Cocoa

I haven't worked with Cocoa bindings a lot before, so I would need a little help here.
I have a radio button group ( NSMatrix ) with three buttons and one checkbox.
I want the checkbox to be enabled only when the last radio button is selected.
Found a tutorial online which advised to bind the selected tag property of the radio button group to the enabled property of the checkbox. The last radiobutton needs to have a tag of 1, the others would need to have a tag of 0.
This works great.
The problem is, that if the checkbox is checked and the radiobutton selection is changed, it stays checked although it isn't enabled. I would want that the box gets unchecked when it changes to the disabled state.
Any advise would be appreciated!
Thanks in advance!
Any way to achieve this without any code?
I doubt it's possible to do this without code.
I handle this in the model, using KVO. Code looks something like this:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([#"checkWithInProcess" isEqualToString:keyPath]) {
NSNumber *oldValue = [change objectForKey:NSKeyValueChangeOldKey];
NSNumber *newValue = [change objectForKey:NSKeyValueChangeNewKey];
BOOL oldValueAsBool = (oldValue != (id)[NSNull null]) && oldValue.boolValue;
BOOL newValueAsBool = (newValue != (id)[NSNull null]) && newValue.boolValue;
if (oldValueAsBool && !newValueAsBool) {
// Save the value
savedRecordValueWithInProcess = self.recordValueWithInProcess;
self.recordValueWithInProcess = nil;
} else if (!oldValueAsBool && newValueAsBool) {
// Restore the value or set it to the default
if (savedRecordValueWithInProcess)
self.recordValueWithInProcess = savedRecordValueWithInProcess;
else
self.recordValueWithInProcess = [NSNumber numberWithBool:NO];
savedRecordValueWithInProcess = nil;
}
}
}
And during initialization:
[self addObserver:self
forKeyPath:#"checkWithInProcess"
options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
context:[Characteristic class]];

Resources