Set the size and position of windows on current or specific screen in Swift MacOS [duplicate] - swift

Beginner question:
I am working on a macOS App with current Xcode 8.3.3 and Swift3. I am using MASShortcut to open a window by a shortcut that has been hidden by startup.
I use the following code on the shortcut event:
NSApplication.shared().windows.last!.makeKeyAndOrderFront(nil)
NSApplication.shared().activate(ignoringOtherApps: true)
For multiple monitor setups (I have two external displays attached to my MacBook), I want to specify the screen where the window pops up. I know there is NSScreen.screens() that gives back all available screens. But how do I use it for letting my window pop up on screen 1/2/3?
Thanks a lot!
Edit: Solved with the answer of #michael-doltermann:
I can iterate over NSScreen.screens() and access for example the midX/midY coords to create a NSPoint instance to replace my window.
var pos = NSPoint()
pos.x = NSScreen.screens()![myIndex].visibleFrame.midX)
pos.y = NSScreen.screens()![myIndex].visibleFrame.midY)
self.window?.setFrameOrigin(pos)

Each screen from NSScreen.screens() has a visibleFrame property that tells you the global frame rectangle.
You can then set your window origin to fit inside the frame rect coordinates of whatever screen you want. Objective-C answers can be seen here and here.
This does mean you have to write some code to specify the preferred window. In my own app, I take the global frame rectangles for each screen and then scale them way down into a NSView to display something that looks like the Monitors pane from System Preferences:

Related

Mac OS - Swift - Get screen containing the dock

I would like to get the screen containing the dock and the menubar.
Is there a way to do this?
I've check Apple documentation about NSScreen but couldn't find any information about that.
Thanks!
Actually, the answer was, as #Willeke suggested, in the documentation of NSScreen.screens (see:
https://developer.apple.com/documentation/appkit/nsscreen/1388393-screens).
The screen considered as main (with the menubar) is the one with index 0.
It is not the same as NSScreen.main which is the screen with the current active window.

How to show and then hide overlay window that occupies full screen

So I'm trying to show a window overlay over entire screen (even dock and menu bar) capturing all input events, and after a Timer defined x seconds, hide it. I know this is possible because there's an app in AppStore doing that.
I've found two approaches:
First:
let windowLevel = CGShieldingWindowLevel();
let windowRect = NSScreen.main?.frame
var overlayWindow = NSWindow(contentRect: windowRect!, styleMask: NSWindow.StyleMask.borderless, backing: NSWindow.BackingStoreType.buffered, defer: false, screen: NSScreen.screens[0])
overlayWindow.level = NSWindow.Level(rawValue: NSWindow.Level.RawValue(windowLevel))
overlayWindow.backgroundColor = .black
overlayWindow.alphaValue = 0.4
overlayWindow.makeKeyAndOrderFront(nil)
overlayWindow.makeMain()
Second:
Using storyboards
let window = FullScreenWindowController(windowNibName: NSNib.Name(rawValue: "FullScreenWindow"))
self.application.runModal(for: self.window.window!)
The first solution causes this log to appear:
Assertion failure in -[NSWindow _changeJustMain], /Library/Caches/com.apple.xbs/Sources/AppKit/AppKit-1504.83.101/AppKit.subproj/NSWindow.m:14861
Invalid parameter not satisfying: [self canBecomeMainWindow]
And also I'm unable to run a timer while the modal is being presented. The modal blocks all events and the timer loop can't happen.
I saw some approaches with NSRunner looper... but I'm not sure this is still possible with current SDK version.
As for the second solution, I can't restore the initial app state, I mean hide the overlay after showing it.
The other drawback of this solution is that the dock is still visible and interaction is possible.
Would appreciate some suggestions as I'm quite new to MacOS dev.
Got it solved by setting window level to CGShieldingWindowLevel() in the second approach.
Not sure though if this is correct(Apple permits) and can be used (docs say shouldn't)...

Caret cursor color and size

How do I change the size and colour of the flashing cursor in my textfield using my storyboard for my Mac app.
I assume I have to connect it into the view controller script (control drag) and edit some uitextfield color parameter for the cursor?
But I don't seem to be getting anywhere fast. I am working in swift 2.2 with Mac app Storyboards. Like Ulysses and Taskpaper.
In Swift 3:
Change YOURTEXTFIELD to your textfield and you are set:
(YOURTEXTFIELD.value(forKey: "textInputTraits") as AnyObject).setValue(UIColor.black, forKey: "insertionPointColor")
Documentation: https://developer.apple.com/documentation/appkit/nstextview/1449309-insertionpointcolor
Search for _drawInsertionPointInRect that's the private method that you need. Though you also need some other stuff I think... but it's all mixed into my code so I can't say for sure what it all is. Anyway search for _drawInsertionPointInRect and you'll get to some explanations.

Window visible on all spaces (including other fullscreen apps)

I'm trying to make a window (NSWindow) visible on all the spaces including other full screen app windows. I've been trying to set a higher window level as well as playing with expose and spaces settings in the inspector. I found some solutions here, but they do not work. At least on El Capitan.
Here is a sample code to test:
let window = NSWindow(contentRect: NSRect(x: 300, y: 300, width: 200, height: 200), styleMask: NSBorderlessWindowMask, backing: .Buffered, `defer`: true)
window.backgroundColor = NSColor.greenColor()
window.level = Int(CGWindowLevelForKey(.FloatingWindowLevelKey))
window.collectionBehavior = [.CanJoinAllSpaces, .Transient]
window.makeKeyAndOrderFront(nil)
Now it displays the window on all the desktop spaces but it does not display it over the full screen windows of other apps.
If you want a window to be visible on top of other fullscreen windows (in spaces), you should make an agent (accessory) application.
You can do it by setting LSUIElement key in the application’s Info.plist to 1 (YES)
If you still need a regular application you can do following:
Create a separate agent (helper) application inside your main application bundle which will show your window. (There are plenty of good examples on how you can create such application)
Play with NSApplicationActivationPolicy. You can try to change application's activation policy during a runtime.
Swift 3:
NSApp.setActivationPolicy(.accessory) switch to agent(accessory)
NSApp.setActivationPolicy(.regular) switch to ordinary application
Keep in mind that .accessory policy hides the icon from Dock and you still need the code you have already:
window.collectionBehavior = .canJoinAllSpaces
window.level = Int(CGWindowLevelForKey(.floatingWindow))
You're on the right track in a way, you need to set your window object's level to one level above the Shield Window's to be in the the forefront:
window.level = Int(CGShieldingWindowLevel()) + 1
Do note that this technique is not really recommended as some problems might arise from the interaction between full-screen graphics (OpenGL full-screen drawing contexts is an example) and the graphics hardware; but it's your best bet if you want to ensure overlay on top of all other applications.

SWT: How to “render” a Widget in the background / into an offscreenbuffer

I would very much appreciate your advice and help:
How can I render a SWT Widget/Component in the BACKGROUND (offscreenbuffer?) and get the “painted” pixels that were drawn by the Widget/Component to save them on the harddisk:
What I currently have is:
Display display = new Display();
Shell shell = new Shell(display);
// ...
MyWidgetComponent mwc = new MyWidgetComponent(shell, SWT.BORDER);
shell.open();
Image screenshot = new Image(shell.getDisplay(), shell.getBounds());
GC.copyArea(screenshot, 0, 0);
//...
Problem:
Taking the screenshot itself of the shell/widget works, but it will open a new Window in the Taskbar. That is something I do NOT want.
What I want to achieve is:
I want to run this application completely in the background as a “server application” (for example embed and call this into a servlet). So the MyWidgetComponent should be rendered pixel by pixel completely in the offscreenbuffer and I later I retrieve the pixels and save them to the harddisk or directly return the rendered widget as an image as the result of the servlet-request. (I do not want to popup any windows in a server environment, in case this might be a windows server...).
How can I achieve this. I searched a lot but havent found anything useful.
Thank you very much!!
Jan
I can't answer your question directly, but I have run into a similar problem that I struggled with: taking a screenshot of a Shell or Widget while it is obstructed from view.
Consider, for instance, window A that overlaps window B. A screenshot is made of B using your code:
Image screenshot = new Image(shellB.getDisplay(), shellB.getBounds());
GC.copyArea(screenshot, 0, 0);
My findings revealed that this could be done under Windows Vista, Windows 7 and Mac OS X (although I'm unsure about the latter). However, under Windows XP, Linux with GNOME and Linux with KDE, the screenshot contains a white area where the overlapping window obstructs the view.
I haven't found a solution for this, and I suspect that not only is this behavior platform dependent, but also fairly buggy in SWT.
I'd love to hear that I'm off the mark, though!