// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include #include #include #include #include #include #include #include QList> cleanupFunctions; #if defined(Q_OS_MACOS) //! [macos] #include #include QWindow *createCalendarWindow() { auto *datePicker = [NSDatePicker new]; cleanupFunctions.push_back([=]{ [datePicker release]; }); datePicker.datePickerStyle = NSDatePickerStyleClockAndCalendar; datePicker.datePickerElements = NSDatePickerElementFlagYearMonthDay; datePicker.drawsBackground = YES; datePicker.dateValue = [NSDate now]; auto *calendarWindow = QWindow::fromWinId(WId(datePicker)); calendarWindow->setMinimumSize(QSizeF::fromCGSize(datePicker.fittingSize).toSize()); return calendarWindow; } //! [macos] #elif defined(QT_PLATFORM_UIKIT) //! [ios] #include QWindow *createCalendarWindow() { auto *datePicker = [UIDatePicker new]; cleanupFunctions.push_back([=]{ [datePicker release]; }); datePicker.datePickerMode = UIDatePickerModeDate; datePicker.preferredDatePickerStyle = UIDatePickerStyleInline; datePicker.backgroundColor = UIColor.systemBackgroundColor; auto *calendarWindow = QWindow::fromWinId(WId(datePicker)); calendarWindow->setMinimumSize(QSizeF::fromCGSize(datePicker.frame.size).toSize()); return calendarWindow; } //! [ios] #elif defined(Q_OS_WIN) //! [windows] #include #include QWindow *createCalendarWindow() { static bool initializedDateControl = []{ INITCOMMONCONTROLSEX icex; icex.dwSize = sizeof(icex); icex.dwICC = ICC_DATE_CLASSES; return InitCommonControlsEx(&icex); }(); Q_ASSERT(initializedDateControl); HWND monthCalendar = CreateWindow(MONTHCAL_CLASSW, nullptr, MCS_NOTODAYCIRCLE | MCS_NOTODAY, 0, 0, 0, 0, nullptr, nullptr, GetModuleHandle(nullptr), nullptr); cleanupFunctions.push_back([=]{ DestroyWindow(monthCalendar); }); auto *calendarWindow = QWindow::fromWinId(WId(monthCalendar)); RECT minimumSize; MonthCal_GetMinReqRect(monthCalendar, &minimumSize); const auto dpr = calendarWindow->devicePixelRatio(); calendarWindow->setMinimumSize(QSize( minimumSize.right / dpr,minimumSize.bottom / dpr)); return calendarWindow; } //! [windows] #elif defined(Q_OS_ANDROID) //! [android] Q_DECLARE_JNI_CLASS(CalendarView, "android/widget/CalendarView") Q_DECLARE_JNI_CLASS(Color, "android/graphics/Color") QWindow *createCalendarWindow() { using namespace QtJniTypes; using namespace QNativeInterface; auto *androidApp = qGuiApp->nativeInterface(); Q_ASSERT(androidApp); auto *calendarView = new CalendarView(androidApp->context()); cleanupFunctions.push_back([=]{ delete calendarView; }); // Resolving Android default colors is not trivial, so let's ask Qt QColor paletteColor = qGuiApp->palette().color(QPalette::Window); int backgroundColor = Color::callStaticMethod("rgb", paletteColor.red(), paletteColor.green(), paletteColor.blue()); calendarView->callMethod("setBackgroundColor", backgroundColor); auto *calendarWindow = QWindow::fromWinId(WId(calendarView->object())); calendarWindow->setMinimumSize(QSize(200, 220)); return calendarWindow; } //! [android] #elif QT_CONFIG(xcb) && __has_include() QT_REQUIRE_CONFIG(glib); //! [x11] #include #include QWindow *createCalendarWindow() { static bool initializedGTK = []{ qputenv("GDK_BACKEND", "x11"); return gtk_init_check(nullptr, nullptr); }(); Q_ASSERT(initializedGTK); auto *plug = gtk_plug_new(0); g_signal_connect(GTK_WIDGET(plug), "delete-event", G_CALLBACK(+[]{ return true; // Don't destroy on close }), nullptr); cleanupFunctions.push_back([=]{ gtk_widget_destroy(GTK_WIDGET(plug)); }); auto *calendar = gtk_calendar_new(); gtk_container_add(GTK_CONTAINER(plug), GTK_WIDGET(calendar)); gtk_widget_show_all(plug); auto *calendarWindow = QWindow::fromWinId(gtk_plug_get_id(GTK_PLUG(plug))); GtkRequisition minimumSize; gtk_widget_get_preferred_size(calendar, &minimumSize, NULL); calendarWindow->setMinimumSize(QSize(minimumSize.width, minimumSize.height)); return calendarWindow; } //! [x11] #elif defined(Q_OS_WASM) //! [webassembly] #include #include using emscripten::val; using emscripten::EM_VAL; EM_JS(EM_VAL, createCalendarElement, (), { var calendar = document.createElement("calendar-date"); calendar.innerHTML = ""; return Emval.toHandle(calendar); }); QWindow *createCalendarWindow() { static bool initializedCalendarComponent = []{ return EM_ASM_INT( var script = document.createElement('script'); script.src = "https://unpkg.com/cally"; script.type = "module"; document.head.appendChild(script); return true; ); }(); Q_ASSERT(initializedCalendarComponent); val *calendarElement = new val(val::take_ownership(createCalendarElement())); cleanupFunctions.push_back([calendarElement]{ delete calendarElement; }); QWindow *window = QWindow::fromWinId(WId(calendarElement)); window->setMinimumSize(QSize(250, 300)); return window; } //! [webassembly] #else class GrayWindow : public QRasterWindow { protected: void paintEvent(QPaintEvent *) override { QPainter painter(this); QRectF rect(0, 0, width(), height()); painter.fillRect(rect, Qt::gray); painter.drawText(rect, Qt::AlignCenter, "Qt fallback\nforeign window"); } }; QWindow *createCalendarWindow() { #warning Using Qt fallback implementation of foreign window auto *calendarWindow = new GrayWindow; calendarWindow->setMinimumSize(QSize(150, 150)); return calendarWindow; } #endif constexpr QMargins contentsMargins = {20, 20, 20, 20}; //! [qt-gui-container-window] class ContainerWindow : public QRasterWindow { protected: bool event(QEvent *event) override { if (event->type() == QEvent::ChildWindowAdded) { auto *childWindow = static_cast(event)->child(); childWindow->resize(childWindow->minimumSize()); setMinimumSize(childWindow->size().grownBy(contentsMargins)); resize(minimumSize()); } return QRasterWindow::event(event); } void showEvent(QShowEvent *) override { findChild()->setVisible(true); } void resizeEvent(QResizeEvent *) override { auto *containedWindow = findChild(); containedWindow->setPosition( (width() / 2) - containedWindow->width() / 2, (height() / 2) - containedWindow->height() / 2 ); } void paintEvent(QPaintEvent *) override { QPainter painter(this); painter.fillRect(0, 0, width(), height(), "#00414A"); } }; //! [qt-gui-container-window] int main(int argc, char* argv[]) { QApplication app(argc,argv); QCoreApplication::setOrganizationName("QtProject"); QCoreApplication::setOrganizationDomain("qt-project.org"); QCoreApplication::setApplicationName("Window Embedding"); QShortcut quitShortcut(QKeySequence::Quit, &app, &app, &QCoreApplication::quit, Qt::ApplicationShortcut); qAddPostRoutine([]{ for (auto cleanupFunction : cleanupFunctions) cleanupFunction(); }); //! [qt-gui] ContainerWindow window; window.setTitle("Qt Gui"); auto *calendarWindow = createCalendarWindow(); calendarWindow->setParent(&window); //! [qt-gui] //! [qt-widgets] QWidget widget; widget.setPalette(QColor("#CDB0FF")); widget.setWindowTitle("Qt Widgets"); widget.setLayout(new QVBoxLayout); widget.layout()->setContentsMargins(contentsMargins); widget.layout()->setAlignment(Qt::AlignCenter); auto *calendarWidget = QWidget::createWindowContainer(createCalendarWindow()); widget.layout()->addWidget(calendarWidget); //! [qt-widgets] //! [qt-quick] QQmlApplicationEngine engine; engine.setInitialProperties({{ "calendarWindow", QVariant::fromValue(createCalendarWindow()) }}); engine.loadFromModule("windowembedding", "Main"); //! [qt-quick] auto &quickWindow = *qobject_cast(engine.rootObjects().first()); auto positionWindows = [&]{ auto screenCenter = app.primaryScreen()->availableGeometry().center(); widget.adjustSize(); auto top = screenCenter.y() - widget.height() / 2; widget.setGeometry(screenCenter.x() - widget.width() / 2, top, widget.width(), widget.height()); window.setPosition(QPoint(widget.geometry().left() - window.width() - contentsMargins.left(), top)); quickWindow.setPosition(QPoint(widget.geometry().right() + contentsMargins.right(), top)); }; // Handle mobile devices by dynamically adjusting to the screen geometry QObject::connect(app.primaryScreen(), &QScreen::availableGeometryChanged, positionWindows); positionWindows(); widget.showNormal(); window.showNormal(); quickWindow.showNormal(); return app.exec(); }