How can I create multiple windows in winui 3.0? - c++

I would like to have a button that creates another window. I'm building off the template app you get in VS2019 Preview So far, I've created a new control BlankWindow, just the default one. I can see that in App::OnLaunched, a window is created with:
window = make<MainWindow>();
window.Activate();
So in my MainWindow.xaml.cs in my button click method, I put:
Window bWindow= make<BlankWindow>();
bWindow.Activate();
That doesn't build, I check the BlankWindow.idl file and see that BlankWindow inherits from Controls and not Windows, so I change it. Now it builds, but when I click the button in the MainWindow, I see the window flicker open and instantly close. What's going on?

Work on multi-window applications is ongoing. It's available as a Preview feature.
https://learn.microsoft.com/en-us/windows/apps/winui/winui3/#preview-features

You can find the solution to this problem in the definition of the Window Class for the Windows UI Library (WinUI) under the subtitle "Create a new Window on a new thread". To put the section in context it would help to read the entire webpage. It's not that long.

We were able to include xaml.h for the target subwindow and display the window using the make template function.
Note that the types held in the member variables and the types specified to the make template function are different.
(I am using the translation at DeepL, so the text may be slightly incorrect.)
#include "pch.h"
#include "MainWindow.xaml.h"
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#include "Window_Sub.xaml.h"
#endif
winrt::App1::Window_Sub _subWindow{ nullptr };
void MainWindow::ShowSubWindowButton_Clicked(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
if (_subWindow == nullptr) {
_subWindow = make<App1::implementation::Window_Sub>();
_subWindow.Closed([](winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::WindowEventArgs const& e){
_subWindow = nullptr;
});
}
_subWindow.Activate();
}

Related

how do I call primary window using pushbutton c++

I'm trying to create a C++ Widget Application in QT with multiple windows where you can return to the mainwindow by clicking a Pushbutton.
I'm a complete noobie so I try to follow YouTube tutorial and for opening windows by pushbuttons I watched this one (minute 8:00): https://youtu.be/tP70B-pdTH0
It works when opening secondary windows from the main one but if I try to do the same from a secondary windows to the mainwindow it doesn't. It appears an error "cannot initialize object parameter of type 'Widget' with an expression of type 'MainWindow'"
in the source file I wrote:
void Crediti::on_pushButton_clicked()
{
close();
mainwindow = new MainWindow(this);
mainwindow->show();
}
mainwindow->show(); is the incriminated part
I also included mainwindow in the header of the secondary window and specified the class
MainWindow *mainwindow
in the header so It recognizes mainwindow initially in the source.
I'm doubting if doing this thing is possible at all, and if not so how can I make a pushbutton that, when clicked, can redirect me to the mainwindow?
Please I need this for a school application, thanks
So here you're creating a new main window each time you click on the button. From your description that's not the behaviour you want. I understand you have an application with a main window and other secondary windows and want to bring up the main window when clicking on the button, assuming the main window still exists somewhere and hasn't been deleted.
What I would try is to find the main window when hitting the push button and show / raise it, something along the line of:
#include <QApplication>
#include "MainWindow.h" // Adapt that one to you main window header
// ... some code of your secondary window
void SecondaryWindow::on_pushButton_clicked()
{
for(auto widget : QApplication::topLevelWidgets())
{
// This will return a nullptr if the widget is not the main window
auto mainWindow = dynamic_cast<MainWindow*>(widget);
// skip if not the main window
if(!mainWindow)
continue;
// Show it if hidden
if(mainWindow->isHidden())
mainWindow->show();
// raise it, as in bring it forward, over all other windows
mainWindow->raise();
}
// eventually close the current window if that's what you want
close();
// if you close it and don't need it any more you might also want to delete it
deleteLater();
}
Note that this function won't do anything if the main window has been deleted in the meantime, which might be the case if you closed it and the Qt::WA_DeleteOnClose attribute is set.
Hope that helps.

JUCE - Making a New Window

Coming from making single-page applications with the visual WYSISWYG editor in JUCE, I'm having a bit of trouble figuring out how to invoke new windows (outside of the main GUI window). I made a test application that just has a small minimal main GUI that I created with the visual editor. It has a button "Make New Window." My goal is to be able to click that button and have a new window pop up and that this new window is a JUCE "GUI component," (AKA, the graphical / visual GUI editor file). Now, I actually have sort of achieved this, however, its throwing errors and assertions, so it would be great to get a very simple, step-by-step tutorial.
I studied the main.cpp file that the Projucer automatically created in order to get a feel for how they are creating a window. Here's what I did.
1) In my project, I added a new GUI Component (which becomes a class) and called it "InvokedWindow."
2) In my main GUI component class header, I added a new scoped pointer of type InvokedWindow: ScopedPointer<InvokedWindow> invokedWindow;
3) I created a new button in the main GUI editor called "Make New Window" and added this to the handler code:
newWindowPtr = new InvokedWindow; so that any time the button is hit, a new object of type InvokedWindow is created.
4) In the InvokedWindow class, in the constructor, on top of the automatically generated code, I added:
setUsingNativeTitleBar (true);
setCentrePosition(400, 400);
setVisible (true);
setResizable(false, false);
Which I sort of got from the main file of the JUCE application.
I also added a slider to this new window just to add functionality to it.
5) I added an overloaded function to let me close the window:
void InvokedWindow::closeButtonPressed()
{
delete this;
}
So, now when I run the app and click the make new window button, a new window does pop up, but I get an assertion:
/* Agh! You shouldn't add components directly to a ResizableWindow - this class
manages its child components automatically, and if you add your own it'll cause
trouble. Instead, use setContentComponent() to give it a component which
will be automatically resized and kept in the right place - then you can add
subcomponents to the content comp. See the notes for the ResizableWindow class
for more info.
If you really know what you're doing and want to avoid this assertion, just call
Component::addAndMakeVisible directly.
*/
Also, I'm able to close the window once and hit the button in the main GUI to create another instance of a newWindow, but closing it a second time leads to an error:
template <typename ObjectType>
struct ContainerDeletePolicy
{
static void destroy (ObjectType* object)
{
// If the line below triggers a compiler error, it means that you are using
// an incomplete type for ObjectType (for example, a type that is declared
// but not defined). This is a problem because then the following delete is
// undefined behaviour. The purpose of the sizeof is to capture this situation.
// If this was caused by a ScopedPointer to a forward-declared type, move the
// implementation of all methods trying to use the ScopedPointer (e.g. the destructor
// of the class owning it) into cpp files where they can see to the definition
// of ObjectType. This should fix the error.
ignoreUnused (sizeof (ObjectType));
delete object;
}
};
This is all a bit over my head. I was figuring it wouldn't be too bad to be able to create a new window, via a button. A new window that I could edit with the graphical GUI editor, but I'm not able to fully figure it out all on my own, through I did try. Could anyone post a step-by-step guide to doing this the correct way? I did post this at the JUCE forums, but due to my lack of GUI programming, I was unable to understand the solutions posted (my own fault), so I'm hoping to get a very simple guide to this. It would be very much appreciated. Thank you.
I figured it out. I needed to create:
A new GUI component (Remember, this is the visual editor in JUCE)
A class (I called it BasicWindow, based on the JUCE demo code) that acts as a shell to run this new window and holds the GUI component.
A JUCE SafePointer that makes a new object of type BasicWindow whenever the button is clicked and sets the attributes to that window.
Here is my code:
Referring to line 3) Inside the handler section of the button to create the new window:
basicWindow = new BasicWindow("Information", Colours::grey, DocumentWindow::allButtons);
basicWindow->setUsingNativeTitleBar(true);
basicWindow->setContentOwned(new InformationComponent(), true);// InformationComponent is my GUI editor component (the visual editor of JUCE)
basicWindow->centreWithSize(basicWindow->getWidth(), basicWindow->getHeight());
basicWindow->setVisible(true);
Referring to line 2) A .cpp file that defines what the BasicWindow is:
#include "../JuceLibraryCode/JuceHeader.h"
class BasicWindow : public DocumentWindow
{
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BasicWindow)
public:
BasicWindow (const String& name, Colour backgroundColour, int buttonsNeeded)
: DocumentWindow (name, backgroundColour, buttonsNeeded)
{
}
void closeButtonPressed() override
{
delete this;
}
};
And referring to line 1) Make the GUI editor component, which this is easy to do. You just right add a new file in the JUCE file manager. "Add New GUI Component," then visually add all your elements and handler code.
My biggest issue was that I was using a JUCE ScopedPointer, so after deleting the object, the pointer pointing to it wasn't being set back to NULL. The SafePointer does this. If any more explanation is needed, I'm happy to help, as this was terrible for someone with not much GUI development "under his belt."

Restoring Qt app after minimized to dock, using dock shortcut on OS X

Single-clicking an application icon in the Dock launches that application or, if the application is already open, switches you to that application and brings forward all open windows in that application.
In my Qt app, I do not see this behavior. I have a dock shortcut, I minimize the app on dock, and if I click the minimized instance the app is restored.
If I click the application icon in the Dock (dock shortcut), nothing happens.
(I can right-click, and "Show All Windows" - but that is not similar to the behavior of all other apps)
I tried to place the code from this answer: https://stackoverflow.com/a/15363738/1217150
Placing qDebug statements,
if(test) qDebug("registered");
in the constructor, and
void MyApplictionClass::onClickOnDock()
{
qDebug("dock clicked");
}
On start-up I get both messages... but after minimizing, or any time after start-up, there is no reaction to dock shortcut being clicked or double clicked.
Full code insertion:
MyApplicationClass.h:
#include <objc/objc.h>
#include <objc/message.h>
class MyApplicationClass: public QApplication
{
....
bool dockClickHandler(id self, SEL _cmd, ...);
void onClickOnDock();
};
MyApplicationClass.cpp:
MyApplicationClass::MyApplicationClass()
{
....
objc_object* cls = objc_getClass("NSApplication");
SEL sharedApplication = sel_registerName("sharedApplication");
objc_object* appInst = objc_msgSend(cls, sharedApplication);
if(appInst != NULL)
{
objc_object* delegate = objc_msgSend(appInst, sel_registerName("delegate"));
objc_object* delClass = objc_msgSend(delegate, sel_registerName("class"));
const char* tst = class_getName(delClass->isa);
bool test = class_addMethod((objc_class*)delClass,
sel_registerName("applicationShouldHandleReopen::hasVisibleWindows:"),
(IMP)dockClickHandler(appInst, sharedApplication), "B#:");
if(!test) qDebug("not registered");
else qDebug("registered");
}
.....
}
bool MyApplicationClass::dockClickHandler(id self, SEL _cmd, ...)
{
Q_UNUSED(self)
Q_UNUSED(_cmd)
onClickOnDock();
return true;
}
void MyApplicationClass::onClickOnDock()
{
qDebug("dock clicked");
}
Perhaps I did something wrong ? There are a few items I don't understand... The referred post had dockClickHandler not part of the class... I just don't know where they have it... And I had to give it arguments when I registered it, otherwise it refused to build. Still, it seems to do... something... just not the right thing. In that code, I do not see where the app will react to clicking the dock.
Is this the correct approach ? Or how can I get my app to restore when its dock shortcut is clicked ?
(OSX 10.6-10.9, Qt 4.8)
If you are refering to this: QT on OS X, how to detect clicking the app Dock Icon, the onClickOnDock should not be a member of the class.
You CAN put it as a normal function inside the MyApplicationClass.cpp file.
There is a hack at least for Qt 5.9.1, when you can implement onClickOnDock() without platform specific code at all. See https://stackoverflow.com/a/46488514/8695355

Qt: Can't set drag mode

I've been stuck on this for a little while now, I'm trying to set the drag mode of my QGraphicsView to ScrollHandDrag so that I can build a panning feature on my application.
However whenever I try to set the drag mode Qt always complains that DragMode is an undeclared identifier.
I'm also aiming to build a crop functionality (I assume I'd be using rubber band dragging for that?), I'm just wondering why I can't set the drag mode on the View
void MainWindow::on_btnCrop_clicked()
{
cropping = true;
QApplication::setOverrideCursor(Qt::CrossCursor);
// Stuck with this...
ui->imageView->setDragMode(ScrollHandDrag);
}
^ I have tried multiple other workarounds but I've not yet found any solution, any suggestions would be greatly appreciated.
This is not QGraphicsView specific an issue, but generic C++. Your problem is located at this line:
ui->imageView->setDragMode(ScrollHandDrag);
The problem is that you assume that you have visibility to the ScrollHandDrag value, whereas it appears inside the QGraphicsView scope. Therefore, since you are trying to access that value in your MainWindow, you will need add the scope explicitly as follows:
ui->imageView->setDragMode(QGraphicsView::ScrollHandDrag);
Note that how even the documentation specifies the scope for this constant:
QGraphicsView::ScrollHandDrag 1 The cursor changes into a pointing hand, and dragging the mouse around will scroll the scrolbars. This mode works both in interactive and non-interactive mode.
Here is my minimal building code:
#include <QGraphicsView>
int main()
{
QGraphicsView graphicsView;
graphicsView.setDragMode(QGraphicsView::ScrollHandDrag);
return 0;
}
main.pro
TEMPLATE = app
TARGET = main
QT += widgets
SOURCES += main.cpp
Build and Run
qmake && make

Sharing variable among class instances

class MyApp : public CWinApp {
afx_msg OnPrefrences();
};
OnPrefrences() get called when user selects tools->Preference from the menubar.
Now in one dialog(Say DlgX) there is one button, on clicking this I need to open the Preference dialog which has in fact many panes, but here I need to open the Preference dialog by selecting one the these pane as active. Also in that particular pane I need to hide some of the controls only when It gets open through this the dialog not through menu.
So I have created one variable(Say m_varX) in MainFrm class.
void DlgX::OnButtonXClick()
{
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
if(pFrame)
{
pFrame->m_varX = TRUE;
((CMyApp*)(AfxGetApp()))->OnPrefrences();
pFrame->m_varX = FALSE;
}
}
And in button handler of DlgX I have made this m_varX TRUE and call the OnPreference() and after closing of this preference dialog I have made m_varX FALSE.
All this is working fine... But the problem is that things gets clutter in mainFrm. Also the project I am working on is legacy one so I cant make much changes.
Is there any patter available for handling such case?
Thanks
You could solve this with a custom dialog (if you don't have it already)
When you show the dialog from the main menu i.e. onPreferences() you fill and show all 'panes'. you would have to do a custom dialog where the ctor takes some arguments.
E.g.
enum { all, part };
void MainFrame::OnPreferences()
{
CMyPreferences dlg( GetDocument(), all );
dlg.DoModal();
}
but when you call it from within a dialog you only fill in the parts you need.
void YourDialog::OnPreferences()
{
CMyPreferences dlg( GetDocument(), part );
dlg.doModal();
}
The argument could be something more sophisticated for more fine tuned configuration of what to show/allow to edit.
I think for that special case, even if sometimes is no more considered a pattern, the singleton pattern would work for you.