StrokesPlus.net
Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

Options
Go to last post Go to first unread
Rob  
#1 Posted : Saturday, August 3, 2019 1:52:05 PM(UTC)
Rob

Rank: Administration

Reputation:

Groups: Translators, Members, Administrators
Joined: 1/11/2018(UTC)
Posts: 1,349
United States
Location: Tampa, FL

Thanks: 28 times
Was thanked: 416 time(s) in 354 post(s)
UPDATE

Start with version 0.3.5.8, the .Rectangle property now returns the visible part of the window, it doesn't include the invisible window frame border area and .AbsoluteRectangle has been added to retrieve the entire window's rectangle.

Location, Size are now based on the non-border area of the window, so setting the Location to new Point(0,0) will put the window exactly in the upper left corner with the border being offset by the frame border amount.

ORIGINAL POST

Here are several script examples to change the size and location of windows.
Note that many of these require version 0.3.3.1 or greater to set the Rectangle property of a window:

Code:
////////////////////////////////////////////////////////
// SET WINDOW LOCATION/POSITION
////////////////////////////////////////////////////////

// Note that location/sizes don't affect a maximized or minimized window at the time, but it does set the
// dimensions which are used when the window is restored (not maximized or minimized)
// Use this script to restore the window if it's maximized before changing the size/location:
if(sp.ForegroundWindow().Maximized) {
    sp.ForegroundWindow().Restore();
}

// These examples use sp.ForegroundWindow(), since that can be used in all scripts.
// However, you could replace that with sp.WindowFromPoint(new Point(x,y), true) to get a window 
// at a specific location (change x & y accordingly); Or for an action, use the action.Start or 
// action.End point properties (locations): sp.WindowFromPoint(action.Start, true)
// For actions, you can replace this with action.Window instead, which is the same 
// as sp.WindowFromPoint(action.Start, true)
// All of the methods/properties above return a SystemWindow object, described in Help, for the main
// window specified

// 1) This would be useful if you want to move the window relatively to its 
//    current position, 50 px to the left and 50px lower
var wnd = sp.ForegroundWindow();
var wndLocation = wnd.Location; //returns .NET Point struct
// Point struct docs: https://docs.microsoft.com/en-us/dotnet/api/system.windows.point?view=netframework-4.6
wndLocation.X = wndLocation.X + 50;
wndLocation.Y = wndLocation.Y + 50;
wnd.Location = wndLocation;

// 2) Set the location explicitly
var wnd = sp.ForegroundWindow();
wnd.Location = new Point(-8,30);

// Which could also be done in a single line. The reason to assign the window to wnd
// would only be beneficial if you're also going to be doing other things with the
// foreground window, as shown in the next section.
sp.ForegroundWindow().Location = new Point(-8,30);

// 3) Set the location using the window's Rectangle. Note that .Size and .Location above
//    are ultimately dereived from the Rectangle, so working with the Rectangle is
//    more direct. 
//    REQUIRES StrokesPlus.net version 0.3.3.1 or greater to set the Rectangle property!
var wnd = sp.ForegroundWindow();
var wndRect = wnd.Rectangle; //returns .NET Rectangle struct
// Rectangle struct docs: https://docs.microsoft.com/en-us/dotnet/api/system.drawing.rectangle?view=netframework-4.6
wndRect.X = -8; //Could also use relative positioning, e.g. wndRect.X = wndRect.X + 50;
wndRect.Y = 30; //Could also use relative positioning, e.g. wndRect.Y = wndRect.Y + 50;
// You can also change the size of the window here in a single step
wndRect.Width = 400; //Could also use relative sizing, e.g. wndRect.Width = wndRect.Width + 50;
wndRect.Height = 400; //Could also use relative sizing, e.g. wndRect.Height = wndRect.Height + 50;
wnd.Rectangle = wndRect;



/////////////////////////////////////////////
// SET WINDOW SIZE
/////////////////////////////////////////////

// Note that location/sizes don't affect a maximized or minimized window at the time, but it does set the
// dimensions which are used when the window is restored (not maximized or minimized)
// Use this script to restore the window if it's maximized before changing the size/location:
if(sp.ForegroundWindow().Maximized) {
    sp.ForegroundWindow().Restore();
}

// 1) This would be useful to change the size relatively to its current size
var wnd = sp.ForegroundWindow();
var wndSize = wnd.Size; //returns .NET Size struct
// Size struct docs: https://docs.microsoft.com/en-us/dotnet/api/system.drawing.size?view=netframework-4.6
wndSize.Width = wndSize.Width + 50;
wndSize.Height = wndSize.Height + 50;
wnd.Size = wndSize;


// 2) Set the size explicitly
var wnd = sp.ForegroundWindow();
wnd.Size = new Size(400,400);

// Which could also be done in a single line. The reason to assign the window to wnd
// would only be beneficial if you're also going to be doing other things with the
// foreground window, as shown in the next section.
sp.ForegroundWindow().Size = new Size(400,400);

// 3) Set the size using the window's Rectangle. Note that .Size and .Location above
//    are ultimately dereived from the Rectangle, so working with the Rectangle is
//    more direct. 
//    REQUIRES StrokesPlus.net version 0.3.3.1 or greater to set the Rectangle property!
var wnd = sp.ForegroundWindow();
var wndRect = wnd.Rectangle; //returns .NET Rectangle struct
// Rectangle struct docs: https://docs.microsoft.com/en-us/dotnet/api/system.drawing.rectangle?view=netframework-4.6
wndRect.Width = 400; //Could also use relative sizing, e.g. wndRect.Width = wndRect.Width + 50;
wndRect.Height = 400; //Could also use relative sizing, e.g. wndRect.Height = wndRect.Height + 50;
// You can also change the location of the window here in a single step
wndRect.X = -8; //Could also use relative positioning, e.g. wndRect.X = wndRect.X + 50;
wndRect.Y = 30; //Could also use relative positioning, e.g. wndRect.Y = wndRect.Y + 50;
wnd.Rectangle = wndRect;

/////////////////////////////////////////////
// SIZE & LOCATION USING THE SCREEN BOUNDARY
/////////////////////////////////////////////

// Note that location/sizes don't affect a maximized or minimized window at the time, but it does set the
// dimensions which are used when the window is restored (not maximized or minimized)
// Use this script to restore the window if it's maximized before changing the size/location:
if(sp.ForegroundWindow().Maximized) {
    sp.ForegroundWindow().Restore();
}

// Set the size and location of the foreground window to 1/3 of the 
// left side of the screen where the window resides
// REQUIRES StrokesPlus.net version 0.3.3.1 or greater to set the Rectangle property!

// First, get the Windows internal X & Y axes frame and border padding values so the 
// window can be placed right at the edges of the screen. Note that there may some
// inconsistencies with the values returned due to varying circumstances, so you
// may just need to use a fixed negative value for your system to get it just right
var Int32T = host.type('System.Int32');
var intValue = host.cast(Int32T, SystemMetric.SM_CXSIZEFRAME);
var SM_CXSIZEFRAME = sp.GetSystemMetricsByIndex(intValue);
intValue = host.cast(Int32T, SystemMetric.SM_CXPADDEDBORDER);
var SM_CXPADDEDBORDER = sp.GetSystemMetricsByIndex(intValue);
intValue = host.cast(Int32T, SystemMetric.SM_CYSIZEFRAME);
var SM_CYSIZEFRAME = sp.GetSystemMetricsByIndex(intValue);
intValue = host.cast(Int32T, SystemMetric.SM_CYBORDER);
var SM_CYBORDER = sp.GetSystemMetricsByIndex(intValue);
intValue = host.cast(Int32T, SystemMetric.SM_CYFRAME);
var SM_CYFRAME = sp.GetSystemMetricsByIndex(intValue);

var wnd = sp.ForegroundWindow();
var wndRect = wnd.Rectangle;
// Screen class docs: https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.screen?view=netframework-4.6
// Screen.WorkingArea returns a .NET Rectangle struct, but only for the size 
// of the working area (e.g. not the space used by the Taskbar)
// Screen.Bounds also returns a Rectangle, but for the entire screen area
wndRect.X = wnd.Screen.WorkingArea.X - (SM_CXSIZEFRAME + SM_CXPADDEDBORDER);  // Shift window to the left
wndRect.Y = wnd.Screen.WorkingArea.Y;
//JavaScript parseInt to make sure it uses a whole number
wndRect.Width = parseInt(wnd.Screen.WorkingArea.Width / 3) + (SM_CXSIZEFRAME + SM_CXPADDEDBORDER); 
wndRect.Height = wnd.Screen.WorkingArea.Height + (SM_CYSIZEFRAME + SM_CYBORDER  + SM_CYFRAME);
wnd.Rectangle = wndRect;


// Set the size and location of the foreground window to 1/3 of the 
// top side of the screen where the window resides
// REQUIRES StrokesPlus.net version 0.3.3.1 or greater to set the Rectangle property!

// First, get the Windows internal X & Y axes frame and border padding values so the 
// window can be placed right at the edges of the screen. Note that there may some
// inconsistencies with the values returned due to varying circumstances, so you
// may just need to use a fixed negative value for your system to get it just right

var Int32T = host.type('System.Int32');
var intValue = host.cast(Int32T, SystemMetric.SM_CXSIZEFRAME);
var SM_CXSIZEFRAME = sp.GetSystemMetricsByIndex(intValue);
intValue = host.cast(Int32T, SystemMetric.SM_CXPADDEDBORDER);
var SM_CXPADDEDBORDER = sp.GetSystemMetricsByIndex(intValue);
intValue = host.cast(Int32T, SystemMetric.SM_CYSIZEFRAME);
var SM_CYSIZEFRAME = sp.GetSystemMetricsByIndex(intValue);
intValue = host.cast(Int32T, SystemMetric.SM_CYBORDER);
var SM_CYBORDER = sp.GetSystemMetricsByIndex(intValue);
intValue = host.cast(Int32T, SystemMetric.SM_CYFRAME);
var SM_CYFRAME = sp.GetSystemMetricsByIndex(intValue);

var wnd = sp.ForegroundWindow();
var wndRect = wnd.Rectangle;
// Screen class docs: https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.screen?view=netframework-4.6
// Screen.WorkingArea returns a .NET Rectangle struct, but only for the size 
// of the working area (e.g. not the space used by the Taskbar)
// Screen.Bounds also returns a Rectangle, but for the entire screen area
wndRect.X = wnd.Screen.WorkingArea.X - (SM_CXSIZEFRAME + SM_CXPADDEDBORDER);  // Shift window to the left
wndRect.Y = wnd.Screen.WorkingArea.Y;
wndRect.Width = wnd.Screen.WorkingArea.Width + (SM_CXSIZEFRAME + SM_CXPADDEDBORDER) * 2; 
wndRect.Height = parseInt(wnd.Screen.WorkingArea.Height / 3) + (SM_CYSIZEFRAME + SM_CYBORDER  + SM_CYFRAME); //JavaScript parseInt to make sure it uses a whole number
wnd.Rectangle = wndRect;

Edited by user Wednesday, November 20, 2019 7:38:01 PM(UTC)  | Reason: Not specified

thanks 5 users thanked Rob for this useful post.
Yuichi on 8/4/2019(UTC), username101 on 8/10/2019(UTC), james200410 on 10/20/2019(UTC), niczoom on 2/9/2021(UTC), carcinogen75 on 9/17/2021(UTC)
username101  
#2 Posted : Thursday, August 15, 2019 10:01:18 PM(UTC)
username101

Rank: Newbie

Reputation:

Groups: Approved
Joined: 8/10/2019(UTC)
Posts: 4

Thanks: 3 times
Was thanked: 1 time(s) in 1 post(s)
One flaw with using system metrics (SM_*) to resize windows is many windows do not adhere to the system values meaning they can have a variety of border sizes.
This causes any window aligning that relies on the system values to be slightly offset depending on the application, leaving gaps between windows or placing them partly off-screen.

Luckily, thanks to the addition of ClientRectangle and ClientPointToScreenPoint in v0.3.3.3 you can now resize windows consistently across different applications.
Here are a couple examples (based on Rob's script) of window-agnostic resizing and positioning:
Code:
/////////////////////////////////////////////
// SIZE & LOCATION USING THE SCREEN BOUNDARY
/////////////////////////////////////////////
// Note that location/sizes don't affect a maximized or minimized window at the time, but it does set the
// dimensions which are used when the window is restored (not maximized or minimized)
// Use this script to restore the window if it's maximized before changing the size/location:
var wnd = sp.ForegroundWindow();
if(wnd.Maximized) {
    wnd.Restore();
}

// Set the size and location of the foreground window to 1/2 of the 
// left side of the screen where the window resides
// REQUIRES StrokesPlus.net version 0.3.3.3 or greater!

// I'm not certain if this is the actual value you need or if the offset is a result
// of some rounding error and this only works because both of these happen to be 1.
// If this breaks for you then try replacing these (SM_CYBORDER and SM_CXBORDER) with 1.
var Int32T = host.type('System.Int32');
intValue = host.cast(Int32T, SystemMetric.SM_CYBORDER);
var SM_CYBORDER = sp.GetSystemMetricsByIndex(intValue);
intValue = host.cast(Int32T, SystemMetric.SM_CXBORDER);
var SM_CXBORDER = sp.GetSystemMetricsByIndex(intValue);

// Windows 10 has an invisible resizing border around all windows that we need to compensate for.
// You could get removeW10ResizeBorder programmatically by using DwmGetWindowAttribute
// to get the value of DWMWA_EXTENDED_FRAME_BOUNDS, but I don't know if this is possible.
if (clr.System.Environment.OSVersion.Version.Major == 10) {
    removeW10ResizeBorder = 0; }
else {
    removeW10ResizeBorder = 8; }

var wndRect =  new Rectangle();
// Here is where the new 0.3.3.3 features are used to determine the window border sizes.
var offsets = wnd.ClientPointToScreenPoint(new Point(0,wnd.ClientRectangle.Height));
var hBorder = offsets.X - wnd.Rectangle.X;
var vBorder = wnd.Rectangle.Bottom - offsets.Y;
// Use min and max here to ensure the window is not smaller than the desired size.
// This can happen if the window is borderless (clientRectangle==Rectangle)
// since subtracting CX/CYBORDER will make the window smaller than the screen.
wndRect.X = Math.min(wnd.Screen.WorkingArea.X,
    wnd.Screen.WorkingArea.X 
    - hBorder
    + removeW10ResizeBorder + SM_CXBORDER);
wndRect.Y = wnd.Screen.WorkingArea.Y;

wndRect.Width = Math.max(Math.floor(wnd.Screen.WorkingArea.Width / 2), 
    Math.floor(wnd.Screen.WorkingArea.Width / 2)
    + 2 * hBorder
    - 2 * (SM_CXBORDER + removeW10ResizeBorder) );
wndRect.Height = Math.max(wnd.Screen.WorkingArea.Height,
    wnd.Screen.WorkingArea.Height 
    + vBorder
    -SM_CYBORDER - removeW10ResizeBorder);
wnd.Rectangle = wndRect;

/////////////////////////////////////////////
/////////////////////////////////////////////
/////////////////////////////////////////////

// Note that location/sizes don't affect a maximized or minimized window at the time, but it does set the
// dimensions which are used when the window is restored (not maximized or minimized)
// Use this script to restore the window if it's maximized before changing the size/location:
var wnd = sp.ForegroundWindow();
if(wnd.Maximized) {
    wnd.Restore();
}

// Set the size and location of the foreground window to the bottom right 
// 1/4 of the screen where the window resides
// REQUIRES StrokesPlus.net version 0.3.3.3 or greater!

// I'm not certain if this is the actual value you need or if the offset is a result
// of some rounding error and this only works because both of these happen to be 1.
// If this breaks for you then try replacing these (SM_CYBORDER and SM_CXBORDER) with 1.
var Int32T = host.type('System.Int32');
intValue = host.cast(Int32T, SystemMetric.SM_CYBORDER);
var SM_CYBORDER = sp.GetSystemMetricsByIndex(intValue);
intValue = host.cast(Int32T, SystemMetric.SM_CXBORDER);
var SM_CXBORDER = sp.GetSystemMetricsByIndex(intValue);

// Windows 10 has an invisible resizing border around all windows that we need to compensate for.
// You could get removeW10ResizeBorder programmatically by using DwmGetWindowAttribute
// to get the value of DWMWA_EXTENDED_FRAME_BOUNDS, but I don't know if this is possible.
if (clr.System.Environment.OSVersion.Version.Major == 10) {
    removeW10ResizeBorder = 0; }
else {
    removeW10ResizeBorder = 8; }

var wndRect =  new Rectangle();
// Here is where the new 0.3.3.3 features are used to determine the window border sizes.
var offsets = wnd.ClientPointToScreenPoint(new Point(0,wnd.ClientRectangle.Height));
var hBorder = offsets.X - wnd.Rectangle.X;
var vBorder = wnd.Rectangle.Bottom - offsets.Y;
// Use min and max here to ensure the window is not smaller than the desired size.
// This can happen if the window is borderless (clientRectangle==Rectangle)
// since subtracting CX/CYBORDER will make the window smaller than the screen.
wndRect.X = Math.min(wnd.Screen.WorkingArea.X + Math.floor(wnd.Screen.WorkingArea.Width / 2),
    wnd.Screen.WorkingArea.X + Math.floor(wnd.Screen.WorkingArea.Width / 2) 
    - hBorder
    + removeW10ResizeBorder + SM_CXBORDER); // Shift window to the left
wndRect.Y = Math.floor(wnd.Screen.WorkingArea.Height / 2);

wndRect.Width = Math.max(Math.floor(wnd.Screen.WorkingArea.Width / 2), 
    Math.floor(wnd.Screen.WorkingArea.Width / 2)
    + 2 * hBorder
    - 2 * (SM_CXBORDER + removeW10ResizeBorder) );
wndRect.Height=Math.max(Math.floor(wnd.Screen.WorkingArea.Height / 2),
    Math.floor(wnd.Screen.WorkingArea.Height / 2) 
    + vBorder
    - SM_CYBORDER - removeW10ResizeBorder);
wnd.Rectangle = wndRect;

Edited by user Friday, August 16, 2019 5:52:27 AM(UTC)  | Reason: clarity + readability

Rob  
#3 Posted : Friday, August 16, 2019 1:13:52 PM(UTC)
Rob

Rank: Administration

Reputation:

Groups: Translators, Members, Administrators
Joined: 1/11/2018(UTC)
Posts: 1,349
United States
Location: Tampa, FL

Thanks: 28 times
Was thanked: 416 time(s) in 354 post(s)
Regarding DwmGetWindowAttribute/DWMWA_EXTENDED_FRAME_BOUNDS, you could easily make a plug-in to handle this. It seems you have programming experience :)

Just make a .NET Class Library (public) DLL and drop it in the plug-ins folder then reload S+.

For example, name the class DesktopWindowManager and have some public methods (probably make the class static given the context) and the necessary pinvoke declarations.

Compile and drop the DLL in the plug ins folder, reload, and in your script call var dwmFrameBounds = DesktopWindowManager.GetExtendedFrameBounds(); or whatever.

It's probably best to return simple types, though ClearScript does seem to be pretty good in general of handling objects and exposing them to the JavaScript engine.
Rob  
#4 Posted : Wednesday, November 20, 2019 7:38:42 PM(UTC)
Rob

Rank: Administration

Reputation:

Groups: Translators, Members, Administrators
Joined: 1/11/2018(UTC)
Posts: 1,349
United States
Location: Tampa, FL

Thanks: 28 times
Was thanked: 416 time(s) in 354 post(s)
See updates in 0.3.5.8 for more intuitive window positioning by default.
Users browsing this topic
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.