Draw NSWindow on one screen only - cocoa

Let's say we have two screen scenario and a NSWindow,
positioned at the edge of screen 1.
A part of that of that window is also shown on screen 2.
What I would like to achieve, is to draw the window only on screen 1 and to not show the rest on screen 2.
The reason for that is that's some kind of a specially behaving mini window, not a usual one.
Is it possible to assign a certain NSScreen to draw the window on, only?

Two very different approaches:
Use Mavericks: What you looking for sounds like the behaviour of Mavericks' "screens have separate spaces" mode. In this mode a window is never drawn on two screens except during drag operations, at other times the window is draw on one screen with any areas protruding onto adjacent screens clipped. So if you can restrict use to 10.9 this may save you some work.
Borderless Windows: You can create a borderless non-opaque NSWindow, just set the appropriate flags. This is how applications create non-square windows, the visible area of the window is now entirely up to you. Now just clip your drawing to the area on one screen using the standard clipping support. You won't have a standard title bar or controls, unless you emulate them yourself, so you have to implement drag yourself etc. You say you have "some kind of a specially behaving mini window, not a usual one" so that may not be an issue.

Not that I've heard of.
What you could do though is restricting the position of the window e.g. via a NSWindowDelegate and windowDidMove: to listen to position changes and reposition the window appropriately.

Related

How can I make window zooming respect auto layout constraints?

To keep things simple, let's say I have a window containing a single view, which has auto layout constraints binding all 4 sides to the window container view with offset 0. And assume that this view also has a constraint setting its aspect ratio to a constant value. If I resize the window manually, then then window nicely maintains the desired aspect ratio. But if I click the little green zoom widget, then the window fills up the whole screen, regardless of the aspect ratio, with part of the view being above the top of the screen. Is there some way I can make zooming resize the window as big as it can be, without violating auto layout constraints?
I couldn't very well detect this problem in the delegate method windowWillResize:toSize:, because that doesn't tell me which screen it's thinking about putting the window on. I could try to fix the window size in the windowDidResize: delegate method, at which time I do know what screen it's on, but I'm not sure exactly how to do that without reinventing Auto Layout's wheel.
Apparently someone thinks I wasn't explicit enough, so I'll try again. Steps to reproduce:
In Xcode, create a new macOS App project using XIB interface.
Open MainMenu.xib and select the window.
Reshape the window to be approximately square.
Using the Attribute Inspector, set the Full Screen behavior for the window to Auxiliary Window.
Drag an Image View from the library and drop it in the window.
In the Attributes Inspector, set the image view to show the NSComputer image and scale axes independently.
Expand the image view to fill up the window content area.
With the image view selected, click the button to add new layout constraints.
Add 5 constraints, binding the 4 sides to the container, and setting the aspect ratio. (see screen shot)
Build and Run.
Observe that if you resize the window by dragging an edge or corner, the aspect ratio remains fixed.
Click the green zoom widget in the title bar of the window, and observe that the window expands without regard for the aspect ratio constraint, cutting off part of the image.
I just set up a test project exactly as you specified, and when I invoke the window zoom widget, the window expands and retains its aspect ratio i.e. it works as expected. The only thing I can think of that might be causing your issue: maybe your content hugging and content compression resistance priorities are at odds with your constraints? Mind you, I just left them at the default values and it worked fine. Unfortunately Mac/AppKit development (esp. when using IB) is rife with these kind of odd bugs and weird behaviour, probably because Apple has not given it any love in years, so bugs creep in/fester and they are clearly so DONE with developing UI the 'old fashioned way'. (Using SwiftUI to make a Mac app is just as frustrating, in different ways, so I'll stick with what I know). FYI, I used Xcode 13.4.1 to create this test project. Good luck!

Is it possible to clip a Win32 window to a single monitor?

Meaning that if I have several monitors, I would like the window to be painted in only one monitor and "not exist" on all the other monitors. I tried messing with WM_NCPAINT etc. but that only allows you to paint the frame differently, it doesn't allow you to hide it (preferably with the window shadow).
I'm playing with the idea of a window that you can spread over several monitors with different DPIs. Right now this is not possible (when 50% of a window is on a new monitor Windows "snaps" the window to the new DPI). On MacOS it draws a new window and displays both windows but in portions, the right DPI for the right monitor, and it's very smooth, none of the spastic snapping of Windows. But MacOS won't let you keep a window stretched across those two monitors. I want to see if I can emulate this behavior but allow a window to be stretched across a multi-monitor multi-DPI desktop.
I can freely scale my UI so that's not an issue, and the idea is to have in each of my Window objects an array of std::tuple<Monitor&, HWND> or something, one for each monitor, with EnableNonClientDpiScaling() etc., draw the appropriate and correctly scaled region into each HWND and then DeferWindowPos() to move all the HWNDs at once.
But I can try this only if it's possible to show each HWND on only one monitor, and so far no luck.

How to add left/bottom/right panes to Mac application as in XCode?

XCode 4 (but also iTunes and other Mac apps) provide side or bottom bars that can be shown or hidden with a smooth animation as the user presses a button.
How can I obtain a similar effect in my applications?
You can use NSSplitView to do that. You can have more than 2 subviews in a split view (left|center|right) and you can build a kind of hierarchy for the different bars (center consists of top and lower split views).
The show/hide effect is not built in, though. But you should be able to use the animator to do that. Most certainly you can also define animations for hiding a view.
You could also use the BWSplitView of http://brandonwalkin.com/bwtoolkit/ where the show/hide animation is already included.
You could also consider subclassing NSViewAnimation, which I believe Xcode uses in a number of places to achieve its fancy animation effects.

Going fullscreen in an itunes like application (cocoa OSX)

I have a cocoa application that has a finder like feel it and is made up of five views. On the left there is a gallery which is a finder like interface to choose a given object. This view stretches across the whole height of the window. Then on the right I have a window for a 3D simulation view and then below it I have three editing views. I would like to be able to press a button and have the 3d view take over the entire window and then go into fullscreen mode, and I am wondering if a more experienced Cocoa developer could give me some advice on how I might want to try this. Should I be removing all the other subviews then resizing the window and 3d view to the size of the screen or would it make more sense to try and just stretch the 3d view to the size of the screen and push the other interface pieces off the screen that way? I would like to eliminate the menu bar when this occurs to have a real full screen feel for me 3d view.
Look up the NSView method enterFullScreenMode:withOptions:. It's easier than you think.

How to draw outside a window?

Looking at a Windows tooltips class hint window, i see that it draws its drop-shadow outside the hint window's actual rectangle.
Using SpyXX - i can get the tooltip's window rect, and class styles:
Rectangle: (440, 229)-(544, 249), 104x20
Restored Rect: (440, 229)-(544, 249), 104x20
Client Rect: (0, 0)-(104, 20), 104x20
You'll notice that the drop shadow you see is physically outside the window that's being drawn. How can i draw a shadow outside around my window, while being outside my window?
Note: The shadow is not drawn using the standard CS_DROPSHADOW class style. i've confirmed this experimentally, and can also see the class style's for the window in SpyXX; it does not use CS_DROPSHADOW:
Windows Styles: 94000001
WS_POPUP 80000000
WS_VISIBLE 10000000
WS_CLIPSIBLINGS 4000000
TTS_ALWAYSTIP 1
Extended Styles: 00080088
WS_EX_LAYERED 80000
WS_EX_TOOLWIN 80
WS_EX_TOPMOST 8
So how can i draw outside my window?
Note: Trying to draw on the desktop DC is out. From Greg Schechter's Redirecting GDI, DirectX, and WPF applications:
Drawing To and Reading From the Screen
-- Baaaad!
Lastly, since we're on the redirection
topic, one particularly dangerous
practice is writing to the screen,
either through the use of GetDC(NULL)
and writing to that, or attempting to
do XOR rubber-band lines, etc. There
are two big reasons that writing to
the screen is bad:
It's expensive... writing to the
screen itself isn't expensive, but it
is almost always accompanied by
reading from the screen because one
typically does read-modify-write
operations like XOR when writing to
the screen. Reading from the video
memory surface is very expensive,
requires synchronization with the DWM,
and stalls the entire GPU pipe, as
well as the DWM application pipe.
It's unpredictable... if you somehow
manage to get to the actual primary
and write to it, there can be no
predictability as to how long what you
wrote to the primary will remain on
screen. Since the UCE doesn't know
about it, it may get cleared in the
next frame refresh, or it may persist
for a very long time, depending on
what else needs to be updated on the
screen. (We really don't allow direct
writing to the primary anyhow, for
that very reason... if you try to
access the DirectDraw primary, for
instance, the DWM will turn off until
the accessing application exits)
You can't draw outside your window in the manner you describe.
If you right click your desktop then go to properties/appearance/effects and uncheck 'Show shadows under menus' ... you will no longer have the shadow.
Bottom line is that this is a product of the window manager not your program.
Q: How do you draw outside of one window? A: Draw inside another window!
First thing to note is that the tooltip class actually does use the CS_DROPSHADOW style - but note that this is a class style, not a window style, so you have to look at the Class tab in the Spy++ properties dialog to find it. You'll see that the tooltips_class32 windows does indeed have this - and a few others.
But that just leads to the next question - how does that work? Well, it seems that Windows implements this by creating a helper HWND to draw the shadow - presumably it's creating another popup window the same size and shape as the one it's shadowing, filling it with gray, placing it directly underneath the main window, and setting it as a WS_EX_LAYERED window so that the shadow can be transparent and fade out around the edges using alpha-blending. And there's nothing to stop you from using the same or similar techniques yourself if you want to add a different type of shadow effect to one of your own windows.
So, long story short: if you want to draw outside of your own window, create a helper transparent window in the general area that you want to draw on, and draw on that helper window instead.
--
Now, if you try to find one of these helper shadow windows in Spy++, you won't find much. Unlike the tooltip_class32 windows, which are long-lived and just hide/show themselves as needed, these shadow windows are a more elusive creature: they are only created for as long as needed, so you'd have to refresh Spy++ while there's a tooltip or popup menu or other window using the shadow present - and that's tricky, since most tooltips and menus will disappear as soon as you move the mouse to switch to Spy++. But it turns out that the tooltips on Spy++'s own toolbar will stick around: so start Spy++, hover over an item in the toolbar, and hit F5 to refresh the HWND tree while the tooltip and shadow are present. Now scroll down, and you should see the third and fourth visible HWNDs in the tree are the tooltip itself, and right after that, a SysShadow window. Unfortunately, since the tooltip and shadow have by now disappeared, if you attempt to get the properties dialog for that HWND, you'll get a get a blank property dialog with an 'Invalid Window' message. If you really want to poke around and see how that SysShadow works, what styles it itself uses and so on, you could create a target app with a long-lived popup that uses CS_DROPSHADOW that you can then explore in Spy++ at leisure.
(Finally, note that these shadows are a completely different thing than the shadows that you see when one app window is on top of another above another since Vista: this type of shadow is part of Aero Glass mode, and handled by the same Desktop Composition Manager that adds the glass titlebar effect, and it doesn't use or need helper windows to implement the shadows.)
I wouldn't be surprised if that shadow is intimately tied to the window manager itself; it is after all the window manager who decides what window gets to paint which parts of itself and when it can do it. I don't see it as rocket science to paint that shadow if control over all that is gained, which the window manager has.

Resources