"coupled" NSWindows - cocoa

I am trying to have "coupled" NSWindows.
I mean 2 NSWindows of the same size, positioned side by side. If I move one, the other one has to move as well to keep both side by side.
I tried to do this with child window but when the child window is moved, the "mother" stays in place.
How I can get this behavior?

Designate an object as the windows' delegate and respond to the ‑windowDidMove:, ‑windowWillResize:toSize: and/or ‑windowWillMove: delegate methods, or register to receive the corresponding notifications. You can then resize the other window as you see fit.
This sounds like an odd UI, by the way. Can you not just use a split view?

Related

How to keep a hidden view in responder chain?

I have a NSSplitViewController and in one of the items I have multiple buttons with keyboard shortcuts.
Once the user hides the item, the shortcuts don't fire.
Is there any way to keep the buttons in the hidden view as part of the responder chain?
Sounds like the simple answer is no, according to Apple's docs. A simple workaround, however, might be to move the buttons out of the visible area by, say, shifting their bounds right by 10,000 or so. If they are in a scrollview/clipview that would expand to show the items in their new position, then this would not work so well, but if they aren't, it ought to work fine. If they are in a scrollview, then you might find a way to make them completely transparent, to achieve a similar effect.
That said, perhaps it is worth considering whether you have the right design in the first place, since having buttons that are not visible respond to key events is a questionable design from a user-interface perspective (as reflected by the fact that Apple tries to prevent it). Maybe those keyboard events should really be getting handled by a view higher in the view hierarchy, or by the window, or some such entity?

Can receive pointerDragged but not pointerPressed in a container

I have a UI in Codenameone where a container contains another container which contains some widgets. On the bottom level container I'm able to receive pointerDragged events but not pointerPressed. These (pointerPressed) seem to be consumed by the widgets on the top of the hierarchy, but not move down to the bottom container.
How can I fix this?
I'd like to do this to detect left-right swipes on the bottom container. Is there perhaps a better way to do that?
Only focusable components receive the events directly in Codename One otherwise we would have to deliver events to multiple components which might inhibit performance.
The best way to do this is to use an existing component, e.g. Tabs supports swiping and hiding the tabs so you can just use that.
Making the Container focusable is probably not as desireable (although possible) since it might make interaction with the components within difficult.
You can use a pointer listener on the parent form as the Tabs component does internally. This should always work since the form gets all the events: https://github.com/codenameone/CodenameOne/

Discard mouse events on NSWindow based on click position

Let's say I have a floating, borderless, circular NSWindow.
It is circular because the content view simply draws a red circle.
That content view needs to be layer-backed ([contentView setWantsLayer:YES]), because I'm applying CoreAnimations on it, e.g., animated scaling.
Usually, the clickable area of a NSWindow is defined by the transparency of the pixels of the content view. However, once the content view of a NSWindow becomes layer-backed, transparent areas will also receive clicks, unfortunately.
In my case, this is a serious problem, because I only want to receive clicks within the radius. But now, a click within the rect of the window, but beyond the circle radius, will activate the window (and thus, the entire app), which it shouldn't. Also the window is draggable via the corner of its content view.
My initial thought was to implement [NSWindow sendEvent:] in a subclass and check whether the click was performed within the radius, using [theEvent locationInWindow]. I thought I could simply discard the event, if it's beyond the radius, by not calling [super sendEvent:theEvent] then. This however did not work: I noticed, that the mouseDown:; window method is called even before the sendEvent:; method.
I've search a lot but the only idea I found, was to have a proxy like non-layer backed NSWindow on top of the window, which delegates clicks conditionally, but this led to unpredictable UI behavior.
Do you guys have any idea, how to solve it?
So after a few weeks, I came to the following results:
A) Proxy window:
Make use of a non layer-backed proxy window, which is placed on top of the target window as a child window. The proxy window has the same shape, as the target window, and since it is not layer-backed, it will properly receive and ignore events. The proxy window delegates all events to the target window by overwriting sendEvent:. The target window is set to ignore all mouse events.
B) Global Mouse Pointer observation:
Install both a global and local event monitor for NSMouseMovedMask|NSLeftMouseDraggedMask events using addGlobalMonitorForEventsMatchingMask and addLocalMonitorForEventsMatchingMask. The event monitors disable and enable ignoring mouse events on all registered target windows based on the current global mouse position. In the case of circular windows, the distance between the mouse pointer and every target window must be calculated.
Both approaches work well in generally, but I've been experiencing some unpredictable misbehaviors of the child window approach (where the child window is 'out-of-sync' of its parent's position).
UPDATE: Both approaches have some significant disadvantages:
In A), the proxy window sometimes may be out of sync and may be placed slightly off the actual window.
In B), the event monitor has a big impact on battery life while moving the mouse, even if the app is not the front-most application.
If you want to Discard mouseDown event based on position you can use the:
CGPathContainsPoint(path,transform,point,eoFill):Bool
Setup your path to match your graphics. Circles, ellipses, rectangles, triangles or paths and even compositional paths (paths with holes in them).

Separating windows on fly

Good evening.
I have two controllers. Call them NSViewController1 and NSViewController2. Controllers placed on one NSWindow and have some network logic. It's not very good to delete them from memory while program is running.
I would like to create button. If user click on button, single window should separate on two windows. First of new window should contain NSViewController1 and second window should contain NSViewController2. Much better if switching is animated.
What is the best way to implement described behavior? May be somebody saw an open source project with this task?
It would be best if you had a controller which controls both your window controllers, as well as both of your view controllers.
If the button is clicked, you can send a message to this controller and remove the second view from the window and add it to the other window.
And of course adjust the frame of those. I guess it's not that big of a deal.

When (and how) to lay out the children of a Win32 window in response to a resize?

Windows sends several messages when a window is resized:
WM_GETMINMAXINFO
WM_ENTERSIZEMOVE
WM_EXITSIZEMOVE
WM_NCCALCSIZE
WM_SIZING
WM_SIZE
WM_WINDOWPOSCHANGING
WM_WINDOWPOSCHANGED
and possibly more.
If I would like to re-position the children when my window is resized, where and how should I do so?
I'm looking for the "best" method -- i.e. the method with the fewest gotcha's and the least flicker.
My current method is to perform all the repositioning inside WM_NCCALCSIZE, using DeferWindowPos.
However, I've also tried handling it inside WM_WINDOWPOSCHANGED... but no matter where I handle it, it seems like there is always at least one "moment" when the window is painted in an in-between state, where the window's size has changed, but its contents have yet to be resized.
Another effect I would also like to avoid: moving a child after it has already moved. I don't want the user to see a control sliding down and then back up because of my change -- it should have as few transient effects as possible.
Am I doing this correctly? Is there a better place I can lay out the window's children?
You should re-position the window in the WM_SIZE message, because that is the last one that the window recieves before complete it's task...To re-position the window you can use MoveWindow

Resources