├── .gitignore ├── include ├── MacHelper ├── MacPopover ├── MacToolbar ├── MacMenubarIcon └── MacToolbarItem ├── README.md ├── src ├── PopoverHelper.h ├── MacMenubarIcon.mm ├── MacPopover.mm ├── MacToolbarItem.mm ├── PopoverHelper.mm ├── StatusbarHelper.h ├── ToolbarDelegate.mm ├── MacToolbar.mm ├── MacPopover.h ├── MacMenubarIcon.h ├── ToolbarDelegate.h ├── MacToolbar.h ├── StatusbarHelper.mm ├── MacToolbarItem.h ├── MacHelper.h └── MacHelper.mm └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /include/MacHelper: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 10/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #include "../src/MacHelper.h" -------------------------------------------------------------------------------- /include/MacPopover: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 10/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #include "../src/MacPopover.h" -------------------------------------------------------------------------------- /include/MacToolbar: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 10/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #include "../src/MacToolbar.h" 23 | -------------------------------------------------------------------------------- /include/MacMenubarIcon: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 10/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #include "../src/MacMenubarIcon.h" -------------------------------------------------------------------------------- /include/MacToolbarItem: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 10/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #include "../src/MacToolbarItem.h" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nedrysoft MacHelper Library 2 | 3 | The MacHelper library provides classess for interactive with cocoa via Qt objects. It provides: 4 | 5 | - A NSToolbar based implementation that supports the "preferences" style in Big Sur or later. 6 | - A NSStatusbar based implementation for creating menu bar icons. 7 | - A NSPopover based implementation for creating standard macOS popover windows. 8 | - A NSAlert based implementation for creating native looking message boxes, particularly useful because the style is very different under Big Sur to what the QMessageBox implementation provides. 9 | - Functions for converting images to and from NSImage. 10 | - Functions for retrieving system fonts. 11 | - Functions for retrieving standard OS icons. 12 | 13 | ## Requirements 14 | 15 | * Qt5 or Qt6 16 | * CMake 17 | 18 | ## Building 19 | 20 | To build the library, invoke CMake or open the CMakeLists.txt file in your preferred IDE. 21 | 22 | Setting the following CMake variables allows the customisation of the build. 23 | 24 | ``` 25 | NEDRYSOFT_MACHELPER_LIBRARY_DIR= 26 | ``` 27 | 28 | Sets the output folder for the dynamic library; if omitted, you can find the binaries in the default location. 29 | 30 | # License 31 | 32 | This project is open source and released under the GPLv3 licence. 33 | 34 | Distributed as-is; no warranty is given. 35 | -------------------------------------------------------------------------------- /src/PopoverHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 04/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #ifndef NEDRYSOFT_POPOVERHELPER_H 23 | #define NEDRYSOFT_POPOVERHELPER_H 24 | 25 | #include 26 | #include 27 | 28 | #include "MacMenubarIcon.h" 29 | #include "MacPopover.h" 30 | 31 | #import 32 | 33 | //! @cond 34 | 35 | @interface PopoverHelper : NSObject { 36 | QWidget *m_contentWidget; 37 | NSPopover *m_popover; 38 | NSView *m_nativeView; 39 | NSViewController *m_viewController; 40 | } 41 | //! @endcond 42 | 43 | /** 44 | * @brief Show the popover with the given parameters. 45 | * 46 | * @param[in] contentWidget the widget that is used as the content of the popover. 47 | * @param[in] view the view that the popover originates from. 48 | * @param[in] sourceRect the rectangle of the view that the popover originates from. 49 | * @param[in] size the size of the popover window. 50 | * @param[in] preferredEdge the preferred edge that the popover should be relative to. 51 | */ 52 | - (void) show:(QWidget *) contentWidget 53 | withView:(NSView *) view 54 | sourceRect:(NSRect) rect 55 | size:(NSSize) size 56 | preferredEdge:(Nedrysoft::MacHelper::MacPopover::Edge) preferredEdge; 57 | 58 | @end 59 | 60 | #endif // NEDRYSOFT_POPOVERHELPER_H 61 | -------------------------------------------------------------------------------- /src/MacMenubarIcon.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * * 6 | * Created by Adrian Carpenter on 07/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #include "MacMenubarIcon.h" 23 | #include "StatusbarHelper.h" 24 | 25 | Nedrysoft::MacHelper::MacMenubarIcon::MacMenubarIcon(QPixmap pixmap) { 26 | m_pixmap = pixmap; 27 | 28 | m_statusbarHelper = [[StatusbarHelper alloc] initWithMenuBarIcon: this]; 29 | } 30 | 31 | auto Nedrysoft::MacHelper::MacMenubarIcon::pixmap() -> QPixmap { 32 | return m_pixmap; 33 | } 34 | 35 | auto Nedrysoft::MacHelper::MacMenubarIcon::buttonRect() -> QRect { 36 | return QRectF::fromCGRect(NSRectToCGRect([m_statusbarHelper buttonRect])).toRect(); 37 | } 38 | 39 | auto Nedrysoft::MacHelper::MacMenubarIcon::button() -> NSView * { 40 | return [m_statusbarHelper button]; 41 | } 42 | 43 | auto Nedrysoft::MacHelper::MacMenubarIcon::setPixmap(QPixmap pixmap) -> void { 44 | m_pixmap = pixmap; 45 | 46 | [m_statusbarHelper updatePixmap]; 47 | } 48 | 49 | auto Nedrysoft::MacHelper::MacMenubarIcon::show() -> void { 50 | [m_statusbarHelper setVisible: true]; 51 | } 52 | 53 | auto Nedrysoft::MacHelper::MacMenubarIcon::hide() -> void { 54 | [m_statusbarHelper setVisible: false]; 55 | } 56 | 57 | auto Nedrysoft::MacHelper::MacMenubarIcon::showMenu(QMenu *menu) -> void { 58 | [m_statusbarHelper showMenu: menu]; 59 | } -------------------------------------------------------------------------------- /src/MacPopover.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * * 6 | * Created by Adrian Carpenter on 07/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #include "MacMenubarIcon.h" 23 | #include "MacPopover.h" 24 | #include "PopoverHelper.h" 25 | 26 | /* 27 | * TODO: need to add the direction of the popover for the general case 28 | */ 29 | 30 | auto Nedrysoft::MacHelper::MacPopover::show( 31 | Nedrysoft::MacHelper::MacMenubarIcon *menubarIcon, 32 | QWidget *contentWidget, 33 | QSize size, 34 | Nedrysoft::MacHelper::MacPopover::Edge edge) -> void { 35 | 36 | m_popover = [[PopoverHelper alloc] init]; 37 | 38 | [m_popover show:contentWidget 39 | withView:menubarIcon->button() 40 | sourceRect:NSRectFromCGRect(menubarIcon->buttonRect().toCGRect()) 41 | size:size.toCGSize() 42 | preferredEdge:edge]; 43 | } 44 | 45 | auto Nedrysoft::MacHelper::MacPopover::show( 46 | QWidget *widget, 47 | QWidget *contentWidget, 48 | QSize size, 49 | Nedrysoft::MacHelper::MacPopover::Edge edge) -> void { 50 | 51 | m_popover = [[PopoverHelper alloc] init]; 52 | 53 | [m_popover show:contentWidget 54 | withView:reinterpret_cast(widget->winId()) 55 | sourceRect:NSRectFromCGRect(widget->rect().toCGRect()) 56 | size:size.toCGSize() 57 | preferredEdge:edge]; 58 | } 59 | -------------------------------------------------------------------------------- /src/MacToolbarItem.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * An open-source cross-platform traceroute analyser. 7 | * 8 | * Created by Adrian Carpenter on 07/05/2021. 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | #include "MacToolbarItem.h" 25 | 26 | #import 27 | 28 | constexpr auto IconSize = 256; 29 | 30 | Nedrysoft::MacHelper::MacToolbarItem::MacToolbarItem( 31 | const QIcon &icon, const QString &identifier, const QString &label, const QString &paletteLabel) : 32 | m_icon(icon), 33 | m_identifier(identifier), 34 | m_label(label.isEmpty()==false ? label:identifier), 35 | m_paletteLabel(paletteLabel.isEmpty()==false ? paletteLabel:identifier), 36 | m_toolbarItem(nullptr) { 37 | 38 | } 39 | 40 | auto Nedrysoft::MacHelper::MacToolbarItem::setIcon(QIcon icon) -> void { 41 | auto pixmap = icon.pixmap(IconSize, IconSize); 42 | 43 | NSImage *image = [[NSImage alloc] initWithCGImage: pixmap.toImage().toCGImage() 44 | size: CGSizeMake(IconSize, IconSize)]; 45 | 46 | [m_toolbarItem setImage:image]; 47 | 48 | [image release]; 49 | } 50 | 51 | auto Nedrysoft::MacHelper::MacToolbarItem::identifier() -> QString { 52 | return m_identifier; 53 | } 54 | 55 | auto Nedrysoft::MacHelper::MacToolbarItem::icon() -> QIcon { 56 | return m_icon; 57 | } 58 | 59 | auto Nedrysoft::MacHelper::MacToolbarItem::item() -> NSToolbarItem * { 60 | return m_toolbarItem; 61 | } 62 | 63 | auto Nedrysoft::MacHelper::MacToolbarItem::setToolbarItem(NSToolbarItem *item) -> void { 64 | m_toolbarItem = item; 65 | } -------------------------------------------------------------------------------- /src/PopoverHelper.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 04/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #include "PopoverHelper.h" 23 | 24 | @implementation PopoverHelper 25 | 26 | - (void) show:(QWidget *) contentWidget 27 | withView:(NSView *) view 28 | sourceRect:(NSRect) rect 29 | size:(NSSize) size 30 | preferredEdge:(Nedrysoft::MacHelper::MacPopover::Edge) preferredEdge { 31 | 32 | m_contentWidget = contentWidget; 33 | 34 | m_viewController = [[NSViewController alloc] init]; 35 | 36 | // create the popover 37 | 38 | m_popover = [[NSPopover alloc] init]; 39 | 40 | [m_popover setContentSize: size]; 41 | [m_popover setBehavior: NSPopoverBehaviorTransient]; 42 | [m_popover setAnimates: YES]; 43 | [m_popover setContentViewController: m_viewController]; 44 | 45 | auto contentWidgetView = reinterpret_cast(m_contentWidget->winId()); 46 | 47 | m_nativeView = [[NSView alloc] init]; 48 | 49 | [m_nativeView addSubview: contentWidgetView]; 50 | 51 | m_contentWidget->show(); 52 | 53 | [m_viewController setView: contentWidgetView]; 54 | 55 | // show the popover in the correct place 56 | 57 | NSRectEdge edge; 58 | 59 | if (preferredEdge==Nedrysoft::MacHelper::MacPopover::Edge::MinXEdge) { 60 | edge = NSRectEdgeMinX; 61 | } else if (preferredEdge==Nedrysoft::MacHelper::MacPopover::Edge::MaxXEdge) { 62 | edge = NSRectEdgeMaxX; 63 | } else if (preferredEdge==Nedrysoft::MacHelper::MacPopover::Edge::MinYEdge) { 64 | edge = NSRectEdgeMinY; 65 | } else { 66 | edge = NSRectEdgeMaxY; 67 | } 68 | 69 | [m_popover showRelativeToRect: rect 70 | ofView: view 71 | preferredEdge: edge]; 72 | 73 | [[NSNotificationCenter defaultCenter] addObserver:self 74 | selector:@selector(receivePopoverClosedNotification:) 75 | name:@"NSPopoverDidCloseNotification" 76 | object:m_popover]; 77 | } 78 | 79 | - (void) receivePopoverClosedNotification:(NSNotification *) notification { 80 | delete m_contentWidget; 81 | } 82 | 83 | @end -------------------------------------------------------------------------------- /src/StatusbarHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 04/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #ifndef NEDRYSOFT_STATUSBARHELPER_H 23 | #define NEDRYSOFT_STATUSBARHELPER_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "MacMenubarIcon.h" 31 | 32 | #import 33 | 34 | class QMenu; 35 | 36 | //! @cond 37 | 38 | @interface StatusbarHelper : NSObject { 39 | NSStatusItem *m_statusbarItem; 40 | NSStatusBarButton *m_button; 41 | Nedrysoft::MacHelper::MacMenubarIcon *m_menubarIcon; 42 | id m_delegate; 43 | QMenu *m_menu; 44 | NSMenu *m_nativeMenu; 45 | QMap m_actionMap; 46 | } 47 | 48 | //! @endcond 49 | 50 | /** 51 | * @brief Called when the menu bar icons button has been clicked. 52 | * 53 | * @param[in] sender the button that was the source of the click. 54 | */ 55 | - (void) statusBarItemClicked:(NSStatusBarButton *) sender; 56 | 57 | /** 58 | * @brief Initialises a StatusbarHelper for a given menu bar icon. 59 | * 60 | * @param[in] menuBarIcon the menu bar icon. 61 | * 62 | * @returns the StatusBarHelper instance. 63 | */ 64 | - (id) initWithMenuBarIcon:(Nedrysoft::MacHelper::MacMenubarIcon *) menubarIcon; 65 | 66 | /** 67 | * @brief Returns the rectangle of the menu bar icons button. 68 | * 69 | * @returns the menu bar buttons rectangle. 70 | */ 71 | - (NSRect) buttonRect; 72 | 73 | /** 74 | * @brief Returns the NSView of the button. 75 | * 76 | * @returns the button. 77 | */ 78 | - (NSView *) button; 79 | 80 | /** 81 | * @brief Updates the pixmap. 82 | * 83 | * @note retrieves the pixmap from the MenuBarIcon that was associated during construction and sets it 84 | * to be the current icon. 85 | */ 86 | - (void) updatePixmap; 87 | 88 | /** 89 | * @brief Displays or hides the icon. 90 | * 91 | * @param[in] visible true if the icon is to be shown; otherwise false. 92 | */ 93 | - (void) setVisible:(bool) visible; 94 | 95 | /** 96 | * @brief When responding to a click on the icon, this method can be used to show the menu. 97 | */ 98 | - (void) showMenu:(QMenu *) menu; 99 | 100 | /** 101 | * @brief Called when the menu is dismissed. 102 | * 103 | * @param[in] menu the menu that was closed. 104 | */ 105 | - (void) menuDidClose:(NSMenu *) menu; 106 | 107 | /** 108 | * @brief Called when a menu item is selected. 109 | * 110 | * @param[id] sender a pointer to the NSMenuItem that was selected. 111 | */ 112 | - (void) performAction:(id) sender; 113 | 114 | @end 115 | 116 | #endif // NEDRYSOFT_STATUSBARHELPER_H 117 | -------------------------------------------------------------------------------- /src/ToolbarDelegate.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 07/05/2020. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #import "ToolbarDelegate.h" 23 | 24 | #include "MacToolbarItem.h" 25 | 26 | constexpr auto IconSize = 256; 27 | 28 | @implementation ToolbarDelegate 29 | 30 | - (NSToolbarItem *) toolbar:(NSToolbar *) toolbar 31 | itemForItemIdentifier:(NSString *) itemIdentifier 32 | willBeInsertedIntoToolbar:(BOOL) flag { 33 | Nedrysoft::MacHelper::MacToolbarItem *foundItem = nil; 34 | 35 | for (auto toolbarItem : m_items) { 36 | if (toolbarItem->identifier()==QString::fromNSString(itemIdentifier)) { 37 | foundItem = toolbarItem; 38 | 39 | break; 40 | } 41 | } 42 | 43 | assert(foundItem!=nil); 44 | 45 | NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier]; 46 | 47 | foundItem->setToolbarItem(item); 48 | 49 | auto pixmap = foundItem->icon().pixmap(IconSize, IconSize); 50 | 51 | NSImage *image = [self imageFromPixmap: pixmap]; 52 | 53 | item.label = foundItem->identifier().toNSString(); 54 | item.image = image; 55 | item.target = self; 56 | item.action = @selector(itemSelected:); 57 | item.enabled = true; 58 | 59 | [image release]; 60 | 61 | return item; 62 | } 63 | 64 | - (void) itemSelected:(NSToolbarItem *) toolbarItem { 65 | for (auto item : m_items) { 66 | if (item->item()==toolbarItem) { 67 | Q_EMIT item->activated(); 68 | 69 | return; 70 | } 71 | } 72 | } 73 | 74 | - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *) toolbar { 75 | return [self identifiers]; 76 | } 77 | 78 | - (NSArray *) toolbarDefaultItemIdentifiers:(NSToolbar *) toolbar { 79 | return [self identifiers]; 80 | } 81 | 82 | - (NSArray *) toolbarSelectableItemIdentifiers:(NSToolbar *) toolbar { 83 | return [self identifiers]; 84 | } 85 | 86 | - (void) toolbarWillAddItem:(NSNotification *) notification { 87 | } 88 | 89 | - (void) toolbarDidRemoveItem:(NSNotification *) notification { 90 | } 91 | 92 | - (NSImage *) imageFromPixmap:(const QPixmap &) pixmap { 93 | NSImage *image = [[NSImage alloc] initWithCGImage: pixmap.toImage().toCGImage() 94 | size:CGSizeMake(IconSize, IconSize)]; 95 | 96 | return image; 97 | } 98 | 99 | - (void) setItems:(QList) items { 100 | m_items = items; 101 | } 102 | 103 | - (NSArray *) identifiers { 104 | NSMutableArray *array = [[NSMutableArray alloc] init]; 105 | 106 | for (auto toolbarItem : m_items) { 107 | [array addObject: toolbarItem->identifier().toNSString()]; 108 | } 109 | 110 | return array; 111 | } 112 | 113 | @end -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2020 Adrian Carpenter 3 | # 4 | # This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | # 6 | # Created by Adrian Carpenter on 07/12/2020. 7 | # 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | # 21 | 22 | cmake_minimum_required(VERSION 3.10) 23 | 24 | set(CMAKE_CXX_STANDARD 17) 25 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 26 | 27 | set(CMAKE_AUTOMOC ON) 28 | set(CMAKE_AUTOUIC ON) 29 | set(CMAKE_AUTORCC ON) 30 | 31 | # discover which Qt version is available 32 | 33 | if (NOT DEFINED QT_VERSION_MAJOR) 34 | if (DEFINED USE_QT_VERSION) 35 | set(QT_VERSION_MAJOR ${USE_QT_VERSION}) 36 | message(STATUS "Qt${QT_VERSION_MAJOR} has been manually selected") 37 | else() 38 | message(STATUS "Detecting Qt version") 39 | 40 | find_package(Qt6 COMPONENTS Core QUIET) 41 | find_package(Qt5 COMPONENTS Core QUIET) 42 | 43 | if (Qt6_FOUND) 44 | set(QT_VERSION_MAJOR 6) 45 | elseif(Qt5_FOUND) 46 | set(QT_VERSION_MAJOR 5) 47 | else() 48 | message(FATAL_ERROR "No valid Qt version was set, and none could be found") 49 | endif() 50 | message(STATUS "Detecting Qt version - done") 51 | endif() 52 | 53 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core QUIET) 54 | 55 | if (NOT Qt${QT_VERSION_MAJOR}_FOUND) 56 | message(FATAL_ERROR "Qt${QT_VERSION_MAJOR} could not be found") 57 | endif() 58 | 59 | message(STATUS "Qt major version: ${QT_VERSION_MAJOR}") 60 | endif() 61 | 62 | # end of qt selection/detection 63 | 64 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Gui Widgets REQUIRED) 65 | 66 | set(Qt_LIBS Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Gui) 67 | 68 | add_definitions(-DQT_NO_KEYWORDS) 69 | 70 | add_definitions(-DNEDRYSOFT_LIBRARY_MACHELPER_EXPORT) 71 | 72 | project(MacHelper) 73 | 74 | set(library_SOURCES 75 | src/MacHelper.h 76 | src/MacHelper.mm 77 | src/MacMenubarIcon.h 78 | src/MacMenubarIcon.mm 79 | src/MacPopover.h 80 | src/MacPopover.mm 81 | src/MacToolbar.h 82 | src/MacToolbar.mm 83 | src/MacToolbarItem.h 84 | src/MacToolbarItem.mm 85 | src/PopoverHelper.h 86 | src/PopoverHelper.mm 87 | src/StatusbarHelper.h 88 | src/StatusbarHelper.mm 89 | src/ToolbarDelegate.h 90 | src/ToolbarDelegate.mm 91 | ) 92 | 93 | include_directories("src") 94 | 95 | add_library(${PROJECT_NAME} SHARED 96 | ${library_SOURCES} 97 | ) 98 | 99 | target_link_libraries(${PROJECT_NAME} ${Qt_LIBS}) 100 | 101 | if(DEFINED NEDRYSOFT_MACHELPER_LIBRARY_DIR) 102 | set_target_properties(${PROJECT_NAME} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${NEDRYSOFT_MACHELPER_LIBRARY_DIR}") 103 | set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${NEDRYSOFT_MACHELPER_LIBRARY_DIR}") 104 | else() 105 | message(STATUS "Set NEDRYSOFT_MACHELPER_LIBRARY_DIR to set the binary output dir.") 106 | endif() 107 | 108 | target_link_libraries(${PROJECT_NAME} ${Qt_LIBS}) 109 | target_link_libraries(${PROJECT_NAME} "-framework AppKit" "-framework Cocoa") 110 | target_link_libraries(${PROJECT_NAME} stdc++ objc) 111 | -------------------------------------------------------------------------------- /src/MacToolbar.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * A cross-platform settings dialog 7 | * 8 | * Created by Adrian Carpenter on 07/05/2021. 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program. If not, see . 22 | */ 23 | 24 | #include "MacToolbar.h" 25 | 26 | #include "MacToolbarItem.h" 27 | 28 | #include 29 | #include 30 | 31 | #import 32 | #import "ToolbarDelegate.h" 33 | 34 | constexpr auto nativeWindowHandle(QWidget *widget) { 35 | widget->winId(); 36 | 37 | return widget->windowHandle(); 38 | } 39 | 40 | Nedrysoft::MacHelper::MacToolbar::MacToolbar() : 41 | m_toolbar(nullptr), 42 | m_toolbarDelegate(nullptr), 43 | m_parentWindow(nullptr), 44 | m_isPreferences(false), 45 | m_window(nullptr) { 46 | 47 | } 48 | 49 | Nedrysoft::MacHelper::MacToolbar::~MacToolbar() { 50 | if (m_toolbarDelegate) { 51 | [m_toolbarDelegate release]; 52 | } 53 | } 54 | 55 | auto Nedrysoft::MacHelper::MacToolbar::addItem( 56 | const QIcon &icon, 57 | const QString &identifier, 58 | const QString &label, 59 | const QString &paletteLabel) -> MacToolbarItem * { 60 | 61 | auto toolbarItem = new MacToolbarItem(icon, identifier, label, paletteLabel); 62 | 63 | m_items.append(toolbarItem); 64 | 65 | return toolbarItem; 66 | } 67 | 68 | auto Nedrysoft::MacHelper::MacToolbar::enablePreferencesToolbar() -> void { 69 | if (!m_window) { 70 | m_isPreferences = true; 71 | 72 | return; 73 | } 74 | 75 | if (m_isPreferences) { 76 | if (@available(macOS 11, *)) { 77 | if ([m_window respondsToSelector:@selector(setToolbarStyle:)]) { 78 | [m_window setToolbarStyle:NSWindowToolbarStylePreference]; 79 | } 80 | } 81 | } 82 | } 83 | 84 | auto Nedrysoft::MacHelper::MacToolbar::attachToWindow(QWidget *parent) -> void { 85 | m_toolbar = [[NSToolbar alloc] init]; 86 | 87 | parent->winId(); 88 | 89 | /** 90 | * this line is strange, there appears to be a bug in CLion that flags the qobject_cast as if it 91 | * is invisible, this hack ensures that the object is a qobject, but uses the dynamic cast to 92 | * silence the issue in CLion. 93 | */ 94 | 95 | m_parentWindow = dynamic_cast(qobject_cast(parent->windowHandle())); 96 | 97 | assert(m_parentWindow!=NULL); 98 | 99 | NSView *view = reinterpret_cast(parent->winId()); 100 | 101 | NSWindow *window = [view window]; 102 | 103 | m_toolbarDelegate = [[ToolbarDelegate alloc] init]; 104 | 105 | [m_toolbarDelegate setItems: m_items]; 106 | 107 | [m_toolbar setDelegate: m_toolbarDelegate]; 108 | 109 | [window setToolbar: m_toolbar]; 110 | 111 | m_window = window; 112 | 113 | if (m_isPreferences) { 114 | enablePreferencesToolbar(); 115 | } 116 | 117 | if (!m_items.isEmpty()) { 118 | [m_toolbar setSelectedItemIdentifier:m_items.at(0)->identifier().toNSString()]; 119 | } 120 | } 121 | 122 | auto Nedrysoft::MacHelper::MacToolbar::items() -> QList { 123 | return m_items; 124 | } 125 | 126 | -------------------------------------------------------------------------------- /src/MacPopover.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 04/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #ifndef NEDRYSOFT_MACPOPOVER_H 23 | #define NEDRYSOFT_MACPOPOVER_H 24 | 25 | #include 26 | #include 27 | 28 | Q_FORWARD_DECLARE_OBJC_CLASS(PopoverHelper); 29 | 30 | namespace Nedrysoft { namespace MacHelper { 31 | class MacMenubarIcon; 32 | 33 | /** 34 | * @brief Displays a native macOS popover window. 35 | * 36 | * @class Nedrysoft::MacHelper::MacPopover MacPopover.h 37 | */ 38 | class MacPopover : 39 | public QObject { 40 | 41 | private: 42 | Q_OBJECT 43 | 44 | public: 45 | /** 46 | * @brief The list of edges that the popover can be aligned to. 47 | */ 48 | enum class Edge { 49 | MaxXEdge, 50 | MaxYEdge, 51 | MinXEdge, 52 | MinYEdge 53 | }; 54 | 55 | public: 56 | /** 57 | * @brief Shows the popup relative to a menu bar icon. 58 | * 59 | * @note The popover becomes the owner of the contentWidget, you must not delete it as it is 60 | * the responsibility of the popover to manage the widget. 61 | * 62 | * @param[in] menubarIcon the menu bar icon that the popover is relative to. 63 | * @param[in] contentWidget the content to display in the popover. 64 | * @param[in] size the size of the popover. 65 | * @param[in] edge the edge that the popover should be attached to. 66 | */ 67 | auto show( 68 | Nedrysoft::MacHelper::MacMenubarIcon *menubarIcon, 69 | QWidget *contentWidget, 70 | QSize size, 71 | Nedrysoft::MacHelper::MacPopover::Edge edge) -> void; 72 | 73 | /** 74 | * @brief Shows the popup relative to a widget. 75 | * 76 | * @note The popover becomes the owner of the contentWidget, you must not delete it as it is 77 | * the responsibility of the popover to manage the widget. 78 | * 79 | * @param[in] widget the widget that the popover is relative to. 80 | * @param[in] contentWidget the content to display in the popover. 81 | * @param[in] size the size of the popover. 82 | * @param[in] edge the edge that the popover should be attached to. 83 | */ 84 | auto show( 85 | QWidget *widget, 86 | QWidget *contentWidget, 87 | QSize size, 88 | Nedrysoft::MacHelper::MacPopover::Edge edge) -> void; 89 | 90 | public: 91 | /** 92 | * @brief This signal is emitted when the popover has been closed. 93 | */ 94 | Q_SIGNAL void closed(); 95 | 96 | private: 97 | //! @cond 98 | 99 | PopoverHelper *m_popover; 100 | 101 | //! @endcond 102 | }; 103 | }} 104 | 105 | #endif // NEDRYSOFT_MACPOPOVER_H 106 | -------------------------------------------------------------------------------- /src/MacMenubarIcon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 04/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #ifndef NEDRYSOFT_MACMENUBARICON_H 23 | #define NEDRYSOFT_MACMENUBARICON_H 24 | 25 | #include 26 | #include 27 | 28 | class QMenu; 29 | 30 | Q_FORWARD_DECLARE_OBJC_CLASS(StatusbarHelper); 31 | Q_FORWARD_DECLARE_OBJC_CLASS(NSView); 32 | 33 | namespace Nedrysoft { namespace MacHelper { 34 | enum class MouseButton { 35 | Unknown, 36 | Left, 37 | Right 38 | }; 39 | /** 40 | * @brief Provides a menu bar icon under macOS. 41 | * 42 | * @class Nedrysoft::MacHelper::MacMenubarIcon MacMenubarIcon.h 43 | */ 44 | class MacMenubarIcon : 45 | public QObject { 46 | 47 | private: 48 | Q_OBJECT 49 | 50 | public: 51 | /** 52 | * @brief Constructs a new MacMenubarIcon with a pixmap. 53 | * 54 | * @param[in] pixmap the pixmap to be used for the icon. 55 | */ 56 | MacMenubarIcon(QPixmap pixmap); 57 | 58 | /** 59 | * @brief Destroys the MenuBarIcon. 60 | */ 61 | ~MacMenubarIcon() = default; 62 | 63 | /** 64 | * @brief Returns the pixmap currently being used on the icon. 65 | * 66 | * @returns the pixmap. 67 | */ 68 | auto pixmap() -> QPixmap; 69 | 70 | /** 71 | * @brief Returns the button rect of the icon on the menu bar. 72 | * 73 | * @returns the rectangle of the icon. 74 | */ 75 | auto buttonRect() -> QRect; 76 | 77 | /** 78 | * @brief Returns the native NSVIew of the icons button. 79 | * 80 | * @returns the icons button. 81 | */ 82 | auto button() -> NSView *; 83 | 84 | /** 85 | * @brief Sets the pixmap for the icon. 86 | * 87 | * @param[in] pixmap the pixmap. 88 | */ 89 | auto setPixmap(QPixmap pixmap) -> void; 90 | 91 | /** 92 | * @brief Shows the menu bar icon. 93 | */ 94 | auto show() -> void; 95 | 96 | /** 97 | * @brief Hides the menu bar icon. 98 | */ 99 | auto hide() -> void; 100 | 101 | /** 102 | * @brief Shows the given menu from the status bar icon origin. 103 | * 104 | * @param[in] menu the menu to open. 105 | */ 106 | auto showMenu(QMenu *menu) -> void; 107 | 108 | public: 109 | /** 110 | * @brief This signal is emitted when the icon is clicked. 111 | */ 112 | Q_SIGNAL void clicked(Nedrysoft::MacHelper::MouseButton mouseButton); 113 | 114 | /** 115 | * @brief This signal is emitted when the menu is closed. 116 | * 117 | * @param[in] menu the menu that was closed. 118 | */ 119 | Q_SIGNAL void menuClosed(QMenu *menu); 120 | 121 | private: 122 | //! @cond 123 | 124 | QPixmap m_pixmap; 125 | StatusbarHelper *m_statusbarHelper; 126 | 127 | //! @endcond 128 | }; 129 | }} 130 | 131 | 132 | #endif // NEDRYSOFT_MACMENUBARICON_H 133 | -------------------------------------------------------------------------------- /src/ToolbarDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 07/05/2020. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #import 23 | 24 | #include "MacToolbar.h" 25 | 26 | #include 27 | 28 | /** 29 | * @brief The ToolbarDelegate object is used by the NSToolbar to populate the toolbar, as macOS allows 30 | * customisation of the toolbar, items are not directly create but instead, the delegate is 31 | * called to return data which macOS uses to populate the toolbar. 32 | */ 33 | 34 | //! @cond 35 | 36 | @interface ToolbarDelegate : NSObject { 37 | NSToolbar *m_toolbar; 38 | QList m_items; 39 | } 40 | 41 | //! @endcond 42 | 43 | /** 44 | * @brief Called by macOS when populating the toolbar, this returns the native toolbar item. 45 | * 46 | * @param[in] toolbar is the toolbar that will own this item. 47 | * @param[in] itemIdentifier the identifier used to correlate items with instances. 48 | * @param[in] flag true if the item is to be immediately inserted into the toolbar; false if it is to 49 | * added to the customisation palette instead. 50 | * 51 | * @returns the native NSToolbarItem. 52 | */ 53 | - (NSToolbarItem *) toolbar:(NSToolbar *) toolbar 54 | itemForItemIdentifier:(NSString *) itemIdentifier 55 | willBeInsertedIntoToolbar:(BOOL) flag; 56 | 57 | /** 58 | * @brief Returns the list of toolbar items that can be inserted into the toolbar. 59 | * 60 | * @param[in] toolbar the toolbar. 61 | * 62 | * @returns an array with the identifiers of the toolbar items that can be inserted. 63 | */ 64 | - (NSArray *) toolbarAllowedItemIdentifiers:(NSToolbar *) toolbar; 65 | 66 | /** 67 | * @brief Returns the list of toolbar items that appear in the toolbar as default. 68 | * 69 | * @param[in] toolbar the toolbar. 70 | * 71 | * @returns an array with the identifiers of the default toolbar items. 72 | */ 73 | - (NSArray *) toolbarDefaultItemIdentifiers:(NSToolbar *) toolbar; 74 | 75 | /** 76 | * @brief Returns the list of toolbar items that can be selected. 77 | * 78 | * @param[in] toolbar the toolbar. 79 | * 80 | * @returns an array with the identifiers of the selectable toolbar items. 81 | */ 82 | - (NSArray *) toolbarSelectableItemIdentifiers:(NSToolbar *) toolbar; 83 | 84 | /** 85 | * @brief Called before an item is inserted into the toolbar. 86 | * 87 | * @param[in] notification a willAddItemNotification. 88 | */ 89 | - (void) toolbarWillAddItem:(NSNotification *) notification; 90 | 91 | /** 92 | * @brief Called after an item has been removed from the toolbar. 93 | * 94 | * @param[in] notification a didRemoveItemNotification. 95 | */ 96 | - (void) toolbarDidRemoveItem:(NSNotification *) notification; 97 | 98 | /** 99 | * @brief Returns the list of identifiers used on the toolbar. 100 | * 101 | * @returns the list of items in an array. 102 | **/ 103 | - (NSArray *) identifiers; 104 | 105 | /** 106 | * @brief Returns an native NSImage from a QPixmap. 107 | * 108 | * @param[in] pixmap the pixmap to be converted. 109 | * 110 | * @returns the resulting NSImage. 111 | */ 112 | - (NSImage *) imageFromPixmap:(const QPixmap &) pixmap; 113 | 114 | /** 115 | * @brief Sets the list of toolbar icons. 116 | * 117 | * @param[in] items the list of items. 118 | * 119 | * @note The items are not directly added to the toolbar, instead their identifiers are returned from a 120 | * delegate which then calls the appropriate functions to allow creation of the native toolbar 121 | * items. 122 | */ 123 | - (void) setItems:(QList) items; 124 | 125 | /** 126 | * @brief Called when the user selects a toolbar item. 127 | * 128 | * @param[in] toolbarItem is the Nedrysoft::SettingsDialog::MacToolbarItem instance for the selected iten. 129 | */ 130 | - (void) itemSelected:(NSToolbarItem *) toolbarItem; 131 | 132 | @end 133 | 134 | 135 | -------------------------------------------------------------------------------- /src/MacToolbar.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 07/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #ifndef NEDRYSOFT_MACTOOLBAR_H 23 | #define NEDRYSOFT_MACTOOLBAR_H 24 | 25 | #include 26 | #include 27 | 28 | class QWidget; 29 | class QWindow; 30 | 31 | Q_FORWARD_DECLARE_OBJC_CLASS(NSToolbar); 32 | Q_FORWARD_DECLARE_OBJC_CLASS(ToolbarDelegate); 33 | Q_FORWARD_DECLARE_OBJC_CLASS(NSToolbarItem); 34 | Q_FORWARD_DECLARE_OBJC_CLASS(NSWindow); 35 | 36 | namespace Nedrysoft { namespace MacHelper { 37 | class MacToolbarItem; 38 | 39 | /** 40 | * @brief The MacToolbar class provides an implementation of a native NSToolbar that is attached to a 41 | * a window derived from QWindow. 42 | * 43 | * @class Nedrysoft::MacHelper::MacToolbar MacToolbar.h 44 | */ 45 | class MacToolbar { 46 | public: 47 | /** 48 | * @brief Constructs a new MacToolbar instance. 49 | */ 50 | MacToolbar(); 51 | 52 | /** 53 | * @brief Destroys a MacToolbar. 54 | */ 55 | ~MacToolbar(); 56 | 57 | /** 58 | * @brief Adds an item to the toolbar. 59 | * 60 | * @note Items are not constructed until the toolbar is attached to the window. macOS toolbars 61 | * are populated by a delegate at the point of attaching the toolbar to the window. It 62 | * is important that you do not attach the toolbar until the items have been added 63 | * by this method. 64 | * 65 | * Every item requires a unique identifier string which is used by the toolbar and the 66 | * delegate as the method of correlating items. 67 | * 68 | * @param[in] icon a QIcon of the image to be displayed on the toolbar item. 69 | * @param[in] identifier the identifier of the toolbar item. 70 | * @param[in] label the label that is to appear on the toolbar item. 71 | * @param[in] paletteLabel the label that is used in the NSToolbar customisation palette. 72 | * 73 | * @returns a MacToolbarItem instance. 74 | */ 75 | auto addItem(const QIcon &icon, 76 | const QString &identifier, 77 | const QString &label=QString(), 78 | const QString &paletteLabel=QString()) -> Nedrysoft::MacHelper::MacToolbarItem *; 79 | 80 | /** 81 | * @brief Attaches the toolbar to the window. 82 | * 83 | * @note Must be called after the items have been added, calling this will result in the 84 | * NSToolbar calling the delegate to populate the toolbar. 85 | * 86 | * The window must be a QWindow subclass. 87 | * 88 | * @param[in] window the window to attach the window to. 89 | */ 90 | auto attachToWindow(QWidget *window) -> void; 91 | 92 | /** 93 | * @brief Returns the list of MacToolbarItems that have been added to the toolbar. 94 | * 95 | * @returns the list of toolbar items. 96 | */ 97 | auto items() -> QList; 98 | 99 | /** 100 | * @brief Instructs macOS that this is a preferences style toolbar. 101 | * 102 | * @note Under macOS 11 (Big Sur) or later, the style of a preferences toolbar is different 103 | * to a normal toolbar, this function will enable this style. 104 | */ 105 | auto enablePreferencesToolbar() -> void; 106 | 107 | private: 108 | //! @cond 109 | 110 | NSToolbar *m_toolbar; 111 | NSWindow *m_window; 112 | ToolbarDelegate *m_toolbarDelegate; 113 | 114 | QList m_items; 115 | QWindow *m_parentWindow; 116 | bool m_isPreferences; 117 | 118 | //! @endcond 119 | }; 120 | }} 121 | 122 | #endif // NEDRYSOFT_MACTOOLBAR_H 123 | -------------------------------------------------------------------------------- /src/StatusbarHelper.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 04/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #include "StatusbarHelper.h" 23 | 24 | #include 25 | 26 | constexpr auto StatusbarIconSize = 20; 27 | 28 | @implementation StatusbarHelper 29 | 30 | - (void) statusBarItemClicked:(NSStatusBarButton *) sender { 31 | NSEventType eventType = [[[NSApplication sharedApplication] currentEvent] type]; 32 | 33 | Nedrysoft::MacHelper::MouseButton button = Nedrysoft::MacHelper::MouseButton::Unknown; 34 | 35 | if (eventType==NSEventTypeRightMouseUp) { 36 | button = Nedrysoft::MacHelper::MouseButton::Right; 37 | } else if (eventType==NSEventTypeLeftMouseUp) { 38 | button = Nedrysoft::MacHelper::MouseButton::Left; 39 | } 40 | 41 | Q_EMIT m_menubarIcon->clicked(button); 42 | } 43 | 44 | - (id) initWithMenuBarIcon:(Nedrysoft::MacHelper::MacMenubarIcon *) menubarIcon { 45 | self = [super init]; 46 | 47 | if (!self) { 48 | return nil; 49 | } 50 | 51 | NSStatusBar *systemStatusBar = [NSStatusBar systemStatusBar]; 52 | 53 | m_menubarIcon = menubarIcon; 54 | 55 | m_statusbarItem = [systemStatusBar statusItemWithLength:NSSquareStatusItemLength]; 56 | 57 | CGImageRef imageRef; 58 | 59 | if (menubarIcon->pixmap().width() != StatusbarIconSize) { 60 | imageRef = menubarIcon->pixmap().scaled(StatusbarIconSize, StatusbarIconSize).toImage().toCGImage(); 61 | } else { 62 | imageRef = menubarIcon->pixmap().toImage().toCGImage(); 63 | } 64 | 65 | m_button = [m_statusbarItem button]; 66 | 67 | NSImage *nativeImage = [[NSImage alloc] initWithCGImage:imageRef 68 | size:NSMakeSize(StatusbarIconSize, StatusbarIconSize)]; 69 | 70 | [m_button setImage:nativeImage]; 71 | 72 | [m_button setTarget:self]; 73 | [m_button setAction:@selector(statusBarItemClicked:)]; 74 | [m_button sendActionOn: NSEventMaskLeftMouseUp | NSEventMaskRightMouseUp]; 75 | 76 | [nativeImage release]; 77 | 78 | return self; 79 | } 80 | 81 | - (NSRect) buttonRect { 82 | return [m_button visibleRect]; 83 | } 84 | 85 | - (NSView *) button { 86 | return m_button; 87 | } 88 | 89 | - (void) updatePixmap { 90 | CGImageRef imageRef; 91 | 92 | if (m_menubarIcon->pixmap().width() != StatusbarIconSize) { 93 | imageRef = m_menubarIcon->pixmap().scaled(StatusbarIconSize, StatusbarIconSize).toImage().toCGImage(); 94 | } else { 95 | imageRef = m_menubarIcon->pixmap().toImage().toCGImage(); 96 | } 97 | 98 | NSImage *nativeImage = [[NSImage alloc] initWithCGImage:imageRef 99 | size:NSMakeSize(StatusbarIconSize, StatusbarIconSize)]; 100 | 101 | [m_button setImage:nativeImage]; 102 | 103 | [nativeImage release]; 104 | } 105 | 106 | - (void) setVisible:(bool) visible { 107 | [m_button setHidden: !visible]; 108 | } 109 | 110 | - (void) showMenu:(QMenu *) menu { 111 | m_nativeMenu = [[NSMenu alloc] init]; 112 | 113 | auto itemIndex = 1; 114 | 115 | int previousISeparatorItem = false; 116 | 117 | for (auto action : menu->actions()) { 118 | NSMenuItem *menuItem; 119 | 120 | if (action->isSeparator()) { 121 | if (previousISeparatorItem) { 122 | continue; 123 | } 124 | 125 | menuItem = [NSMenuItem separatorItem]; 126 | 127 | previousISeparatorItem = true; 128 | } else { 129 | menuItem = [[NSMenuItem alloc] init]; 130 | 131 | [menuItem setTitle: action->text().toNSString()]; 132 | [menuItem setTarget: self]; 133 | [menuItem setAction: @selector(performAction:)]; 134 | 135 | previousISeparatorItem = false; 136 | } 137 | 138 | m_actionMap[itemIndex] = action; 139 | 140 | [menuItem setTag: itemIndex++]; 141 | 142 | [m_nativeMenu addItem:menuItem]; 143 | } 144 | 145 | m_delegate = [m_nativeMenu delegate]; 146 | 147 | [m_nativeMenu setDelegate: self]; 148 | 149 | [m_statusbarItem setMenu: m_nativeMenu]; 150 | 151 | [m_button performClick:nil]; 152 | } 153 | 154 | - (void) menuDidClose:(NSMenu *) menu { 155 | [m_statusbarItem setMenu: nil]; 156 | 157 | [menu release]; 158 | 159 | Q_EMIT m_menubarIcon->menuClosed(m_menu); 160 | } 161 | 162 | - (void) performAction:(id) sender { 163 | NSMenuItem *menuItem = (NSMenuItem *) sender; 164 | 165 | if (menuItem!=nil) { 166 | int itemIndex = [menuItem tag]; 167 | 168 | if (itemIndex) { 169 | auto action = m_actionMap[itemIndex]; 170 | 171 | if (action) { 172 | action->trigger(); 173 | } 174 | } 175 | } 176 | } 177 | 178 | @end 179 | -------------------------------------------------------------------------------- /src/MacToolbarItem.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 07/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #ifndef NEDRYSOFT_MACTOOLBARITEM_H 23 | #define NEDRYSOFT_MACTOOLBARITEM_H 24 | 25 | #include 26 | #include 27 | 28 | class QWidget; 29 | class QWindow; 30 | 31 | Q_FORWARD_DECLARE_OBJC_CLASS(NSToolbar); 32 | Q_FORWARD_DECLARE_OBJC_CLASS(ToolbarDelegate); 33 | Q_FORWARD_DECLARE_OBJC_CLASS(NSToolbarItem); 34 | Q_FORWARD_DECLARE_OBJC_CLASS(NSWindow); 35 | 36 | namespace Nedrysoft { namespace MacHelper { 37 | /** 38 | * @brief The MacToolbarItem class represents a toolbar item. It is a container for the native 39 | * NSToolbarItem. 40 | * 41 | * @class Nedrysoft::MacHelper::MacToolbarItem MacToolbarItem.h 42 | */ 43 | class MacToolbarItem : 44 | public QObject { 45 | 46 | public: 47 | Q_OBJECT 48 | 49 | public: 50 | /** 51 | * @brief Constructs a new toolbar item with the given parameters. 52 | * 53 | * @note The identifier should be unique as it is used by the NSToolbar and its delegate to 54 | * correlate items. 55 | * 56 | * If label or palette label are omitted, they are set to the identifier value. 57 | * 58 | * @param[in] icon the items icon. 59 | * @param[in] identifier the identifier used by the toolbar and delegate. 60 | * @param[in] label the label for the item. 61 | * @param[in] paletteLabel the label that is displayed in the toolbar customiser. 62 | */ 63 | MacToolbarItem(const QIcon &icon, 64 | const QString &identifier, 65 | const QString &label = QString(), 66 | const QString &paletteLabel = QString()); 67 | 68 | /** 69 | * @brief Returns the identifier of the item. 70 | * 71 | * @returns the items identifier. 72 | */ 73 | auto identifier() -> QString; 74 | 75 | /** 76 | * @brief Returns the icon of the item. 77 | * 78 | * @returns the items icon. 79 | */ 80 | auto icon() -> QIcon; 81 | 82 | /** 83 | * @brief Sets the icon for the item. 84 | */ 85 | auto setIcon(QIcon icon) -> void; 86 | 87 | /** 88 | * @brief Returns the native NSToolBarItem assigned to the item. 89 | * 90 | * @note This will be a nullptr until the toolbar has been attached to the window and the 91 | * delegate called to populate the toolbar. 92 | * 93 | * @returns the native NSToolbarItem if available; otherwise nullptr. 94 | */ 95 | auto item() -> NSToolbarItem *; 96 | 97 | /** 98 | * @brief Returns the label that is displayed on the toolbar item. 99 | * 100 | * @note If this has not been set, then it defaults to the items identifier. 101 | * 102 | * @returns the toolbar label. 103 | */ 104 | auto label() -> QString; 105 | 106 | /** 107 | * @brief Returns the label that is displayed on customiser for this item. 108 | * 109 | * @note If this has not been set, then it defaults to the items identifier. 110 | * 111 | * @returns the toolbars palette label. 112 | */ 113 | auto paletteLabel() -> QString; 114 | 115 | /** 116 | * @brief Sets the native toolbar item. 117 | * 118 | * @note Should not be directly called, the delegate will call this when the toolbar is 119 | * populated. 120 | * 121 | * @param[in] item the native item to associate with this toolbar item. 122 | */ 123 | auto setToolbarItem(NSToolbarItem *item) -> void; 124 | 125 | public: 126 | /** 127 | * @brief This signal is emitted when the item is clicked and becomes active. 128 | */ 129 | Q_SIGNAL void activated(); 130 | 131 | private: 132 | //! @cond 133 | 134 | NSToolbarItem *m_toolbarItem; 135 | QString m_identifier; 136 | QIcon m_icon; 137 | QString m_paletteLabel; 138 | QString m_label; 139 | 140 | //! @endcond 141 | }; 142 | }} 143 | 144 | #endif // NEDRYSOFT_MACTOOLBARITEM_H 145 | -------------------------------------------------------------------------------- /src/MacHelper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 07/12/2020. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #ifndef NEDRYSOFT_MACHELPER_H 23 | #define NEDRYSOFT_MACHELPER_H 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | class QMacToolBar; 30 | 31 | Q_FORWARD_DECLARE_OBJC_CLASS(NSObject); 32 | 33 | namespace Nedrysoft { namespace MacHelper { 34 | namespace StandardImage { 35 | /** 36 | * @brief StandardImageName enum. 37 | * 38 | * @details Provides parity naming with AppKit standard images. 39 | */ 40 | enum StandardImageName { 41 | NSImageNamePreferencesGeneral, /**< the general icon in preferences. */ 42 | NSImageNameUserAccounts, /**< the user accounts icon in preferences. */ 43 | NSImageNameAdvanced /**< the name icon in preferences. */ 44 | }; 45 | } 46 | 47 | namespace AlertButton { 48 | /** 49 | * @brief AlertButton enum. 50 | * 51 | * @details Equivalent values from NSAlert 52 | */ 53 | enum AlertButtonResult { 54 | FirstButton = 1000, /**< the first button in an NSAlert **/ 55 | SecondButton, /**< the second button in a NSAlert **/ 56 | ThirdButton /**< the third button in a NSAlert */ 57 | }; 58 | 59 | /** 60 | * @brief Returns the NSAlert button identifier for the zero based index. 61 | * 62 | * @note This is a constexpr function which the preprocessor will convert to a constant value, and 63 | * this allows it to be used in switch statements. 64 | * 65 | * @param[in] n the zero based index of the button; 66 | * 67 | * @returns the macOS identifier of the button that was 68 | */ 69 | constexpr auto Button(int n) -> int { 70 | return FirstButton+n-1; 71 | } 72 | } 73 | 74 | /** 75 | * @brief The MacHelper class. 76 | * 77 | * @details Provides helper functions to allow more native mac os functionality where required. 78 | * 79 | * @class Nedrysoft::MacHelper::MacHelper MacHelper.h 80 | */ 81 | class MacHelper { 82 | public: 83 | /** 84 | * @brief Returns a pixmap of a mac standard icon. 85 | * 86 | * @param[in] imageName the standard image identifier. 87 | * @param[in] imageSize the required size of the resulting image. 88 | * 89 | * @returns A pixmap of the named image. 90 | */ 91 | static auto standardImage(StandardImage::StandardImageName imageName, QSize imageSize) -> QPixmap; 92 | 93 | /** 94 | * @brief Shows a native macOS alert dialog. 95 | * 96 | * @param[in] parent the owner widget. 97 | * @param[in] messageText the main message text. 98 | * @param[in] informativeText the text to appear below the main message. 99 | * @param[in] buttons the list of buttons to appear. 100 | * @param[in] alertFunction the function to be called when the user has made a selection. 101 | * 102 | * @note macOS returns NSAlertFirstButtonReturn, NSAlertSecondButtonReturn, NSAlertThirdButtonReturn 103 | * NSAlertFirstButtonReturn+n etc. NSAlertFirstButtonReturn is the constant 1000. 104 | * 105 | * @see Nedrysoft::AlertButton 106 | */ 107 | static auto nativeAlert( 108 | QWidget *parent, 109 | const QString &messageText, 110 | const QString &informativeText, 111 | const QStringList &buttons, 112 | std::function alertFunction 113 | ) -> void; 114 | 115 | /** 116 | * @brief Loads an image via NSImage and returns a TIFF. 117 | * 118 | * @details NSImage is able to load various image types, due to a bug in the DevIL library that causes it to 119 | * crash when loading a .icns file, we use this class as a first stage image loader. 120 | * 121 | * If the image is loaded, then a tiff representation of the image is returned in the data parameter, 122 | * ownership of the data is passed to the caller and they are responsible for freeing the allocated memory. 123 | * 124 | * @param[in] filename the file to be loaded. 125 | * @param[out] data the binary data containing the TIFF image. 126 | * @param[out] length the size of the TIFF image in bytes. 127 | * 128 | * @returns true if the image was loaded successfully; otherwise false. 129 | */ 130 | static auto loadImage(const QString &filename, std::shared_ptr &data, unsigned int *length) -> bool; 131 | 132 | /** 133 | * @brief Returns a TIFF image of the files icon. 134 | * 135 | * @param[in] filename the file whose icon is to be retrieved. 136 | * @param[out] data the binary data containing the TIFF image. 137 | * @param[out] length the size of the TIFF image in bytes. 138 | * @param[in] width the requested width of the image (images may have multiple sizes inside). 139 | * @param[in] height the requested height of the image (images may have multiple sizes inside). 140 | * 141 | * @returns true if the image was loaded successfully; otherwise false. 142 | */ 143 | static auto imageForFile(const QString &filename, std::shared_ptr &data, unsigned int *length, int width, int height) -> bool; 144 | 145 | /** 146 | * @brief Returns the name of the font used in the GUI of macOS. 147 | * 148 | * @returns the name of the system font. 149 | */ 150 | static auto systemFontName() -> QString; 151 | 152 | /** 153 | * @brief Returns the path of the font specified by fontName. 154 | * 155 | * @param[in] fontName is the name of the font to find. 156 | * 157 | * @returns the path to the font if found; otherwise an empty string. 158 | */ 159 | static auto fontFilename(const QString &fontName) -> QString; 160 | 161 | /** 162 | * @brief Changes the native toolbar to a preferences toolbar. 163 | * 164 | * @param[in] window the window with the native toolbar. 165 | */ 166 | static void enablePreferencesToolbar(QWidget *window); 167 | 168 | /** 169 | * @brief Sets the title bar to the given colour. 170 | * 171 | * @param[in] window the window to change the title bar of. 172 | * @param[in] colour the desired color. 173 | * @param[in] brightText true if the text should be light; otherwie false; 174 | */ 175 | auto setTitlebarColour(QWidget *window, QColor colour, bool brightText) -> void; 176 | 177 | /** 178 | * @brief Clears the titlebar colour. 179 | * 180 | * @param[in] window the window to clear the title bar colour on. 181 | * @param[in] isDarkMode true if resetting to dark mode; otherwse false. 182 | */ 183 | auto clearTitlebarColour(QWidget *window, bool isDarkMode) -> void; 184 | 185 | /** 186 | * @brief Hides the application, closes the windows but remains running in the background. 187 | */ 188 | static auto hideApplication() -> void; 189 | 190 | /** 191 | * @brief Shows the application if it is hidden. 192 | */ 193 | static auto showApplication() -> void; 194 | 195 | /** 196 | * @brief prevent app nap from being used. 197 | */ 198 | static auto enableAppNap() -> void; 199 | 200 | /** 201 | * @brief prevent app nap from being used. 202 | */ 203 | static auto disableAppNap(const QString &reason) -> void; 204 | 205 | private: 206 | static int m_appNapCount; 207 | static QRecursiveMutex m_appNapMutex; 208 | static NSObject *m_appNapActivity; 209 | //static QStack m_appNapReasons; 210 | }; 211 | }} 212 | 213 | #endif // NEDRYSOFT_MACHELPER_H 214 | -------------------------------------------------------------------------------- /src/MacHelper.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Adrian Carpenter 3 | * 4 | * This file is part of the Nedrysoft MacHelper library. (https://github.com/nedrysoft/MacHelper) 5 | * 6 | * Created by Adrian Carpenter on 07/05/2021. 7 | * 8 | * This program is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program. If not, see . 20 | */ 21 | 22 | #include "MacHelper.h" 23 | 24 | #include 25 | #include 26 | 27 | #import 28 | 29 | constexpr auto BitsPerPixel = 8; 30 | constexpr auto SamplesPerPixel = 4; 31 | constexpr auto SystemFontSize = 12; 32 | 33 | int Nedrysoft::MacHelper::MacHelper::m_appNapCount = 0; 34 | NSObject *Nedrysoft::MacHelper::MacHelper::m_appNapActivity = nullptr; 35 | QRecursiveMutex Nedrysoft::MacHelper::MacHelper::m_appNapMutex = QRecursiveMutex(); 36 | 37 | auto Nedrysoft::MacHelper::MacHelper::setTitlebarColour(QWidget *window, QColor colour, bool brightText) -> void { 38 | auto nativeView = reinterpret_cast(window->winId()); 39 | 40 | if (!nativeView) { 41 | return; 42 | } 43 | 44 | Q_ASSERT_X([nativeView isKindOfClass:[NSView class]], static_cast(__FUNCTION__), 45 | "Object was not a NSView"); 46 | 47 | auto nativeWindow = [nativeView window]; 48 | 49 | if (nativeWindow == nil) { 50 | return; 51 | } 52 | 53 | Q_ASSERT_X([nativeWindow isKindOfClass:[NSWindow class]], static_cast(__FUNCTION__), 54 | "Object was not a NSWindow"); 55 | 56 | [nativeWindow setTitlebarAppearsTransparent:true]; 57 | 58 | [nativeWindow setBackgroundColor:[NSColor colorWithRed:colour.redF() 59 | green:colour.greenF() 60 | blue:colour.blueF() 61 | alpha:colour.alphaF()]]; 62 | 63 | if (brightText) { 64 | [nativeWindow setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]]; 65 | } else { 66 | [nativeWindow setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantLight]]; 67 | } 68 | } 69 | 70 | auto Nedrysoft::MacHelper::MacHelper::clearTitlebarColour(QWidget *window, bool isDarkMode) -> void { 71 | auto nativeView = reinterpret_cast(window->winId()); 72 | 73 | if (!nativeView) { 74 | return; 75 | } 76 | 77 | Q_ASSERT_X([nativeView isKindOfClass:[NSView class]], static_cast(__FUNCTION__), 78 | "Object was not a NSView"); 79 | 80 | auto nativeWindow = [nativeView window]; 81 | 82 | if (nativeWindow == nil) { 83 | return; 84 | } 85 | 86 | Q_ASSERT_X([nativeWindow isKindOfClass:[NSWindow class]], static_cast(__FUNCTION__), 87 | "Object was not a NSWindow"); 88 | 89 | [nativeWindow setTitlebarAppearsTransparent:false]; 90 | 91 | if (@available(macOS 10.14, *)) { 92 | if (isDarkMode) { 93 | [nativeWindow setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]]; 94 | } else { 95 | [nativeWindow setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameAqua]]; 96 | } 97 | } else { 98 | [nativeWindow setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameAqua]]; 99 | } 100 | } 101 | 102 | void Nedrysoft::MacHelper::MacHelper::enablePreferencesToolbar(QWidget *window) { 103 | if (@available(macOS 11, *)) { 104 | auto nativeView = reinterpret_cast(window->winId()); 105 | 106 | if (!nativeView) { 107 | return; 108 | } 109 | 110 | Q_ASSERT_X([nativeView isKindOfClass:[NSView class]], static_cast(__FUNCTION__), 111 | "Object was not a NSView"); 112 | 113 | auto nativeWindow = [nativeView window]; 114 | 115 | if (nativeWindow == nil) { 116 | return; 117 | } 118 | 119 | Q_ASSERT_X([nativeWindow isKindOfClass:[NSWindow class]], static_cast(__FUNCTION__), 120 | "Object was not a NSWindow"); 121 | 122 | if ([nativeWindow respondsToSelector:@selector(setToolbarStyle:)]) { 123 | [nativeWindow setToolbarStyle:NSWindowToolbarStylePreference]; 124 | } 125 | } 126 | } 127 | 128 | auto Nedrysoft::MacHelper::MacHelper::standardImage( 129 | StandardImage::StandardImageName standardImage, 130 | QSize imageSize)->QPixmap { 131 | 132 | auto bitmapRepresentation = [[NSBitmapImageRep alloc] 133 | initWithBitmapDataPlanes: nullptr 134 | pixelsWide: imageSize.width() 135 | pixelsHigh: imageSize.height() 136 | bitsPerSample: BitsPerPixel 137 | samplesPerPixel: SamplesPerPixel 138 | hasAlpha: YES 139 | isPlanar: NO 140 | colorSpaceName: NSDeviceRGBColorSpace 141 | bitmapFormat: NSBitmapFormatAlphaFirst 142 | bytesPerRow: 0 143 | bitsPerPixel: 0 144 | ]; 145 | 146 | NSString *nativeImageName = nullptr; 147 | 148 | switch(standardImage) { 149 | case Nedrysoft::MacHelper::StandardImage::NSImageNamePreferencesGeneral: { 150 | nativeImageName = NSImageNamePreferencesGeneral; 151 | break; 152 | } 153 | 154 | case Nedrysoft::MacHelper::StandardImage::NSImageNameUserAccounts: { 155 | nativeImageName = NSImageNameUserAccounts; 156 | break; 157 | } 158 | 159 | case Nedrysoft::MacHelper::StandardImage::NSImageNameAdvanced: { 160 | nativeImageName = NSImageNameAdvanced; 161 | break; 162 | } 163 | } 164 | 165 | auto nsImage = [NSImage imageNamed: nativeImageName]; 166 | 167 | [NSGraphicsContext saveGraphicsState]; 168 | 169 | [NSGraphicsContext setCurrentContext: [NSGraphicsContext graphicsContextWithBitmapImageRep: bitmapRepresentation]]; 170 | 171 | [nsImage drawInRect: NSMakeRect(0, 0, imageSize.width(), imageSize.height()) 172 | fromRect: NSZeroRect 173 | operation: NSCompositingOperationSourceOver 174 | fraction: 1]; 175 | 176 | [NSGraphicsContext restoreGraphicsState]; 177 | 178 | auto imageRef = [bitmapRepresentation CGImage]; 179 | 180 | auto pixelData = CFDataGetBytePtr(CGDataProviderCopyData(CGImageGetDataProvider(imageRef))); 181 | 182 | auto image = QImage(pixelData, imageSize.width(), imageSize.height(),QImage::Format_ARGB32); 183 | 184 | [bitmapRepresentation release]; 185 | 186 | return QPixmap::fromImage(image); 187 | } 188 | 189 | auto Nedrysoft::MacHelper::MacHelper::nativeAlert( 190 | QWidget *parent, 191 | const QString &messageText, 192 | const QString &informativeText, 193 | const QStringList &buttons, 194 | std::function alertFunction) -> void { 195 | 196 | Q_UNUSED(parent) 197 | 198 | // ok, this looks odd, but without the singleShot the dialog fails to display properly and flashes up on 199 | // screen briefly before closing itself, this only happens under certain circumstances but this fixes 200 | // the issue. 201 | 202 | QTimer::singleShot(0, [=]() { 203 | auto alert = [[NSAlert alloc] init]; 204 | 205 | for (auto &button : buttons) { 206 | [alert addButtonWithTitle:button.toNSString()]; 207 | } 208 | 209 | [alert setMessageText:messageText.toNSString()]; 210 | [alert setInformativeText:informativeText.toNSString()]; 211 | [alert setAlertStyle:NSAlertStyleInformational]; 212 | 213 | auto returnValue = [alert runModal]; 214 | 215 | [alert release]; 216 | 217 | alertFunction(static_cast(returnValue)); 218 | }); 219 | } 220 | 221 | auto Nedrysoft::MacHelper::MacHelper::loadImage( 222 | const QString &filename, 223 | std::shared_ptr &data, 224 | unsigned int *length) -> bool { 225 | 226 | auto fileName = filename.toNSString(); 227 | 228 | auto loadedImage = [[NSImage alloc] initWithContentsOfFile:fileName]; 229 | 230 | if (loadedImage.isValid) { 231 | NSData *tiffData = [loadedImage TIFFRepresentation]; 232 | 233 | data = std::make_shared(static_cast(malloc(tiffData.length))); 234 | 235 | *length = static_cast(tiffData.length); 236 | 237 | memcpy(data.get(), tiffData.bytes, *length); 238 | 239 | [loadedImage release]; 240 | 241 | return true; 242 | } 243 | 244 | [loadedImage release]; 245 | 246 | return false; 247 | } 248 | 249 | auto Nedrysoft::MacHelper::MacHelper::imageForFile( 250 | const QString &filename, 251 | std::shared_ptr &data, 252 | unsigned int *length, 253 | int width, 254 | int height) -> bool { 255 | 256 | auto loadedImage = [[NSWorkspace sharedWorkspace] iconForFile:filename.toNSString()]; 257 | 258 | [loadedImage setSize:NSMakeSize(width,height)]; 259 | 260 | if (loadedImage.isValid) { 261 | NSData *tiffData = [loadedImage TIFFRepresentation]; 262 | 263 | data = std::make_shared(static_cast(malloc(tiffData.length))); 264 | 265 | *length = static_cast(tiffData.length); 266 | 267 | memcpy(data.get(), tiffData.bytes, *length); 268 | 269 | return true; 270 | } 271 | 272 | return false; 273 | } 274 | 275 | auto Nedrysoft::MacHelper::MacHelper::systemFontName() -> QString { 276 | auto font = [NSFont systemFontOfSize: SystemFontSize]; 277 | 278 | return QString([[font fontName] cStringUsingEncoding:[NSString defaultCStringEncoding]]); 279 | } 280 | 281 | auto Nedrysoft::MacHelper::MacHelper::fontFilename(const QString& fontName) ->QString { 282 | auto font = [NSFont fontWithName: [NSString stringWithCString: fontName.toLatin1().data() encoding: [NSString defaultCStringEncoding]] size: SystemFontSize]; 283 | 284 | if (font) { 285 | auto fontRef = CTFontDescriptorCreateWithNameAndSize(reinterpret_cast([font fontName]), [font pointSize]); 286 | 287 | auto url = static_cast(CTFontDescriptorCopyAttribute(fontRef, kCTFontURLAttribute)); 288 | 289 | auto fontPath = [NSString stringWithString: [(NSURL *) CFBridgingRelease(url) path]]; 290 | 291 | return QString([fontPath cStringUsingEncoding: [NSString defaultCStringEncoding]]); 292 | } 293 | 294 | return QString(); 295 | } 296 | 297 | auto Nedrysoft::MacHelper::MacHelper::hideApplication() -> void { 298 | [[NSApplication sharedApplication] setActivationPolicy:NSApplicationActivationPolicyProhibited]; 299 | } 300 | 301 | auto Nedrysoft::MacHelper::MacHelper::showApplication() -> void { 302 | NSApplication *applicationInstance = [NSApplication sharedApplication]; 303 | 304 | [applicationInstance setActivationPolicy:NSApplicationActivationPolicyRegular]; 305 | [applicationInstance activateIgnoringOtherApps:YES]; 306 | } 307 | 308 | auto Nedrysoft::MacHelper::MacHelper::disableAppNap(const QString &reason) -> void { 309 | QMutexLocker mutexLocker(&m_appNapMutex); 310 | 311 | if (m_appNapActivity == nil) { 312 | if ([[NSProcessInfo processInfo] respondsToSelector:@selector(beginActivityWithOptions:reason:)]) { 313 | m_appNapActivity = [[NSProcessInfo processInfo] beginActivityWithOptions:NSActivityUserInitiatedAllowingIdleSystemSleep reason:reason.toNSString()]; 314 | } 315 | 316 | if (m_appNapActivity) { 317 | m_appNapCount++; 318 | } 319 | } 320 | } 321 | 322 | auto Nedrysoft::MacHelper::MacHelper::enableAppNap() -> void { 323 | QMutexLocker mutexLocker(&m_appNapMutex); 324 | 325 | if (m_appNapCount) { 326 | m_appNapCount--; 327 | 328 | if (m_appNapCount == 0) { 329 | if (m_appNapActivity != nil) { 330 | [[NSProcessInfo processInfo] endActivity:m_appNapActivity]; 331 | 332 | m_appNapActivity = nil; 333 | } 334 | } 335 | } 336 | } --------------------------------------------------------------------------------