├── .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 | }
--------------------------------------------------------------------------------