On this page

Qt for HarmonyOS Window Settings Example

Changes platform settings of the windows of a Qt application on HarmonyOS.

Each window of the example changes settings of its own platform window, the window.Window that the window is shown in. All windows can keep the screen on and hide their content in screenshots and screen recordings. Sub windows can also have rounded corners and be resized by dragging their edges.

The settings are changed by methods of window.Window: setWindowKeepScreenOn(), setWindowPrivacyMode(), setWindowCornerRadius() and enableDrag(). The corner radius is measured in vp. A window gets its window.Window from QNativeInterface::QHarmonyWindow on the Qt thread, as a QHarmonyJsObjectRef handle. Each control hands the handle and the new value to the JS thread:

QHarmonyJsObjectRef WindowSettingsWindow::jsWindow() const
{
    auto *harmonyWindow = windowHandle() ? windowHandle()->nativeInterface<QNativeInterface::QHarmonyWindow>() : nullptr;
    return harmonyWindow ? harmonyWindow->jsWindow() : QHarmonyJsObjectRef();
}

void WindowSettingsWindow::setKeepScreenOn(bool keepScreenOn)
{
    QHarmonyApplication::runOnJsThread(
        [jsWindowRef = jsWindow(), keepScreenOn, log = QPointer<QTextEdit>(m_log)](QHarmonyJsContext &jsContext) {
            callJsWindowSetter(jsContext, jsWindowRef, "setWindowKeepScreenOn", Napi::Boolean::New(jsContext.env(), keepScreenOn), log);
        });
}

void WindowSettingsWindow::setPrivacyMode(bool privacyMode)
{
    QHarmonyApplication::runOnJsThread(
        [jsWindowRef = jsWindow(), privacyMode, log = QPointer<QTextEdit>(m_log)](QHarmonyJsContext &jsContext) {
            callJsWindowSetter(jsContext, jsWindowRef, "setWindowPrivacyMode", Napi::Boolean::New(jsContext.env(), privacyMode), log);
        });
}

void WindowSettingsWindow::setCornerRadius(double cornerRadius)
{
    QHarmonyApplication::runOnJsThread(
        [jsWindowRef = jsWindow(), cornerRadius, log = QPointer<QTextEdit>(m_log)](QHarmonyJsContext &jsContext) {
            callJsWindowSetter(jsContext, jsWindowRef, "setWindowCornerRadius", Napi::Number::New(jsContext.env(), cornerRadius), log);
        });
}

void WindowSettingsWindow::setDragResizable(bool dragResizable)
{
    QHarmonyApplication::runOnJsThread(
        [jsWindowRef = jsWindow(), dragResizable, log = QPointer<QTextEdit>(m_log)](QHarmonyJsContext &jsContext) {
            callJsWindowSetter(jsContext, jsWindowRef, "enableDrag", Napi::Boolean::New(jsContext.env(), dragResizable), log);
        });
}

On the JS thread, the task dereferences the handle and calls the method. Each of the methods returns a promise. The handlers attached to the promise log the outcome in the window. They post the text to the Qt thread with QNativeInterface::QHarmonyApplication::runOnQtThread(). An error that the call throws is logged in the same way:

void showStatusInLog(QPointer<QTextEdit> log, QString text)
{
    QHarmonyApplication::runOnQtThread(
        [log, text]() {
            if (log)
                log->append(text);
        });
}

void showFailureInLog(QPointer<QTextEdit> log, const QString &asyncCallName, const QString &errorText)
{
    showStatusInLog(log, QStringLiteral("%1 failed: %2").arg(asyncCallName, errorText));
}

void attachPromiseHandlersReportingStatusInLog(Napi::Object asyncCallPromise, const QString &asyncCallName, QPointer<QTextEdit> log)
{
    asyncCallPromise.Get("then").As<Napi::Function>().Call(
        asyncCallPromise,
        {
            Napi::Function::New(
                asyncCallPromise.Env(),
                [log, asyncCallName](const Napi::CallbackInfo &) {
                    showStatusInLog(log, QStringLiteral("%1 succeeded").arg(asyncCallName));
                }),
            Napi::Function::New(
                asyncCallPromise.Env(),
                [log, asyncCallName](const Napi::CallbackInfo &info) {
                    showFailureInLog(log, asyncCallName, QString::fromStdString(info[0].ToString()));
                }),
        });
}

void callJsWindowSetter(
    QHarmonyJsContext &jsContext, const QHarmonyJsObjectRef &jsWindowRef, const std::string &setterName,
    Napi::Value argument, QPointer<QTextEdit> log)
{
    const QString asyncCallName = QStringLiteral("%1()").arg(QString::fromStdString(setterName));
    Napi::Env env(jsContext.env());
    Napi::Object jsWindow(env, jsWindowRef.value(jsContext));
    if (jsWindow.IsEmpty()) {
        showFailureInLog(log, asyncCallName, QStringLiteral("the window is not shown in a window.Window"));
        return;
    }

    try {
        auto setterFunc = jsWindow.Get(setterName).As<Napi::Function>();
        auto setterPromise = setterFunc.Call(jsWindow, {argument}).As<Napi::Object>();
        attachPromiseHandlersReportingStatusInLog(setterPromise, asyncCallName, log);
    } catch (const Napi::Error &error) {
        showFailureInLog(log, asyncCallName, QString::fromUtf8(error.what()));
    }
}

setWindowPrivacyMode() requires the ohos.permission.PRIVACY_WINDOW permission. The example declares it in its CMakeLists.txt with qt_add_harmonyos_permission():

qt_add_harmonyos_permission(harmonywindowsettings
    NAME ohos.permission.PRIVACY_WINDOW
)

The controls show the current settings of the platform window. getWindowProperties() and getWindowCornerRadius() return them synchronously, so the example reads them with QNativeInterface::QHarmonyApplication::runOnJsThreadAndWait(), which returns the result of the task:

struct PlatformWindowSettings
{
    std::optional<bool> optKeepScreenOn;
    std::optional<bool> optPrivacyMode;
    std::optional<double> optCornerRadius;
    QString errorText;
};

PlatformWindowSettings readPlatformWindowSettings(QHarmonyJsContext &jsContext, const QHarmonyJsObjectRef &jsWindowRef, bool subWindow)
{
    Napi::Env env(jsContext.env());
    Napi::Object jsWindow(env, jsWindowRef.value(jsContext));
    if (jsWindow.IsEmpty()) {
        return PlatformWindowSettings{
            .errorText = QStringLiteral("the window is not shown in a window.Window"),
        };
    }

    PlatformWindowSettings settings;
    try {
        auto getWindowPropertiesFunc = jsWindow.Get("getWindowProperties").As<Napi::Function>();
        auto windowProperties = getWindowPropertiesFunc.Call(jsWindow, {}).As<Napi::Object>();
        settings.optKeepScreenOn = windowProperties.Get("isKeepScreenOn").As<Napi::Boolean>().Value();
        settings.optPrivacyMode = windowProperties.Get("isPrivacyMode").As<Napi::Boolean>().Value();
        if (subWindow) {
            auto getWindowCornerRadiusFunc = jsWindow.Get("getWindowCornerRadius").As<Napi::Function>();
            settings.optCornerRadius = getWindowCornerRadiusFunc.Call(jsWindow, {}).As<Napi::Number>().DoubleValue();
        }
    } catch (const Napi::Error &error) {
        settings.errorText = QString::fromUtf8(error.what());
    }
    return settings;
}

The example reads the settings each time the window is shown, once it is exposed, as the platform window is shown by then. The platform does not report whether a window can be resized by dragging its edges, so the example applies the initial state of that check box instead:

void WindowSettingsWindow::showEvent(QShowEvent *event)
{
    QWidget::showEvent(event);
    windowHandle()->installEventFilter(this);
}

bool WindowSettingsWindow::eventFilter(QObject *watched, QEvent *event)
{
    if (watched == windowHandle() && event->type() == QEvent::Expose && windowHandle()->isExposed()) {
        windowHandle()->removeEventFilter(this);
        syncWithPlatformWindow();
    }
    return QWidget::eventFilter(watched, event);
}

void WindowSettingsWindow::syncWithPlatformWindow()
{
    const QHarmonyJsObjectRef jsWindowRef = jsWindow();
    const bool subWindow = isSubWindow();
    const PlatformWindowSettings settings = QHarmonyApplication::runOnJsThreadAndWait(
        [&](QHarmonyJsContext &jsContext) {
            return readPlatformWindowSettings(jsContext, jsWindowRef, subWindow);
        });

    if (settings.optKeepScreenOn) {
        const QSignalBlocker blocker(m_keepScreenOnCheckBox);
        m_keepScreenOnCheckBox->setChecked(*settings.optKeepScreenOn);
    }
    if (settings.optPrivacyMode) {
        const QSignalBlocker blocker(m_privacyModeCheckBox);
        m_privacyModeCheckBox->setChecked(*settings.optPrivacyMode);
    }
    if (settings.optCornerRadius) {
        const QSignalBlocker blocker(m_cornerRadiusSpinBox);
        m_cornerRadiusSpinBox->setValue(*settings.optCornerRadius);
    }
    if (!settings.errorText.isEmpty())
        m_log->append(QStringLiteral("reading the window settings failed: %1").arg(settings.errorText));

    if (subWindow)
        setDragResizable(m_dragResizableCheckBox->isChecked());
}

Running the Example

Press New sub window to open a sub window. Change its corner radius, or clear Resizable by dragging its edges and try to resize the sub window by dragging an edge.

To check Keep the screen on, set a short screen timeout in the system settings. While the option is set and the window is shown, the screen stays on.

To check Hide the content in screenshots and recordings, set the option and take a screenshot. The content of the window does not appear in it.

The corner radius and resizing by dragging depend on the device. On a device that does not support them, the calls fail and the window logs the error.

Example project @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.