1
2#import "ViewController.h"
3#import "Renderer.h"
4#include "imgui.h"
5
6#if TARGET_OS_OSX
7#include "imgui_impl_osx.h"
8#endif
9
10@interface ViewController ()
11@property (nonatomic, readonly) MTKView *mtkView;
12@property (nonatomic, strong) Renderer *renderer;
13@end
14
15@implementation ViewController
16
17- (MTKView *)mtkView {
18    return (MTKView *)self.view;
19}
20
21- (void)viewDidLoad
22{
23    [super viewDidLoad];
24
25    self.mtkView.device = MTLCreateSystemDefaultDevice();
26
27    if (!self.mtkView.device) {
28        NSLog(@"Metal is not supported");
29        abort();
30    }
31
32    self.renderer = [[Renderer alloc] initWithView:self.mtkView];
33
34    [self.renderer mtkView:self.mtkView drawableSizeWillChange:self.mtkView.bounds.size];
35
36    self.mtkView.delegate = self.renderer;
37
38#if TARGET_OS_OSX
39    // Add a tracking area in order to receive mouse events whenever the mouse is within the bounds of our view
40    NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect
41                                                                options:NSTrackingMouseMoved | NSTrackingInVisibleRect | NSTrackingActiveAlways
42                                                                  owner:self
43                                                               userInfo:nil];
44    [self.view addTrackingArea:trackingArea];
45
46    // If we want to receive key events, we either need to be in the responder chain of the key view,
47    // or else we can install a local monitor. The consequence of this heavy-handed approach is that
48    // we receive events for all controls, not just Dear ImGui widgets. If we had native controls in our
49    // window, we'd want to be much more careful than just ingesting the complete event stream, though we
50    // do make an effort to be good citizens by passing along events when Dear ImGui doesn't want to capture.
51    NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged | NSEventTypeScrollWheel;
52    [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event) {
53        BOOL wantsCapture = ImGui_ImplOSX_HandleEvent(event, self.view);
54        if (event.type == NSEventTypeKeyDown && wantsCapture) {
55            return nil;
56        } else {
57            return event;
58        }
59
60    }];
61
62    ImGui_ImplOSX_Init();
63#endif
64}
65
66#if TARGET_OS_OSX
67
68- (void)mouseMoved:(NSEvent *)event {
69    ImGui_ImplOSX_HandleEvent(event, self.view);
70}
71
72- (void)mouseDown:(NSEvent *)event {
73    ImGui_ImplOSX_HandleEvent(event, self.view);
74}
75
76- (void)mouseUp:(NSEvent *)event {
77    ImGui_ImplOSX_HandleEvent(event, self.view);
78}
79
80- (void)mouseDragged:(NSEvent *)event {
81    ImGui_ImplOSX_HandleEvent(event, self.view);
82}
83
84- (void)scrollWheel:(NSEvent *)event {
85    ImGui_ImplOSX_HandleEvent(event, self.view);
86}
87
88#elif TARGET_OS_IOS
89
90// This touch mapping is super cheesy/hacky. We treat any touch on the screen
91// as if it were a depressed left mouse button, and we don't bother handling
92// multitouch correctly at all. This causes the "cursor" to behave very erratically
93// when there are multiple active touches. But for demo purposes, single-touch
94// interaction actually works surprisingly well.
95- (void)updateIOWithTouchEvent:(UIEvent *)event {
96    UITouch *anyTouch = event.allTouches.anyObject;
97    CGPoint touchLocation = [anyTouch locationInView:self.view];
98    ImGuiIO &io = ImGui::GetIO();
99    io.MousePos = ImVec2(touchLocation.x, touchLocation.y);
100
101    BOOL hasActiveTouch = NO;
102    for (UITouch *touch in event.allTouches) {
103        if (touch.phase != UITouchPhaseEnded && touch.phase != UITouchPhaseCancelled) {
104            hasActiveTouch = YES;
105            break;
106        }
107    }
108    io.MouseDown[0] = hasActiveTouch;
109}
110
111- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
112    [self updateIOWithTouchEvent:event];
113}
114
115- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
116    [self updateIOWithTouchEvent:event];
117}
118
119- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
120    [self updateIOWithTouchEvent:event];
121}
122
123- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
124    [self updateIOWithTouchEvent:event];
125}
126
127#endif
128
129@end
130
131