• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// dear imgui: Platform Binding for OSX / Cocoa
2// This needs to be used along with a Renderer (e.g. OpenGL2, OpenGL3, Vulkan, Metal..)
3// [BETA] Beta bindings, not well tested. If you want a portable application, prefer using the Glfw or SDL platform bindings on Mac.
4
5// Issues:
6// [ ] Platform: Keys are all generally very broken. Best using [event keycode] and not [event characters]..
7// [ ] Platform: Mouse cursor shapes and visibility are not supported (see end of https://github.com/glfw/glfw/issues/427)
8
9#include "imgui.h"
10#include "imgui_impl_osx.h"
11#import <Cocoa/Cocoa.h>
12
13// CHANGELOG
14// (minor and older changes stripped away, please see git history for details)
15//  2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
16//  2018-07-07: Initial version.
17
18// Data
19static CFAbsoluteTime g_Time = 0.0;
20
21// Functions
22bool ImGui_ImplOSX_Init()
23{
24    ImGuiIO& io = ImGui::GetIO();
25
26    // Setup back-end capabilities flags
27    //io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;         // We can honor GetMouseCursor() values (optional)
28    //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;          // We can honor io.WantSetMousePos requests (optional, rarely used)
29    //io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports;    // We can create multi-viewports on the Platform side (optional)
30    //io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy)
31    io.BackendPlatformName = "imgui_impl_osx";
32
33    // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array.
34    const int offset_for_function_keys = 256 - 0xF700;
35    io.KeyMap[ImGuiKey_Tab]         = '\t';
36    io.KeyMap[ImGuiKey_LeftArrow]   = NSLeftArrowFunctionKey + offset_for_function_keys;
37    io.KeyMap[ImGuiKey_RightArrow]  = NSRightArrowFunctionKey + offset_for_function_keys;
38    io.KeyMap[ImGuiKey_UpArrow]     = NSUpArrowFunctionKey + offset_for_function_keys;
39    io.KeyMap[ImGuiKey_DownArrow]   = NSDownArrowFunctionKey + offset_for_function_keys;
40    io.KeyMap[ImGuiKey_PageUp]      = NSPageUpFunctionKey + offset_for_function_keys;
41    io.KeyMap[ImGuiKey_PageDown]    = NSPageDownFunctionKey + offset_for_function_keys;
42    io.KeyMap[ImGuiKey_Home]        = NSHomeFunctionKey + offset_for_function_keys;
43    io.KeyMap[ImGuiKey_End]         = NSEndFunctionKey + offset_for_function_keys;
44    io.KeyMap[ImGuiKey_Insert]      = NSInsertFunctionKey + offset_for_function_keys;
45    io.KeyMap[ImGuiKey_Delete]      = NSDeleteFunctionKey + offset_for_function_keys;
46    io.KeyMap[ImGuiKey_Backspace]   = 127;
47    io.KeyMap[ImGuiKey_Space]       = 32;
48    io.KeyMap[ImGuiKey_Enter]       = 13;
49    io.KeyMap[ImGuiKey_Escape]      = 27;
50    io.KeyMap[ImGuiKey_A]           = 'A';
51    io.KeyMap[ImGuiKey_C]           = 'C';
52    io.KeyMap[ImGuiKey_V]           = 'V';
53    io.KeyMap[ImGuiKey_X]           = 'X';
54    io.KeyMap[ImGuiKey_Y]           = 'Y';
55    io.KeyMap[ImGuiKey_Z]           = 'Z';
56
57    io.SetClipboardTextFn = [](void*, const char* str) -> void
58    {
59        NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
60        [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString] owner:nil];
61        [pasteboard setString:[NSString stringWithUTF8String:str] forType:NSPasteboardTypeString];
62    };
63
64    io.GetClipboardTextFn = [](void*) -> const char*
65    {
66        NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
67        NSString* available = [pasteboard availableTypeFromArray: [NSArray arrayWithObject:NSPasteboardTypeString]];
68        if (![available isEqualToString:NSPasteboardTypeString])
69            return NULL;
70
71        NSString* string = [pasteboard stringForType:NSPasteboardTypeString];
72        if (string == nil)
73            return NULL;
74
75        const char* string_c = (const char*)[string UTF8String];
76        size_t string_len = strlen(string_c);
77        static ImVector<char> s_clipboard;
78        s_clipboard.resize((int)string_len + 1);
79        strcpy(s_clipboard.Data, string_c);
80        return s_clipboard.Data;
81    };
82
83    return true;
84}
85
86void ImGui_ImplOSX_Shutdown()
87{
88}
89
90void ImGui_ImplOSX_NewFrame(NSView* view)
91{
92    // Setup display size
93    ImGuiIO& io = ImGui::GetIO();
94    const float dpi = [view.window backingScaleFactor];
95    io.DisplaySize = ImVec2((float)view.bounds.size.width, (float)view.bounds.size.height);
96    io.DisplayFramebufferScale = ImVec2(dpi, dpi);
97
98    // Setup time step
99    if (g_Time == 0.0)
100        g_Time = CFAbsoluteTimeGetCurrent();
101    CFAbsoluteTime current_time = CFAbsoluteTimeGetCurrent();
102    io.DeltaTime = current_time - g_Time;
103    g_Time = current_time;
104}
105
106static int mapCharacterToKey(int c)
107{
108    if (c >= 'a' && c <= 'z')
109        return c - 'a' + 'A';
110    if (c == 25) // SHIFT+TAB -> TAB
111        return 9;
112    if (c >= 0 && c < 256)
113        return c;
114    if (c >= 0xF700 && c < 0xF700 + 256)
115        return c - 0xF700 + 256;
116    return -1;
117}
118
119static void resetKeys()
120{
121    ImGuiIO& io = ImGui::GetIO();
122    for (int n = 0; n < IM_ARRAYSIZE(io.KeysDown); n++)
123        io.KeysDown[n] = false;
124}
125
126bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view)
127{
128    ImGuiIO& io = ImGui::GetIO();
129
130    if (event.type == NSEventTypeLeftMouseDown || event.type == NSEventTypeRightMouseDown || event.type == NSEventTypeOtherMouseDown)
131    {
132        int button = (int)[event buttonNumber];
133        if (button >= 0 && button < IM_ARRAYSIZE(io.MouseDown))
134            io.MouseDown[button] = true;
135        return io.WantCaptureMouse;
136    }
137
138    if (event.type == NSEventTypeLeftMouseUp || event.type == NSEventTypeRightMouseUp || event.type == NSEventTypeOtherMouseUp)
139    {
140        int button = (int)[event buttonNumber];
141        if (button >= 0 && button < IM_ARRAYSIZE(io.MouseDown))
142            io.MouseDown[button] = false;
143        return io.WantCaptureMouse;
144    }
145
146    if (event.type == NSEventTypeMouseMoved || event.type == NSEventTypeLeftMouseDragged)
147    {
148        NSPoint mousePoint = event.locationInWindow;
149        mousePoint = [view convertPoint:mousePoint fromView:nil];
150        mousePoint = NSMakePoint(mousePoint.x, view.bounds.size.height - mousePoint.y);
151        io.MousePos = ImVec2(mousePoint.x, mousePoint.y);
152    }
153
154    if (event.type == NSEventTypeScrollWheel)
155    {
156        double wheel_dx = 0.0;
157        double wheel_dy = 0.0;
158
159        #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
160        if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6)
161        {
162            wheel_dx = [event scrollingDeltaX];
163            wheel_dy = [event scrollingDeltaY];
164            if ([event hasPreciseScrollingDeltas])
165            {
166                wheel_dx *= 0.1;
167                wheel_dy *= 0.1;
168            }
169        }
170        else
171        #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/
172        {
173            wheel_dx = [event deltaX];
174            wheel_dy = [event deltaY];
175        }
176
177        if (fabs(wheel_dx) > 0.0)
178            io.MouseWheelH += wheel_dx * 0.1f;
179        if (fabs(wheel_dy) > 0.0)
180            io.MouseWheel += wheel_dy * 0.1f;
181        return io.WantCaptureMouse;
182    }
183
184    // FIXME: All the key handling is wrong and broken. Refer to GLFW's cocoa_init.mm and cocoa_window.mm.
185    if (event.type == NSEventTypeKeyDown)
186    {
187        NSString* str = [event characters];
188        int len = (int)[str length];
189        for (int i = 0; i < len; i++)
190        {
191            int c = [str characterAtIndex:i];
192            if (c < 0xF700 && !io.KeyCtrl)
193                io.AddInputCharacter((unsigned short)c);
194
195            // We must reset in case we're pressing a sequence of special keys while keeping the command pressed
196            int key = mapCharacterToKey(c);
197            if (key != -1 && key < 256 && !io.KeyCtrl)
198                resetKeys();
199            if (key != -1)
200                io.KeysDown[key] = true;
201        }
202        return io.WantCaptureKeyboard;
203    }
204
205    if (event.type == NSEventTypeKeyUp)
206    {
207        NSString* str = [event characters];
208        int len = (int)[str length];
209        for (int i = 0; i < len; i++)
210        {
211            int c = [str characterAtIndex:i];
212            int key = mapCharacterToKey(c);
213            if (key != -1)
214                io.KeysDown[key] = false;
215        }
216        return io.WantCaptureKeyboard;
217    }
218
219    if (event.type == NSEventTypeFlagsChanged)
220    {
221        ImGuiIO& io = ImGui::GetIO();
222        unsigned int flags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
223
224        bool oldKeyCtrl = io.KeyCtrl;
225        bool oldKeyShift = io.KeyShift;
226        bool oldKeyAlt = io.KeyAlt;
227        bool oldKeySuper = io.KeySuper;
228        io.KeyCtrl      = flags & NSEventModifierFlagControl;
229        io.KeyShift     = flags & NSEventModifierFlagShift;
230        io.KeyAlt       = flags & NSEventModifierFlagOption;
231        io.KeySuper     = flags & NSEventModifierFlagCommand;
232
233        // We must reset them as we will not receive any keyUp event if they where pressed with a modifier
234        if ((oldKeyShift && !io.KeyShift) || (oldKeyCtrl && !io.KeyCtrl) || (oldKeyAlt && !io.KeyAlt) || (oldKeySuper && !io.KeySuper))
235            resetKeys();
236        return io.WantCaptureKeyboard;
237    }
238
239    return false;
240}
241