7

I want to create an application without the title bar, but with native close, minimize and maximize buttons. This is the intent of the layout:

how it would look like on mac

The app is built using Go and QML. I was able to remove the title bars by adding:

flags: Qt.FramelessWindowHint | Qt.Window

But this means that I'm having to recreate all kinds of native behaviors, like window moving and resizing. I'm also recreating the close/minimize/fullscreen buttons by hand, but it means I lose all kinds of native OS behaviour like the window snapping in windows or the zoom option on mac.

Is there a better way to do this? Is it possible at least to create the native max-min-close buttons instead of building it by scratch?

Thanks for all

  • 1
    Native styling is available on Mac OS. I think your question is a sort of duplicate of [this question](http://stackoverflow.com/questions/21770426/in-qt-qml-controls-applicationwindow-lacks-the-native-looking-theme-when-run): the provided answer should apply to your problem. – BaCaRoZzo Mar 05 '15 at 17:49
  • I'm not sure if it applies. The default style has the app title bar AND the colored buttons. I want the coloured buttons without (or with a customised) titlebar. – Alexandre Van de Sande Mar 05 '15 at 21:21
  • Uhm...it does not apply. The default theme is thought to be used "as is". Applying customization removes the default theme. It's like that to minimize inconsistencies. – BaCaRoZzo Mar 05 '15 at 21:23
  • 3
    Not that this helps, but your proposed design would violate the Apple Human Interface Guidelines for Windows. You should have a very good reason (and possibly a degree in design and usability engineering :) for doing this. Something to think about. https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/WindowAppearanceBehavior.html#//apple_ref/doc/uid/20000957-CH33-SW1 – Tod Mar 07 '15 at 21:58

2 Answers2

5

You can use objective-c to setup your window correctly. This might be a little buggy, but I got it working through this (create a separate .mm class):

#include "macwindow.h"
#include <Cocoa.h>

MacWindow::MacWindow(long winid)
{
   NSView *nativeView = reinterpret_cast<NSView *>(winid);
   NSWindow* nativeWindow = [nativeView window];

   [nativeWindow setStyleMask:[nativeWindow styleMask] | NSFullSizeContentViewWindowMask | NSWindowTitleHidden];
   [nativeWindow setTitlebarAppearsTransparent:YES];

   [nativeWindow setMovableByWindowBackground:YES];
}

In your main.cpp you need to pass the window id like this:

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QWindow>
#include "macwindow.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    QWindowList windows = QGuiApplication::allWindows();
    QWindow* win = windows.first();

    MacWindow* mac = new MacWindow(win->winId());

    return app.exec();
}   

In your .pro file you'll need to add the Cocoa reference:

macx:LIBS += -framework Foundation -framework Cocoa
macx:INCLUDEPATH += /System/Library/Frameworks/Foundation.framework/Versions/C/Headers \
/System/Library/Frameworks/AppKit.framework/Headers \
/System/Library/Frameworks/Cocoa.framework/Headers

Not sure why, but I had to add a TextEdit with the focus attribute to get the window drawn correctly, otherwise it appeared just black (my main.qml):

import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.4

ApplicationWindow {
    visible: true
    color: "white"
    width: 600
    height: 400
    minimumWidth: width
    minimumHeight: height
    maximumWidth: width
    maximumHeight: height

    Rectangle {
        anchors.fill: parent
        color: "white"

        TextEdit {
            opacity: 0
            focus: true
        }
    }
}

enter image description here

Eduard
  • 176
  • 2
  • 10
  • It would have been nice if you add "macwindow.h" to your post – ervinbosenbacher Apr 04 '17 at 08:12
  • 1
    I don't have this example on my PC anymore, but the header is just the default header you get, when you create a class (make sure to change the cpp to mm in the class creation wizard). There is only the constructor, no other methods. The constructor has to take a long as parameter. No special header includes or so, thats handled in the mm file. By the way, I found some bugs are fixed, if you use: `QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);` at the beginning of your main. This sets it to software rendering in Qt 5.8 and up. – Eduard Apr 05 '17 at 09:33
0

My solution (based on Eduard's one, adjusted so window is draggable).

(Complete source code https://github.com/teimuraz/qt-macos-without-title)

Create macos.h and macos.mm files (If you create it with qt creator rename macos.cpp to macos.mm)

macos.h

#ifndef MACOS_H
#define MACOS_H

#include <QGuiApplication>
#include <QWindow>

class MacOS {
    MacOS(long winid);
public:
    static void removeTitlebarFromWindow(long winId = -1);
};

#endif // MACOS_H

macos.mm

#include "macos.h"
#include <Cocoa/Cocoa.h>

#include <QGuiApplication>
#include <QWindow>

void MacOS::removeTitlebarFromWindow(long winId)
{
    if(winId == -1) {
        QWindowList windows = QGuiApplication::allWindows();
        QWindow* win = windows.first();
        winId = win->winId();
    }

    NSView *nativeView = reinterpret_cast<NSView *>(winId);
    NSWindow* nativeWindow = [nativeView window];

    [nativeWindow setStyleMask:[nativeWindow styleMask] | NSFullSizeContentViewWindowMask | NSWindowTitleHidden];
    [nativeWindow setTitlebarAppearsTransparent:YES];
    [nativeWindow setMovableByWindowBackground:YES];
}

main.qml

import QtQuick

import QtQuick.Controls

ApplicationWindow {
    id: mainWindow
    visible: true
    width: 640
    height: 480

   // Make windows draggable, currently whole windows area is draggable, but can be adjusted to make draggable only top area
   property int previousX
   property int previousY

    MouseArea {
        anchors {
            top: parent.top
            bottom: parent.bottom
            left: parent.left
            right: parent.right
        }

        onPressed: {
            previousX = mouseX
            previousY = mouseY
        }

        onMouseXChanged: {
            var dx = mouseX - previousX
            mainWindow.setX(mainWindow.x + dx)
        }

        onMouseYChanged: {
            var dy = mouseY - previousY
            mainWindow.setY(mainWindow.y + dy)
        }
    }
}

main.cpp

Just add MacWindow::removeTitlebarFromWindow();

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "macos.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(u"qrc:/qt-macos-without-title/main.qml"_qs);
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    MacOS::removeTitlebarFromWindow();

    return app.exec();
}

And finally include cocoa lib in .pro file

yourapp.pro

...
macx:LIBS += -framework Foundation -framework Cocoa
macx:INCLUDEPATH += /System/Library/Frameworks/Foundation.framework/Versions/C/Headers \
/System/Library/Frameworks/AppKit.framework/Headers \
/System/Library/Frameworks/Cocoa.framework/Headers
Teimuraz
  • 8,795
  • 5
  • 35
  • 62