├── README.md ├── main.mm ├── main.qml ├── qml.qrc ├── qt-touchbar.pro ├── qtquickcontrols2.conf ├── touchbar.h ├── touchbar.mm ├── touchbarbutton.h ├── touchbarbutton.mm ├── touchbarslider.h └── touchbarslider.mm /README.md: -------------------------------------------------------------------------------- 1 | Qt Touch Bar 2 | ============ 3 | 4 | This example shows how to create a global touch bar with standard 5 | and custom items for a Qt-based application. 6 | -------------------------------------------------------------------------------- /main.mm: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "touchbar.h" 5 | #include "touchbarbutton.h" 6 | #include "touchbarslider.h" 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | qmlRegisterType("TouchBar", 1, 0, "TouchBar"); 11 | qmlRegisterType("TouchBar", 1, 0, "TouchBarButton"); 12 | qmlRegisterType("TouchBar", 1, 0, "TouchBarSlider"); 13 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 14 | QGuiApplication app(argc, argv); 15 | 16 | QQmlApplicationEngine engine; 17 | engine.load(QUrl(QLatin1String("qrc:/main.qml"))); 18 | if (engine.rootObjects().isEmpty()) 19 | return -1; 20 | 21 | 22 | return app.exec(); 23 | } 24 | -------------------------------------------------------------------------------- /main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.7 2 | import QtQuick.Controls 2.0 3 | import QtQuick.Controls 1.4 as QQC1 4 | import QtQuick.Layouts 1.3 5 | import TouchBar 1.0 6 | 7 | QQC1.ApplicationWindow { 8 | visible: true 9 | width: 640 10 | height: 480 11 | title: qsTr("Hello World") 12 | TouchBar { 13 | id: controller 14 | TouchBarButton { 15 | title: "MyButton 1" 16 | onPressed: console.log("Did press this button") 17 | } 18 | TouchBarButton { 19 | title: "MyButton 2" 20 | onPressed: console.log("Did press this other button") 21 | } 22 | TouchBarSlider { 23 | id: tbSlider 24 | label: "MySlider" 25 | from: 1 26 | to: 10 27 | value: slider.value 28 | } 29 | } 30 | 31 | Slider { 32 | id: slider 33 | from: tbSlider.from 34 | to: tbSlider.to 35 | value: tbSlider.value 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | qtquickcontrols2.conf 5 | 6 | 7 | -------------------------------------------------------------------------------- /qt-touchbar.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | TARGET = qt-and-touchbar 3 | INCLUDEPATH += . 4 | QT += quick 5 | CONFIG += c++11 6 | 7 | # Input 8 | OBJECTIVE_SOURCES += \ 9 | main.mm \ 10 | touchbar.mm \ 11 | touchbarbutton.mm 12 | 13 | HEADERS += \ 14 | touchbar.h \ 15 | touchbarbutton.h \ 16 | touchbarslider.h 17 | 18 | LIBS += -framework AppKit 19 | RESOURCES += qml.qrc 20 | 21 | SOURCES += \ 22 | touchbarslider.mm 23 | -------------------------------------------------------------------------------- /qtquickcontrols2.conf: -------------------------------------------------------------------------------- 1 | ; This file can be edited to change the style of the application 2 | ; See Styling Qt Quick Controls 2 in the documentation for details: 3 | ; http://doc.qt.io/qt-5/qtquickcontrols2-styles.html 4 | 5 | [Controls] 6 | Style=Default 7 | 8 | [Universal] 9 | Theme=Light 10 | ;Accent=Steel 11 | 12 | [Material] 13 | Theme=Light 14 | ;Accent=BlueGrey 15 | ;Primary=BlueGray 16 | -------------------------------------------------------------------------------- /touchbar.h: -------------------------------------------------------------------------------- 1 | #ifndef TOUCHBARCONTROLLER_H 2 | #define TOUCHBARCONTROLLER_H 3 | 4 | #include 5 | 6 | 7 | 8 | 9 | class TouchBar : public QQuickItem 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit TouchBar(QQuickItem *parent = nullptr); 14 | signals: 15 | 16 | public slots: 17 | 18 | private: 19 | void* m_tbProvider; 20 | // QQuickItem interface 21 | protected: 22 | virtual void itemChange(ItemChange, const ItemChangeData &) override; 23 | }; 24 | 25 | #endif // TOUCHBARCONTROLLER_H 26 | -------------------------------------------------------------------------------- /touchbar.mm: -------------------------------------------------------------------------------- 1 | #include "touchbar.h" 2 | #import 3 | #import 4 | #import 5 | #import 6 | #import 7 | #include 8 | #include 9 | #include 10 | #include "touchbarbutton.h" 11 | #include "touchbarslider.h" 12 | 13 | using namespace std; 14 | 15 | #import 16 | 17 | // This example shows how to create and populate touch bars for Qt applications. 18 | // Two approaches are demonstrated: creating a global touch bar for the entire 19 | // application via the NSApplication delegate, and creating per-window touch bars 20 | // via the NSWindow delegate. Applications may use either or both of these, for example 21 | // to provide global base touch bar with window specific additions. Refer to the 22 | // NSTouchBar documentation for further details. 23 | 24 | // The TouchBarProvider class implements the NSTouchBarDelegate protocol, as 25 | // well as app and window delegate protocols. 26 | @interface TouchBarProvider: NSResponder 27 | @property (strong) NSMutableDictionary *items; 28 | @property (strong) NSObject *qtDelegate; 29 | 30 | @end 31 | 32 | @implementation TouchBarProvider 33 | @synthesize items; 34 | - (NSTouchBar *)makeTouchBar 35 | { 36 | // Create the touch bar with this instance as its delegate 37 | 38 | self.touchBar = [[NSTouchBar alloc] init]; 39 | self.touchBar.delegate = self; 40 | 41 | return self.touchBar; 42 | } 43 | 44 | - (NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier 45 | { 46 | Q_UNUSED(touchBar); 47 | if([self.items objectForKey:identifier] != nil) { 48 | return [self.items objectForKey:identifier]; 49 | } 50 | NSLog(@"Did not have key, checking rest: %@", identifier); 51 | return nil; 52 | } 53 | 54 | - (void)installAsDelegateForWindow:(NSWindow *)window 55 | { 56 | _qtDelegate = window.delegate; // Save current delegate for forwarding 57 | window.delegate = self; 58 | } 59 | 60 | - (void)installAsDelegateForApplication:(NSApplication *)application 61 | { 62 | _qtDelegate = application.delegate; // Save current delegate for forwarding 63 | application.delegate = self; 64 | } 65 | 66 | - (BOOL):(SEL)aSelector 67 | { 68 | // We want to forward to the qt delegate. Respond to selectors it 69 | // responds to in addition to selectors this instance resonds to. 70 | return [_qtDelegate respondsToSelector:aSelector] || [super respondsToSelector:aSelector]; 71 | } 72 | 73 | - (void)forwardInvocation:(NSInvocation *)anInvocation 74 | { 75 | // Forward to the existing delegate. This function is only called for selectors 76 | // this instance does not responds to, which means that the qt delegate 77 | // must respond to it (due to the respondsToSelector implementation above). 78 | [anInvocation invokeWithTarget:_qtDelegate]; 79 | } 80 | 81 | @end 82 | 83 | TouchBar::TouchBar(QQuickItem *parent) : QQuickItem(parent) 84 | { 85 | qDebug() << "Did create TouchBar"; 86 | m_tbProvider = [[TouchBarProvider alloc] init]; 87 | [(id)m_tbProvider installAsDelegateForApplication:[NSApplication sharedApplication]]; 88 | } 89 | 90 | void TouchBar::itemChange(ItemChange, const ItemChangeData &) 91 | { 92 | // When children are updated in QML, loop through all of them, 93 | // dynamic cast to known supported TouchBarItems and create identifier and 94 | // NSTouchBarItem before updating the touchbar delegate. 95 | 96 | // TODO: use other function to see when QML children are updated? 97 | NSMutableDictionary *items = [NSMutableDictionary dictionary]; 98 | 99 | int i = 0; 100 | for(QObject *child : children()) { 101 | NSString *identifier = [NSString stringWithFormat:@"com.myapp.%d", i++]; 102 | if(dynamic_cast(child)) { 103 | // This is a button 104 | TouchBarButton *button = dynamic_cast(child); 105 | NSCustomTouchBarItem *item = [[[NSCustomTouchBarItem alloc] initWithIdentifier:identifier] autorelease]; 106 | item.view = button->getNSButton(); 107 | [items setObject:item forKey:identifier]; 108 | } else if(dynamic_cast(child)) { 109 | // This is a slider 110 | TouchBarSlider *slider = dynamic_cast(child); 111 | NSSliderTouchBarItem *item = [[NSSliderTouchBarItem alloc] initWithIdentifier:identifier]; 112 | 113 | item.slider = slider->getNSSlider(); 114 | item.label = slider->label().toNSString(); 115 | [item setTarget:slider->onMovedBlock()]; 116 | [item setAction:@selector(invoke)]; 117 | [items setObject:item forKey:identifier]; 118 | } 119 | } 120 | TouchBarProvider *tbp = static_cast(m_tbProvider); 121 | [tbp setItems:items]; 122 | tbp.touchBar.defaultItemIdentifiers = [items allKeys]; 123 | } 124 | -------------------------------------------------------------------------------- /touchbarbutton.h: -------------------------------------------------------------------------------- 1 | #ifndef TOUCHBARBUTTON_H 2 | #define TOUCHBARBUTTON_H 3 | #include 4 | #ifdef __OBJC__ 5 | #import 6 | #endif 7 | 8 | class TouchBarButton : public QQuickItem 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) 12 | QString m_title; 13 | 14 | public: 15 | TouchBarButton(); 16 | ~TouchBarButton(); 17 | #ifdef __OBJC__ 18 | NSButton *getNSButton(); 19 | id onPressedBlock(); 20 | #endif 21 | 22 | QString title() const; 23 | signals: 24 | void titleChanged(QString title); 25 | void pressed(); 26 | public slots: 27 | void setTitle(QString title); 28 | private: 29 | #ifdef __OBJC__ 30 | id m_onPressedBlock; 31 | NSButton *m_button; 32 | #endif 33 | }; 34 | 35 | #endif // TOUCHBARBUTTON_H 36 | -------------------------------------------------------------------------------- /touchbarbutton.mm: -------------------------------------------------------------------------------- 1 | #include "touchbarbutton.h" 2 | // #import 3 | 4 | TouchBarButton::TouchBarButton() 5 | { 6 | m_button = nil; 7 | m_onPressedBlock = [^{ emit pressed(); } copy]; 8 | } 9 | 10 | TouchBarButton::~TouchBarButton() { 11 | [(id)m_onPressedBlock release]; 12 | if(m_button) 13 | [m_button release]; 14 | } 15 | 16 | id TouchBarButton::onPressedBlock() 17 | { 18 | return m_onPressedBlock; 19 | } 20 | 21 | NSButton *TouchBarButton::getNSButton() 22 | { 23 | m_button = [[NSButton buttonWithTitle:title().toNSString() target:(id)m_onPressedBlock 24 | action:@selector(invoke)] autorelease]; // TODO: memory issues since we haven't increased retain count 25 | return m_button; 26 | } 27 | 28 | QString TouchBarButton::title() const 29 | { 30 | return m_title; 31 | } 32 | 33 | void TouchBarButton::setTitle(QString title) 34 | { 35 | if (m_title == title) 36 | return; 37 | 38 | m_title = title; 39 | if(m_button) { 40 | [m_button setTitle:m_title.toNSString()]; 41 | } 42 | 43 | emit titleChanged(m_title); 44 | } 45 | -------------------------------------------------------------------------------- /touchbarslider.h: -------------------------------------------------------------------------------- 1 | #ifndef TOUCHBARSLIDER_H 2 | #define TOUCHBARSLIDER_H 3 | #include 4 | #ifdef __OBJC__ 5 | #import 6 | #endif 7 | 8 | class TouchBarSlider : public QQuickItem 9 | { 10 | Q_OBJECT 11 | Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) 12 | Q_PROPERTY(double from READ from WRITE setFrom NOTIFY fromChanged) 13 | Q_PROPERTY(double to READ to WRITE setTo NOTIFY toChanged) 14 | Q_PROPERTY(double value READ value WRITE setValue NOTIFY valueChanged) 15 | 16 | public: 17 | TouchBarSlider(); 18 | ~TouchBarSlider(); 19 | #ifdef __OBJC__ 20 | NSSlider *getNSSlider(); 21 | id onMovedBlock(); 22 | #endif 23 | 24 | double from() const; 25 | double to() const; 26 | double value() const; 27 | QString label() const; 28 | 29 | signals: 30 | void fromChanged(double from); 31 | void toChanged(double to); 32 | void valueChanged(double value); 33 | void labelChanged(QString label); 34 | 35 | public slots: 36 | void setFrom(double from); 37 | void setTo(double to); 38 | void setValue(double value); 39 | void setLabel(QString label); 40 | 41 | private: 42 | QString m_label; 43 | #ifdef __OBJC__ 44 | id m_onMovedBlock; 45 | NSSlider *m_slider; 46 | #endif 47 | }; 48 | 49 | #endif // TOUCHBARBUTTON_H 50 | -------------------------------------------------------------------------------- /touchbarslider.mm: -------------------------------------------------------------------------------- 1 | #include "touchbarslider.h" 2 | 3 | TouchBarSlider::TouchBarSlider() 4 | { 5 | m_slider = [[NSSlider alloc] init]; 6 | m_onMovedBlock = [^(double){ 7 | emit valueChanged(m_slider.doubleValue); 8 | } copy]; 9 | } 10 | 11 | TouchBarSlider::~TouchBarSlider() { 12 | [(id)m_onMovedBlock release]; 13 | if(m_slider) 14 | [m_slider release]; 15 | } 16 | 17 | NSSlider *TouchBarSlider::getNSSlider() 18 | { 19 | return m_slider; 20 | } 21 | 22 | id TouchBarSlider::onMovedBlock() { 23 | return m_onMovedBlock; 24 | } 25 | 26 | double TouchBarSlider::from() const 27 | { 28 | return m_slider.minValue; 29 | } 30 | 31 | double TouchBarSlider::to() const 32 | { 33 | return m_slider.maxValue; 34 | } 35 | 36 | double TouchBarSlider::value() const 37 | { 38 | return m_slider.doubleValue; 39 | } 40 | 41 | QString TouchBarSlider::label() const 42 | { 43 | return m_label; 44 | } 45 | 46 | void TouchBarSlider::setFrom(double from) 47 | { 48 | // qWarning("Floating point comparison needs context sanity check"); 49 | if (qFuzzyCompare(m_slider.minValue, from)) 50 | return; 51 | 52 | m_slider.minValue = from; 53 | emit fromChanged(m_slider.minValue); 54 | } 55 | 56 | void TouchBarSlider::setTo(double to) 57 | { 58 | // qWarning("Floating point comparison needs context sanity check"); 59 | if (qFuzzyCompare(m_slider.maxValue, to)) 60 | return; 61 | 62 | m_slider.maxValue = to; 63 | emit toChanged(m_slider.maxValue); 64 | } 65 | 66 | void TouchBarSlider::setValue(double value) 67 | { 68 | // qWarning("Floating point comparison needs context sanity check"); 69 | if (qFuzzyCompare(m_slider.doubleValue, value)) 70 | return; 71 | 72 | m_slider.doubleValue = value; 73 | emit valueChanged(m_slider.doubleValue); 74 | } 75 | 76 | void TouchBarSlider::setLabel(QString label) 77 | { 78 | if (m_label == label) 79 | return; 80 | 81 | m_label = label; 82 | emit labelChanged(m_label); 83 | } 84 | --------------------------------------------------------------------------------