UWP app with multiple views with different sizes - windows

I have a UWP app. I want to be ale to create new windows from the app, but any subsequent window needs to be a specific smaller size compared to the main window. I don't do anything with regards to sizing the main window and let the OS take care of sizing it for me.
I bring up a new window like this:
auto window = CoreApplication::CreateNewView();
window->show();
void NewWindow::show() {
auto currView = ApplicationView::GetForCurrentView();
currView->PreferredLaunchViewSize =
Windows::Foundation::Size(float(options.width), float(options.height));
currView->PreferredLaunchWindowingMode = ApplicationViewWindowingMode::PreferredLaunchViewSize;
currView->SetPreferredMinSize(Size(20,20));
Xaml::Window::Current->Activate();
ApplicationViewSwitcher::TryShowAsStandaloneAsync(
window_->id(),
ViewSizePreference::Default,
window_->parentId(),
ViewSizePreference::Default);
}
When the main window comes up, it comes up just fine. When I click on a button which gets to this function, the new window comes up in the same size as the main window. I reopen the app, but now the main window shows up in the size I wanted the new window to show up in. Then clicking the button to bring up the new window brings up the new window in the size I wanted it in. So I am a bit confused. Am I setting the sizes the right way? Is there anything else glaringly wrong here?

As # Raymond Chen said, PreferredLaunchViewSize set the size when the app launches, it will change the size of your main window. And you can use ApplicationView.TryResizeView method to set the size of new window.
For example:
auto parentView = ApplicationView::GetForCurrentView();
auto newView = CoreApplication::CreateNewView();
newView->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal,
ref new Windows::UI::Core::DispatchedHandler([this,parentView]()
{
int newViewId = 0;
Windows::UI::Xaml::Controls::Frame^ rootFrame = ref new Windows::UI::Xaml::Controls::Frame();
rootFrame->Navigate(Windows::UI::Xaml::Interop::TypeName(MainPage::typeid), nullptr);
Window::Current->Content = rootFrame;
// You have to activate the window in order to show it later.
Window::Current->Activate();
newViewId = ApplicationView::GetForCurrentView()->Id;
IAsyncOperation<bool>^ mytask = ApplicationViewSwitcher::TryShowAsStandaloneAsync(newViewId);
auto deviceEnumTask = concurrency::create_task(mytask);
deviceEnumTask.then([this](bool res)
{
// set the size of new window
ApplicationView::GetForCurrentView()->TryResizeView(Size(600, 320));
});
}));

Related

Dialog window positioning

I'm creating a small non-modal dialog in TornadoFX like this:
find<Grib>(scope).apply { openModal(block = true,
owner = FX.primaryStage,
stageStyle = StageStyle.UTILITY,
modality = Modality.NONE) }
How do I go about setting (and retrieving) it's window position for later? That is, I have a preferences object for the window location and I want to update it so that the next time the user opens the window, it opens in the same place they last closed it.
I was able to mostly solve my problem by digging through some of the TornadoFX source code.
I added this to my init{} function:
Platform.runLater {
root.scene.window.x = Main.preferences.getDouble(GRIB_WINDOW_X, 400.0)
root.scene.window.y = Main.preferences.getDouble(GRIB_WINDOW_Y, 400.0)
}
And then adding this to my close() function:
Main.preferences.putDouble(GRIB_WINDOW_X, root.scene.window.x)
Main.preferences.putDouble(GRIB_WINDOW_Y, root.scene.window.y)
This "mostly" solves the problem in that it does save/restore the window position, however the window flickers when it is created as it moves from some default position to the newly set position.

How can I get a borderless child window to re-scale to current screen in multi-monitor setup?

My app has a main window which creates and opens an instance of a subclass of a QML Window {} using createObject(). This window has its flags: set to be a borderless window (I've added code so that it can be grabbed and dragged around).
When I attach a monitor to my laptop and set its font scale factor to 125% (or 150%), when I drag my main window over to the second monitor, you can see it suddenly "snap" to the larger size when it reaches the halfway point. Likewise, when I drag it back to my laptop screen it again "snaps" to the smaller size when I get halfway over (this behavior is what I want, so no problems here).
My problem is that when I drag my created borderless window over into the monitor, it keeps the original 100% scale factor and does not "snap" to a larger size. If I drag my main window over to the monitor, it gets larger but the borderless window remains at the smaller scale; only when I grab the borderless window and move it slightly does it suddenly "snap" to the larger scale size. The same thing happens in reverse - if I then drag the borderless window back onto the laptop, it remains at the larger size until I drag the main window back over and then move the borderless window slightly (at which point it suddenly "snaps" to the smaller size).
So it appears that this created Window uses the scale factor of the screen that the parent window window that created it is currently in, even if it is in a different screen itself.
Is this happening because the Window is borderless? (I'm about to test this but my build process is incredibly slow) Or is there any way to set this borderless Window up so that it detects that it is crossing into a new screen and re-scales itself (in the same way that my main window does)?
Update: I just ran a test giving my Window a native titlebar, and with a titlebar the window instantly adopts ("snaps to") the scale factor of whichever screen it happens to be in, just like my main window (and independent of the main window's scale factor).
So is there any way to duplicate this auto-scaling window behavior with a borderless window? Some flag I need to call, or some method(s) I need to call to get the OS to rescale the window?
Update 2: I tried out Felix's SetWindowPos solution. It does move the window, but this does not fix the scaling problem - the behavior of the frameless window is the same and it still does not correctly pick up the scaling factor of the screen it is in.
I am running a test using MoveWindow instead of SetWindowPos to see if that affects anything [edit: MoveWindow does not work, either - same problem]. Then I'm going to try SendMessage or PostMessage along with NoBugz' suggestion of the WM_DPICHANGED message.
Any other suggestions are welcome.
Update 3: I just created a quick C# app (winforms) to see if the same problem occurs with that, and it doesn't - when a borderless form in the C# app is dragged over into the other monitor, it immediately picks up the scale factor change. So it appears this is a Qt problem.
Update 4: See my answer below for a working solution to this problem (if a bit of a hack).
So as far as I understand, your current goal is to move the window via the WIN-API.
You will have to do so via C++. The approach would be:
Pass the QML Window to a C++-Method exposed to QML as a QQuickWindow (The QML window instanciates that type, as seen in the documentation)
Use QWindow::winId to get the native HWND
Call the SetWindowPos WIN-API method to move it
Code sample (C++-part):
// the method
void moveWindow(QQuickWindow *window, int x, int y) {
HWND handle = (HWND)window->winId();
Q_ASSERT(handle);
::SetWindowPos(handle, HWND_TOP,
x, y, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
}
// assuming "moveWindow" is a member of "MyClass"
qmlEngine->rootContext()->setContextProperty("mover", new MyClass(qmlEngine));
Code sample (QML-part):
// call this method as soon as the drag has finished, with the new positions
mover.moveWindow(idOfWindow, xPos, yPos);
Note: I would recommend you try out calling this only after the drag was finished (and move the window as you do right now until then). If that works, you can try out what happens if you call this during the drag instead of changing the x/y of the window.
I figured out a relatively simple way to fix this problem. Since a frameless window in Qt gets its scaling factor from the window that created it, the trick is to create another window (that has a titlebar but is not visible to the user) and create the frameless window there, and then add code to the frameless window to keep the hidden window positioned underneath it as the user drags it. When the frameless window is dragged into another screen, the hidden window goes with it, picks up the new scale factor (since it has a titlebar) and then the frameless window immediately gets the new screen's scale factor as well.
Here is sample solution code:
// HiddenWindow.qml
Window {
id: hiddenWindow
// note: just making window visible: false does not work.
opacity: 0
visible: true
flags: Qt.Tool | Qt.WindowTitleHint | Qt.WindowTransparentForInput |
Qt.WindowStaysOnTopHint // Qt.Tool keeps this window out of the
// taskbar
function createVisibleWindow() {
var component = Qt.createComponent("VisibleWindow.qml")
if (component.status === Component.Ready) {
var win = component.createObject(hiddenWindow)
return win
}
}
}
// VisibleWindow.qml
Window {
id: visibleWindow
property var creatorWindow: undefined
flags: Qt.FramelessWindowHint
onXChanged: {
creatorWindow.x = x
}
onYChanged: {
creatorWindow.y = y
}
onWidthChanged: {
creatorWindow.width = width
}
onHeightChanged: {
creatorWindow.height = height
}
}
And then to use these classes from your main window QML:
property var hiddenWindow: undefined
property var visibleWindow: undefined
Component.onCompleted: {
var component = Qt.createComponent("HiddenWindow.qml")
if (component.status === Component.Ready) {
hiddenWindow = component.createObject(null)
}
visibleWindow = hiddenWindow.createVisibleWindow()
visibleWindow.creatorWindow = hiddenWindow
visibleWindow.show()
}
You need to resize window when window move to other screen
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
onPressed: {
movePos = Qt.point(mouse.x, mouse.y)
isDoubleClicked = false
lastWindowWidth = mainWindow.width
lastWindowHeight = mainWindow.height
}
onPositionChanged: {
if (!isDoubleClicked) {
const delta = Qt.point(mouse.x - movePos.x, mouse.y - movePos.y)
if (mainWindow.visibility !== Window.Maximized) {
mainWindow.x = mainWindow.x + delta.x
mainWindow.y = mainWindow.y + delta.y
mainWindow.width = lastWindowWidth
mainWindow.height = lastWindowHeight
}
}
}
}

Save and restore window position of a LibGDX application (LWJGL)

I want to restore the position and size of my LibGDX application (LwjglApplication) window when started again.
I know that I can use LwjglApplicationConfiguration to set the position of the window before creating it as follows:
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.x = lastX;
cfg.y = lasty;
LwjglApplication a = new LwjglApplication(new MyGame(), cfg);
But how can I read the last position of the window before quitting? The user might have moved and resized the window while playing my game. How to save the data?
Thanks a lot!
The only way I found is to use the LWJGL class Display (Javadoc) with methods getX() and getY() as you can see here:
int x = Display.getX();
int y = Display.getY();

Creating X11 window to span multiple displays

I'm having the exact problem described here. How to make X11 window span multiple monitors
I have six monitors and am trying to create a window larger than the size of one of the monitors. It keeps getting resized by the window manager.
Apologize if I should post within that thread, the etiquette is not clear to me.
Anhow, I do the following in my code:
/* Pass some information along to the window manager to size the window */
sizeHints.flags = USSize; // | PMinSize;
sizeHints.width = sizeHints.base_width = width;
sizeHints.height = sizeHints.base_height = height;
// sizeHints.min_width = width;
// sizeHints.min_height = height;
// sizeHints.max_width = mScreenWidth;
// sizeHints.max_height = mScreenHeight;
if (geometry->x != DONT_CARE && geometry->y != DONT_CARE) {
sizeHints.x = geometry->x;
sizeHints.y = geometry->y;
sizeHints.flags |= USPosition;
}
XSetNormalHints(mDisplay, mWindow, &sizeHints);
SetTitle(suggestedName);
XSetStandardProperties(mDisplay, mWindow,
suggestedName.toAscii(), suggestedName.toAscii(),
None, (char **)NULL, 0, &sizeHints);
/* Bring it up; then wait for it to actually get here. */
XMapWindow(mDisplay, mWindow);
The problem I'm having is that if I set min_width and min_height, the user cannot resize the window, which is not what I want. But if I don't, then when I do any X11 call later, such as
XGetWindowAttributes(mDisplay, mWindow, &win_attributes);
the window manager resizes my window to fit into one monitor instead of being larger than the monitor. I cannot just get a window of the desired size for some reason. Note that WidthOfScreen and HeightOfScreen give me the combined width and height of all monitors as expected.
Can anyone help? I hope I'm explaining myself clearly enough.

What does window.close() do to the contents of said window?

I've got the below code:
// Initiate the font choosing lists.
var headerListWindow = Titanium.UI.createWindow();
var headerFontListData = [{title:"Row 1"},{title:"Row 2"},{title:"Row 3"}];
var headerFontList = Titanium.UI.createTableView({data:headerFontListData});
chooseHeader.addEventListener('click', function(e) {
headerListWindow.left=win.width;
var a = Titanium.UI.createAnimation();
a.left = 0;
a.duration = 1000;
headerListWindow.add(headerFontList);
headerListWindow.open(a);
});
headerFontList.addEventListener('click', function(e) {
var a = Titanium.UI.createAnimation();
a.left = win.width;
a.duration = 1000;
headerListWindow.close(a);
});
Which slides a window in nicely. When I open headerListWindow for the first time, everything appears, the list works, and the window slides in nicely. When I close() the window, however, all the list elements' names revert to "R.". Then, upon reopening the window, the list elements are completely gone. Why is this?
.close() kills your window. it's passed on. this window is no more. It has ceased to be. It's expired and gone to meet it's maker. It's a stiff. Bereft of life. It rests in peace... etc.
when your window is closed, then your are destroying it. You might like to try .hide() instead which will not null the window, but just not show it on the screen.
blissapp, I am using the Alloy framework, and if I do a window.close() it does not kill the window. It closes the window, but if I log the window after it's still defined, and its definitely not dead.
I am not sure wether this applies to developing without alloy or not, but the object remains. You do have to re-open the window in order to render it, but it is my experience that the window is not deceased as you put it. I am definitely not destroying it.
An example:
welcome.addEventListener('open', function() {
console.log("welcome is open");
win1.close();
Ti.API.debug(win1);
});
Outputs:
[INFO] : welcome is open
[DEBUG] : [object welcome]
Ofcause Nulling the variable is a solution, but why doesn't .close() work as it is suposed to according the documentation using Alloy? Am I missing something when working in Alloy?

Resources