Why does win.setPosition('center') move the window to top-left? - windows

I have this code which I execute after toggling fullscreen back from fullscreen to windowed mode:
win.setPosition('center');
The window consistently appears in top-left part of the screen -- not the middle.
The manual claims at http://docs.nwjs.io/en/latest/References/Window/#winsetpositionposition :
position {String} the position of the window. There are three valid positions: null or center or mouse
Move window to specified position. Currently only center is supported on all platforms, which will put window in the middle of the screen.
I tried "center" both quoted and unquoted, and even "centre" in case it was a typo in the manual.
For the record, the "mouse" value (which I don't want/need) also doesn't work; it seems to put the window in a random or semi-random place, far away from the cursor.
Windows 10. nwjs-v0.61.0-win-x64.

Make sure you are creating the win variable like below:
<script>
const win = nw.Window.get();
// Move window to top left of screen
win.moveTo(0, 0);
// Move window to middle of screen
win.setPosition('center');
// Manually move window to middle of screen
const screen = nw.Screens.screen[0].bounds;
const x = Math.floor((screen.width - win.width) / 2);
const y = Math.floor((screen.height - win.height) / 2);
win.moveTo(x, y);
</script>
You can use nw.Screen.screens to see dimensions of all the monitors if you wanted to move it to a different location.
https://nwjs.readthedocs.io/en/latest/References/Screen#screenscreens
https://nwjs.readthedocs.io/en/latest/References/Window/#winmovetox-y
https://nwjs.readthedocs.io/en/latest/References/Window/#winsetpositionposition

Related

PsychToolBox GetImage doesn't save using given rect dimensions

I'm trying to save a cropped portion of the screen from my PTB experiment, but the Screen('GetImage',...) function is not working properly. Here is my code:
screenNumber = 0;
% Open screen with grey background
[w, rect] = Screen('OpenWindow', screenNumber,...
background,[], [], [],[],6);
[xCenter, yCenter] = RectCenter(rect);
sideLength = randi([100 175], 1);
baseRect = [0, 0, sideLength, sideLength];
% Center the frame
centeredRect = CenterRectOnPoint(baseRect, xCenter, yCenter);
%dot position
dots.x = randi([ round(xCenter - (sideLength/2))+15 round(xCenter + (sideLength/2))-15], 1, numeral);
dots.y = randi([round(yCenter - (sideLength/2))+15 round(yCenter + (sideLength/2))-15], 1, numeral);
%skipping code to move dots.x into dot_info.position for brevity
Screen('DrawDots',w, dot_info.position, dot_info.size,...
dot_info.colour, [], 2); %dots are drawn to center of screen
Screen('Flip', w);
% Save dot cue
%im = Screen('GetImage', w);
im = Screen('GetImage', w, centeredRect);
imwrite(im, filename);
The issue is that there should be a small square image, centered on the centeredRect rectangle, which contains the dots I've drawn. However, all I get is a correctly sized rectangle without the dots. If I save without passing the centeredRect parameter (commented out line), I get a saved picture of the whole screen, showing the dots in the center - so I know the dots are being drawn. There must be something wrong with the GetImage call and its use of the rect, such that its not saving from the center point of the screen, so i just get the background. When I run this on my monitor it works fine, but on my laptop it does not. Unfortunately I don't have access to my monitor now so I need to get this to work on my laptop. I've checked with a Screens() call and there is only one screen: screen 0 - my laptop screen.
Any help greatly appreciated. Thanks.
SOLVED: I'm not sure why this worked, but I had to multiply the elements in centeredRect by 2. Hoping this helps someone else in future.

Find cursor's coordinates in NSTextView

I have wrapping NSTextView instances stacked vertically, for example:
The quick brown fox
jumps over the lazy dog
Jackdaws love my big
sphinx of quartz
I need to move between them with up/down arrows. For example, when the cursor is positioned after the l in "lazy" and the user presses the down arrow, the cursor should jump right after the y in "my" – like it would do if these sentences were in the same text view.
By default, when the down arrow is pressed while the cursor is at the last wrapped line, a text view moves it to the end of that line. While I can use textView(_:doCommandBy:) in NSTextViewDelegate to detect the "arrow down" selector and override the default behavior, there are two problems:
I can determine if the cursor is at the last line by getting its position via the selectedRanges property and then checking for the newline character after this position, but it is not possible to know if it is at the last wrapped line, i.e. near the border of the current text view.
I need to know the X coordinate of the cursor to place it at approximately the same X coordinate in another text view (the fonts won't necessarily be fixed-width, so I can't rely on the character count).
I suppose both of them can be resolved via NSLayoutManager, but I can't wrap my head around all of its available methods.
It turned out to be relatively easy, here's what I've done (the examples are in C#). First, boundingRect(forGlyphRange:in:) gets the cursor's location in the current view:
var cursorLocation = new NSRange(CurrentTextView.SelectedRange.Location, 0);
var cursorCoordinates = CurrentTextView.LayoutManager.BoundingRectForGlyphRange(cursorLocation, CurrentTextView.TextContainer).Location;
Then if the second text view is below, the insertion point will be at 0 on the Y axis:
var insertionPoint = new CGPoint(cursorCoordinates.X, 0);
And if it is above, then another view's height should be used (reduced by 1, otherwise the resulting character index will be incorrect and the cursor will be placed at the end of the line):
var insertionPoint = new CGPoint(cursorCoordinates.X, AnotherTextView.Bounds.Size.Height - 1);
After getting the insertion point, another view needs to become the first responder and then characterIndexForInsertion(at:) does the job:
Window.MakeFirstResponder(AnotherTextView);
var index = AnotherTextView.CharacterIndex(insertionPoint);
AnotherTextView.SelectedRange = new NSRange(index, 0);

move TRectangle with mouse (FMX, Win32)

I have an FMX form with a TLayout on it aligned to client. On the TLayout I have a TRectangle. I can move the TRectangle easily with the following code in a button click event:
Rectangle1->Position->X = Rectangle1->Position->X + 10;
Is there a clean way for me to do this (move the rectangle) with the mouse? Like click on the Rectangle and move it around to a new location? I'm just playing around trying to make a little drawing program to learn....
Using C++Builder 10.2 Version 25.0.29899.2631 and building in Win32.
UPDATE: I took Hans approach and ended up making it work nicely. I've added the full code as an answer below. Yay!
A way to drag components is to store the offset between the mouse position and the control position on mouse down, then use this offset to calculate the position of the control in the mouse move event.
In semi-pseudo code it would look like this:
Add the following to your TForm class:
fMouseIsDown: boolean;
fMouseDownOffset: TPointF;
procedure OnRectangleMouseDown(X,Y)
begin
fMouseIsDown := true;
fMouseDownOffset := PointF(Rectangle.Position.X-X, Rectangle.Position.Y-Y)
end;
procedure OnRectangleMouseMove(X,Y)
begin
if fMouseIsDown then
begin
Rectangle.Position.X := X+fMouseDownOffset.X;
Rectangle.Position.Y := Y+fMouseDownOffset.Y;
end;
end;
procedure OnRectangleMouseUp(X,Y);
begin
fMouseIsDown := false;
end;
Here is the complete code needed to left-click on and move a TRectangle on an FMX form in Win32 (haven't tried it on mobile yet). Just create a new FireMonkey multi-device application and put a TRectangle and a TButton on it.
Code to add to the forms's class declaration (in the .h file just under class TForm1 : public TForm {):
bool fMouseIsDown; // gets set to TRUE when left mouse click on the rectangle
TPointF fMousePosGlobal; // this is the mouses position relative to the screen
TPointF fMousePosForm; // this is the mouse pos relative to the form
TPointF captionOffset; // this is a small offset necessary since the form's TOP and LEFT are outside of form's client area due to caption bar and left edge of form
TPointF fMouseInRectAtClick; // this is the mouse pos relative to the rectangle (top left of rectangle is 0,0)
Code to add to the rectangle's Rectangle1MouseDown event:
if (Button == 0) { // 0 for left mb, 1 for right mb
fMouseIsDown = true;
fMouseInRectAtClick.X = X; //mouse pos with respect to rectangle at time of click
fMouseInRectAtClick.Y = Y;
}
Code to add to the rectangle's Rectangle1MouseMove event (add to the form's FormMouseMove too or sometimes you lose the rectangle on a fast drag):
fMousePosGlobal = Screen->MousePos(); //mouse global pos
fMousePosForm.X = fMousePosGlobal.X - Form1->Left; // mouse pos relative to the form
fMousePosForm.Y = fMousePosGlobal.Y - Form1->Top;
if (fMouseIsDown) {
Form1->Rectangle1->Position->X = fMousePosForm.X - captionOffset.X - fMouseInRectAtClick.X;
Form1->Rectangle1->Position->Y = fMousePosForm.Y - captionOffset.Y - fMouseInRectAtClick.Y;
}
Code to add to the Rectangle1MouseUp event:
fMouseIsDown = false; // add this to the form's MouseUp too in case you "lose" the rectangle on a drag. That only happened when i forget to set the offsets.
Code to add to the Button's Button1Click event:
captionOffset.X = 8.0; // this accounts for the width of the form left edge
captionOffset.Y = 30.0; // this accounts for the height of the form caption
// if you don't add this your "drag point" on the rectangle will jump as soon as you start the drag.
Thanks to Hans for the direction to start!
Also, i noticed the drag wasn't smooth when moving across other controls. If this bothers you then you need to set those other controls "HitTest" false so they ignore it. Add TEdit boxes if you want to see all the TPointF coordinates as you move mouse and rectangle - helps a bunch when trying to figure out coordinates.

GetWindowRect returns a size including "invisible" borders

I'm working on an app that positions windows on the screen in a grid style. When Running this on Windows 10, there is a huge gap between the windows. Further investigation shows that GetWindowRect is returning unexpected values, including an invisible border, but I can't get it to return the real values with the visible border.
1) This thread suggests this is by design and you can "fix" it by linking with winver=6. My environment does not allow this but I've tried changing the PE MajorOperatingSystemVersion and MajorSubsystemVersion to 6 with no affect
2) That same thread also suggests using DwmGetWindowAttribute with DWMWA_EXTENDED_FRAME_BOUNDS to get the real coordinates from DWM, which works, but means changing everywhere that gets the window coordinates. It also doesn't allow the value to be set, leaving us to reverse the process to be able to set the window size.
3) This question suggests it's lack of the DPI awareness in the process. Neither setting the DPI awareness flag in the manifest, or calling SetProcessDpiAwareness had any result.
4) On a whim, I've also tried adding the Windows Vista, 7, 8, 8.1 and 10 compatibility flags, and the Windows themes manifest with no change.
This window is moved to 0x0, 1280x1024, supposedly to fill the entire screen, and when querying the coordinates back, we get the same values.
The window however is actually 14 pixels narrower, to take into account the border on older versions of Windows.
How can I convince Windows to let me work with the real window coordinates?
Windows 10 has thin invisible borders on left, right, and bottom, it is used to grip the mouse for resizing. The borders might look like this: 7,0,7,7 (left, top, right, bottom)
When you call SetWindowPos to put the window at this coordinates:
0, 0, 1280, 1024
The window will pick those exact coordinates, and GetWindowRect will return the same coordinates. But visually, the window appears to be here:
7, 0, 1273, 1017
You can fool the window and tell it to go here instead:
-7, 0, 1287, 1031
To do that, we get Windows 10 border thickness:
RECT rect, frame;
GetWindowRect(hwnd, &rect);
DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame, sizeof(RECT));
//rect should be `0, 0, 1280, 1024`
//frame should be `7, 0, 1273, 1017`
RECT border;
border.left = frame.left - rect.left;
border.top = frame.top - rect.top;
border.right = rect.right - frame.right;
border.bottom = rect.bottom - frame.bottom;
//border should be `7, 0, 7, 7`
Then offset the rectangle like so:
rect.left -= border.left;
rect.top -= border.top;
rect.right += border.left + border.right;
rect.bottom += border.top + border.bottom;
//new rect should be `-7, 0, 1287, 1031`
Unless there is a simpler solution!
How can I convince Windows to let me work with the real window coordinates?
You are already working with the real coordinates. Windows10 has simply chosen to hide the borders from your eyes. But nonetheless they are still there. Mousing past the edges of the window, your cursor will change to the resizing cursor, meaning that its still actually over the window.
If you want your eyes to match what Windows is telling you, you could try exposing those borders so that they are visible again, using the Aero Lite theme:
http://winaero.com/blog/enable-the-hidden-aero-lite-theme-in-windows-10/
AdjustWindowRectEx (or on Windows 10 and later AdjustWindowRectExForDpi) might be of use. These functions will convert a client rectangle into a window size.
I'm guessing you don't want to overlap the borders though, so this probably isn't a full solution--but it may be part of the solution and may be useful to other people coming across this question.
Here's a quick snippet from my codebase where I've successfully used these to set the window size to get a desired client size, pardon the error handling macros:
DWORD window_style = (DWORD)GetWindowLong(global_context->window, GWL_STYLE);
CHECK_CODE(window_style);
CHECK(window_style != WS_OVERLAPPED); // Required by AdjustWindowRectEx
DWORD window_style_ex = (DWORD)GetWindowLong(global_context->window, GWL_EXSTYLE);
CHECK_CODE(window_style_ex);
// XXX: Use DPI aware version?
RECT requested_size = {};
requested_size.right = width;
requested_size.bottom = height;
AdjustWindowRectEx(
&requested_size,
window_style,
false, // XXX: Why always false here?
window_style_ex
);
UINT set_window_pos_flags = SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
CHECK_CODE(SetWindowPos(
global_context->window,
nullptr,
0,
0,
requested_size.right - requested_size.left,
requested_size.bottom - requested_size.top,
set_window_pos_flags
));
There are still two ambiguities in the above use case:
My window does have a menu, but I have to pass in false for the menu param or I get the wrong size out. I'll update this answer with an explanation if I figure out why this is!
I haven't yet read about how Windows handles DPI awareness so I'm not sure when you want to use that function vs the non DPI aware one
You can respond to the WM_NCCALCSIZE message, modify WndProc's default behaviour to remove the invisible border.
As this document and this document explain, when wParam > 0, On request wParam.Rgrc[0] contains the new coordinates of the window and when the procedure returns, Response wParam.Rgrc[0] contains the coordinates of the new client rectangle.
The golang code sample:
case win.WM_NCCALCSIZE:
log.Println("----------------- WM_NCCALCSIZE:", wParam, lParam)
if wParam > 0 {
params := (*win.NCCALCSIZE_PARAMS)(unsafe.Pointer(lParam))
params.Rgrc[0].Top = params.Rgrc[2].Top
params.Rgrc[0].Left = params.Rgrc[0].Left + 1
params.Rgrc[0].Bottom = params.Rgrc[0].Bottom - 1
params.Rgrc[0].Right = params.Rgrc[0].Right - 1
return 0x0300
}

Convert a given point from the window’s base coordinate system to the screen coordinate system

I am trying to figure out the way to convert a given point from the window’s base coordinate system to the screen coordinate system. I mean something like - (NSPoint)convertBaseToScreen:(NSPoint)point.
But I want it from quartz/carbon.
I have CGContextRef and its Bounds with me. But the bounds are with respect to Window to which CGContextRef belongs. For Example, if window is at location (100, 100, 50, 50) with respect to screen the contextRef for window would be (0,0, 50, 50). i.e. I am at location (0,0) but actually on screen I am at (100,100). I
Any suggestion are appreciated.
Thank you.
The window maintains its own position in global screen space and the compositor knows how to put that window's image at the correct location in screen space. The context itself, however doesn't have a location.
Quartz Compositor knows where the window is positioned on the screen, but Quartz 2D doesn't know anything more than how big the area it is supposed to draw in is. It has no idea where Quartz Compositor is going to put the drawing once it is done.
Similarly, when putting together the contents of a window, the frameworks provide the view system. The view system allows the OS to create contexts for drawing individual parts of a window and manages the placement of the results of drawing in those views, usually by manipulating the context's transform, or by creating temporary offscreen contexts. The context itself, however, doesn't know where the final graphic is going to be rendered.
I'm not sure if you can use directly CGContextRef, you need window or view reference or something like do the conversion.
The code I use does the opposite convert mouse coordinates from global (screen) to view local and it goes something like this:
Point mouseLoc; // point you want to convert to global coordinates
HIPoint where; // final coordinates
PixMapHandle portPixMap;
// portpixmap is needed to get correct offset otherwise y coord off at least by menu bar height
portPixMap = portPixMap = GetPortPixMap( GetWindowPort( GetControlOwner( view ) ) );
QDGlobalToLocalPoint(GetWindowPort( GetControlOwner( view ), &mouseLoc);
where.x = mouseLoc.h - (**portPixMap).bounds.left;
where.y = mouseLoc.v - (**portPixMap).bounds.top;
HIViewConvertPoint( &where, NULL, view );
so I guess the opposite is needed for you (haven't tested if it actually works):
void convert_point_to_screen(HIView view, HIPoint *point)
{
Point point; // used for QD calls
PixMapHandle portPixMap = GetPortPixMap( GetWindowPort( GetControlOwner( view ) ) );
HIViewConvertPoint( &where, view, NULL ); // view local to window local coordtinates
point.h = where->x + (**portPixMap).bounds.left;
point.w = where->y + (**portPixMap).bounds.top;
QDLocalToGlobalPoint(GetWindowPort( GetControlOwner( view ), &point);
// convert Point to HIPoint
where->x = point.h;
where->y = point.v;
}

Resources