Tcl/Tk: get default position of resized window (and/or whether the default position is used) - window

I'm creating a multi-window application (cross-platform (X11, macOS, Windows), unspecified window manager), where each window corresponds to a document.
When creating a new document (and thus window), the window manager should be free to put the window wherever it finds suitable.
However, if the user manually moves the window to some location, this location should be saved along with the document (so on re-opening the document, it will be at exactly the place it was last saved).
The (initial) window size is fixed.
In order to find out the current position, I can either use wm geometry or winfo geometry, which both return slightly different results (the former with decoration (like border and menu), the latter without).
As I understand it, I need the result of wm geometry if I would like to restore the position of the window.
However, when I create an initial window and resize it, winfo geometry returns the correct position (but without the decoration), whereas wm geometry does not.
# hide the default window
wm withdraw .
toplevel .my
wm geometry .my 200x100
# the following prints "1x1+0+0" for both geometries
puts "[wm geometry .my] [winfo geometry .my]"
raise .my
# this prints something like "200x100+0+0 200x100+860+542"
puts "[wm geometry .my] [winfo geometry .my]"
If I manually move the window and then call [wm geometry .my] again, it now correctly reports whatever position the window is at.
So my question is:
Is there a way to get the window position (as set by the window manager) after I resized the window using wm geometry ${w}x${h}?
Alternatively, is there a way to find out (from the window itself) whether the user has moved it to some other place? (so I can save the position as "undefined, let the WM do their thing").
Ideally, such a solution would handle the case where the user to manually moved the window to the +0+0 position differently from the case where the window was not moved at all.
I'm especially interested in getting this information without having to bind to the <<Configure>> (or some other) event, in order to detect whether the user touched the window.
sidenote: setting the window dimensions
I know that I can set the window dimensions with both wm geometry ${w}x${h} and when creating the window with toplevel .wm -width $w -height $h.
When doing the latter, wm geometry returns the correct window position, but what are there any other differences between the two?

I think a more extensive use of winfo is what you need:
https://www.tcl.tk/man/tcl8.6/TkCmd/winfo.htm
set _x [winfo rootx .my]
The command above returns the X coord of the upper-left corner
winfo rooty .my
Same as before, returns the Y coord of the upper-left corner
winfo width .my
winfo height .my
Same for these self-explaining commands.
You can save in real time the position and size of any window, to restore later on re-open.

Related

Flicker-free expansion (resize) of a window to the left

Say you have a form that you can expand to the left to show additional controls:
Collapsed:
Expanded:
The simplest way to achieve this in Delphi is to use alRight as the primary anchor for all controls (instead of alLeft) and then simply adjust the width and X coordinate of the form. Either you can set the Width and Left properties individually, or you can use a function that sets them simultaneously, like
if FCollapsed then
SetWindowPos(Handle, 0, Left - Width, Top, 2 * Width, Height, 0)
else
SetWindowPos(Handle, 0, Left + Width div 2, Top, Width div 2, Height, 0)
The problem is that there is quite noticeable flickering in the always-visible part of the form (in this example, the buttons) while expanding or collapsing. Try it yourself!
It is possible for the operating system to resize the form to the left without any flickering at all -- just grab the left edge of the form using the mouse and drag the mouse to the left or right -- but I am unable to find any function in the Windows API that exposes this kind of resizing.
I have tried to use several different Windows API functions to resize and reposition the form, tried their various parameters (for instance, the SWP_* flags), tried LockWindowUpdate, WM_SETREDRAW, TForm.DoubleBuffered etc. to no avail. I also examined the possibility to use the WM_SYSCOMMAND SC_SIZE approach.
I am not yet sure if the problem lies at the OS level or the VCL level.
Any suggestions?
Edit: I am very surprised to see that this Q received close votes. Let me try to clarify:
Create a new VCL forms application.
Add a few buttons to the right side of the main form and a memo to the left. Set Anchors to [alTop, alRight] on all controls. On the OnClick handler of the buttons, add the following code:
if FCollapsed then
SetWindowPos(Handle, 0, Left - Width, Top, 2 * Width, Height, 0)
else
SetWindowPos(Handle, 0, Left + Width div 2, Top, Width div 2, Height, 0);
FCollapsed := not FCollapsed;
where FCollapsed is private boolean field of the form (initialized to false).
Now, click the buttons repeatedly. (Or give one of them keyboard focus and hold the Enter key for a few seconds.) You will probably notice that the region with the buttons on your monitor will not display a perfect still image, but will flicker. In addition, you might actually see 'ghosts' of the buttons to the left of the actual column of buttons.
I am unable to capture this millisecond flickering using screen capture, so instead I used a digital camera to record my screen:
https://privat.rejbrand.se/VCLFormExpandFlicker.mp4
In this video clip, it is apparent that the column of buttons isn't a static image on the screen; instead, for a few milliseconds each time the form is resized, this region is something else than it should be. It is equally apparent that there is a 'ghost' column of buttons to the left.
My question is if there is any reasonably simple way to get rid of these visual artefacts (that at least to me are very visible even if you expand/collapse the form a single time).
On my Windows 10/Delphi 10.1 computer at work, the form is resized in a perfect manner when I drag its left-most edge using the mouse: the unaffected client area of the form is perfectly static on the monitor. However, on my Windows 7/Delphi 2009 PC at home, I do see that there is a lot of repositioning going on when I do this.
I can provide some insight about why you see ghost images of the other half of your UI and possibly a way to stop it. The ghost image indicates that someone is copying your client area pixels (and copying them to the wrong place, always flush-left in your window) before you have a chance to redraw them with the correct pixels.
There are likely two different, overlapping sources of these ghost pixels.
The first layer applies to all Windows OSes and comes from a BitBlt inside SetWindowPos. You can get rid of that BitBlt in several ways. You can create your own custom implementation of WM_NCCALCSIZE to tell Windows to blit nothing (or to blit one pixel on top of itself), or alternately you can intercept WM_WINDOWPOSCHANGING (first passing it onto DefWindowProc) and set WINDOWPOS.flags |= SWP_NOCOPYBITS, which disables the BitBlt inside the internal call to SetWindowPos() that Windows makes during window resizing. This has the same eventual effect of skipping the BitBlt.
However, Windows 8/10 aero adds another, more troublesome layer. Apps now draw into an offscreen buffer which is then composited by the new, evil DWM.exe window manager. And it turns out DWM.exe will sometimes do its own BitBlt type operation on top of the one already done by the legacy XP/Vista/7 code. And stopping DWM from doing its blit is much harder; so far I have not seen any complete solutions.
For sample code that will break through the XP/Vista/7 layer and at least improve the performance of the 8/10 layer, please see:
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?
Since you have multiple child windows, the situation is even a little more complicated. The BitBlt type operations I mentioned above happen on your whole top-level window as a whole (they treat the window as one set of pixels regardless of how many windows are underneath, and regardless of CLIPCHILDREN). But you need to have windows move atomically so that on the next redraw they are all positioned correctly. You may find BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos useful for that (but only go there if the above tricks do not work).

Resizing Layered Windows

I'm hoping I can get away with communicating the issue I'm having clearly without posting a ton of source code. The source is all ASM and while most would understand it, it's still not their primary "thing."
I have a layered window which is working perfectly - up to the point of resizing it. The window is all per-pixel alpha. There are no transparent pixels. The borders and caption are opaque (255 alpha); the client area is something like 128 alpha or whatever I put it at. So no transparent pixels.
I have a function that handles redraws - it's actually geared toward rebuilding the frame for a new window size. GetWindowDC is called on the layered window. CreateCompatibleDC creates off of that DC. The bitmap is a DIB section. The bitmap is properly sent to UpdateLayeredWindow. Again, everything works perfectly as long as the window is not in active resize (border drag).
I have tried using DefWindowProc to handle border drags, as well as completely handling all aspects of that functionality myself. The results are the same: massive flicker but ONLY beyond the window boundaries that existed before border drag began, and then the window size does not ultimately change - sort of. The original window area remains displayed without issue. No flicker there. However if (for example) I'm dragging the right border to the right, everything right of the original window right edge flickers rapidly then vanishes. When I release the left mouse button, the display remains at the original size (the size the window was before dragging the border). I've verified the bitmap is correctly rebuilt to the new size. It seems like the DC for the layered window doesn't want to resize because the entire area of original window size is completely stable, but any new area appearing post-resize flickers then hides.
I've even tried removing the ws_ex_layered style before resizing the window in my manual handling (I've tried both MoveWindow and SetWindowPos) then resetting the ex style after SetWindowPos / MoveWindow but before dragging.
When I let DefWindowProc handle the updates, I got the exact same result. So indications are that something is wrong with my repainting. But I even whittled that process down (commenting out oodles of code for test) and just filled the bitmap with 0xFF0000FF (arbitrary test value, blue, full opacity) then jumped straight to UpdateLayeredWindow. Same result.
The bitmap used for UpdateLayeredWindow is deleted before calling the "rebuild" function and within the function that bitmap is recreated to the new size. I have verified the new size extensively - I can see the new bitmap as it high-speed flickers and it's all good. The DC is GetWindowDC from the layered window. The compatible DC creates off that DC. The bitmap is selected into the compatible DC, and that compatible DC is passed to UpdateLayeredWindow. If any of these things were off, I would never see the new bitmap displayed. It's there - it just flickers at high speed ONLY beyond the window bounds that existed before resize began. DefWindowProc yields the same results. Within that original area, my caption buttons slide off to the right as I drag the right border to the right, further verifying everything is good.
I'm at a loss. Any input would be welcome.

Unable to Move the Canvas or Change Any of the Transform Values

I am using Unity 5 and I started to make a menu scene. When I made the canvas, all of values under the Rect Transform component are locked and it says "some values driven by Canvas." The only thing I can change is the z position when using the gizmo in the editor. I can't reset the position or anything. Why is this happening?
This means that the canvas's canvas component has it's render mode set to Screen space - overlay. This forces it to be the size of the screen. Change it to World Space and it will allow you to resize it and move it around.
Changing the Render mode is not an ideal solution; neither is Overlay mode the reason why this is happening at all. World Space is just a render mode that changes the way your whole UI behaves and would mean a whole different set up and a whole lot more work just to get a child UI object to move independently.
Here is the description of what World Space is for from the Unity site:
In this render mode, the Canvas will behave as any other object in the
scene. The size of the Canvas can be set manually using its Rect
Transform, and UI elements will render in front of or behind other
objects in the scene based on 3D placement. This is useful for UIs
that are meant to be a part of the world. This is also known as a
“diegetic interface”.
The Rect Transform usually gets locked because it is a child of another Canvas Object which controls its Transforms. The way to fix this is to overwrite it by adding a “Layout Element” component to it. From there you can configure it to work the way you like and it can have transforms independent of the Parent UI Object.
For full details, see this Unity support page: https://support.unity3d.com/hc/en-us/articles/115000179163-How-to-overwrite-Width-and-Height-values-that-are-driven-by-a-Layout-Group-in-runtime-
Canvas is depend on game tab in your window panel.
Adjust panel by use of close tab or resize panel or doc game panel.
It will help you make default 800 X 600 canvas.

Puting a BoxSizer in the middle of its panel

I've written some code which displays a wx.Frame, within which there is a wx.Panel, within which there is a jpg. The jpg is the same size as the frame, so that when the program starts up the user is presented with a picture covering the whole of the window. Overlayed over the top of this picture, and in the centre of the panel I want to put a wx.BoxSizer containing various widgets. So far I've got the sizer visible but, try as I might, I cannot shift it from the uppermost lefthand corner of the panel to the center. No doubt there are many round-about ways of doing this involving grids and borders, but before I set off down this path, I was wondering if I'm missing something simple and there is a command that can be used to position in widget in a specified part of a panel.
ps. I also know I could do this using absolute positioning, but I want to be able to resize the window while keeping the sizer at its centre.
Normally, you can't see a sizer. But regardless, you don't really center a sizer. You center the widgets INSIDE the sizer. So when you add a widget to the sizer, you'd want to do something like this:
mySizer = wx.BoxSizer(wx.VERTICAL)
mySizer.Add(widget, 0, wx.CENTER|wx.ALL, 5)
Now, that will only center it horizontally. If you want it in the very center of the panel, then create another BoxSizer that is oriented horizontally and add the first sizer to the second one, centering it too.
hSizer = wx.BoxSizer(wx.HORIZONTAL)
hSizer.Add(mySizer, 0, wx.CENTER)

Matlab figure window transitions (screen transitions - GUI design)

My idea is building a logo image (similar to Matlab's startup one) which shows before my gui is appeared. I have the following code (it was obtained modifying the solution given by How do I make a splash screen for my MATLAB GUI application?) for it:
% create a figure1 that is not visible yet, and has minimal titlebar properties
fh = figure('Visible','off','MenuBar','none','NumberTitle','off',...
'DockControls','off');
% put an axes in it
ah = axes('Parent',fh,'Visible','on');
% put the image in it
ih = imshow('SIS.gif','Parent',ah);
% set the figure1 size to be just big enough for the image, and centered at
% the center of the screen
imxpos = get(ih,'XData');
imypos = get(ih,'YData');
set(ah,'Unit','Normalized','Position',[0,0,1,1]);
figpos = get(fh,'Position');
figpos(3:4) = [imxpos(2) imypos(2)];
set(fh,'Position',figpos);
movegui(fh,'center')
% make the figure1 visible
set(fh,'Visible','on');
pause(3);
close(fh);
I have two problems:
Number 1: I want the logo image's figure windows has no borders, no title and no taskbars. I have tried the WindowAPI but it does not work because I call it after the above code and because of the visibility of the window is off, then the handle of it is off too.
Number 2:I want that, when the logo image is disappeared, it is showed the gui window maximized. Where is the problem? The screen transition between the logo image's window and the gui window is not smoothed. I have tried to use a lot of Matlab´s applications that I found in Matlab Central's File Exchange (WindowAPI, Maxfig, Maximize, SetFigTransparency,...) without success. I realized that the problem is the visibility of my gui (I set off until all elements are created and then I change it to on). Because of the off visibility cause the handlevisibility is off too, the previous mentioned applications has no effects on the figure window that I want to maximize.
After observating the startup of Matlab, I have noticed that after the logo is showed, it appears a fullscreen image followed by the normal fullscreen of the program. So I have tried to create an maximized fullscreen windows that appears after the logo's windows is closed. However, now the problem is the transition between this last and the gui window. If I set the visibility of the gui window on and then I maximize it, during an instant it can be seen that transition that bothers me. I do not know what to do. I also think that if I could avoid that the guide window is currentfigure when I change its visibility, perhaps I would achieve it. Other solution it might be a timer that hold the white window as currentfigure while the guide window is behind changing its visibility but I do not know how to do. Thank you for your attention. Cheers.
As I show in this answer, derived from this MathWorks newsgroup thread, you can create a window without a title, borders, etc., using Java. Here's a modification of the code from my other answer to create a splash window centered on the screen:
img = imread('peppers.png'); %# A sample image to display
jimg = im2java(img);
frame = javax.swing.JFrame;
frame.setUndecorated(true);
icon = javax.swing.ImageIcon(jimg);
label = javax.swing.JLabel(icon);
frame.getContentPane.add(label);
frame.pack;
imgSize = size(img);
frame.setSize(imgSize(2),imgSize(1));
screenSize = get(0,'ScreenSize'); %# Get the screen size from the root object
frame.setLocation((screenSize(3)-imgSize(2))/2,... %# Center on the screen
(screenSize(4)-imgSize(1))/2);
frame.show; %# You can hide it again with frame.hide
I would try this to create your splash screen and see if it also helps with the problems transitioning to the next GUI window.
After a long research work, I have found a reasonable answer which was nearest to that I thought. If you type in "splash screen" in the File Exchange browser, you have some interesting applications designed specifically to it. I have chosen splash.m. With respect to the smooth transition, I have used these programs: WindowAPI and maximize.
The written code looks like this:
logoh = splash('logo.jpg'); %# Appear the logo image
fh = figure('Visible','on','Name','MyGUI','Position',[-1000,-1000,...
1000,500],'Menu','none','Toolbar','none','NumberTitle','off');
%# Put the figure window outside the screen (see Position property) because
%# its visibility is on
WindowAPI(fh,'Alpha',0); %# Make the figure window invisible
...
movegui(fh,'center'); %# Move the figure window to center
maximize(fh);% Maximize it
WindowAPI(fh,'Alpha',1); %# Make the figure window visible totally
pause(2); %# time during which the logo image is exposed
splash(logoh,'off'); %# Disappear the logo image

Resources