imgui

FORK: Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
git clone https://git.neptards.moe/neptards/imgui.git
Log | Files | Refs

main.mm (13395B)


      1 // Dear ImGui: standalone example application for OSX + Metal.
      2 // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
      3 // Read online: https://github.com/ocornut/imgui/tree/master/docs
      4 
      5 #import <Foundation/Foundation.h>
      6 
      7 #if TARGET_OS_OSX
      8 #import <Cocoa/Cocoa.h>
      9 #else
     10 #import <UIKit/UIKit.h>
     11 #endif
     12 
     13 #import <Metal/Metal.h>
     14 #import <MetalKit/MetalKit.h>
     15 
     16 #include "imgui.h"
     17 #include "imgui_impl_metal.h"
     18 
     19 #if TARGET_OS_OSX
     20 #include "imgui_impl_osx.h"
     21 
     22 @interface ViewController : NSViewController
     23 @end
     24 #else
     25 @interface ViewController : UIViewController
     26 @end
     27 #endif
     28 
     29 @interface ViewController () <MTKViewDelegate>
     30 @property (nonatomic, readonly) MTKView *mtkView;
     31 @property (nonatomic, strong) id <MTLDevice> device;
     32 @property (nonatomic, strong) id <MTLCommandQueue> commandQueue;
     33 @end
     34 
     35 @implementation ViewController
     36 
     37 - (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil
     38 {
     39     self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
     40 
     41     _device = MTLCreateSystemDefaultDevice();
     42     _commandQueue = [_device newCommandQueue];
     43 
     44     if (!self.device)
     45     {
     46         NSLog(@"Metal is not supported");
     47         abort();
     48     }
     49 
     50     // Setup Dear ImGui context
     51     // FIXME: This example doesn't have proper cleanup...
     52     IMGUI_CHECKVERSION();
     53     ImGui::CreateContext();
     54     ImGuiIO& io = ImGui::GetIO(); (void)io;
     55     //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
     56     //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
     57 
     58     // Setup Dear ImGui style
     59     ImGui::StyleColorsDark();
     60     //ImGui::StyleColorsClassic();
     61 
     62     // Setup Renderer backend
     63     ImGui_ImplMetal_Init(_device);
     64 
     65     // Load Fonts
     66     // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
     67     // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
     68     // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
     69     // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
     70     // - Read 'docs/FONTS.txt' for more instructions and details.
     71     // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
     72     //io.Fonts->AddFontDefault();
     73     //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
     74     //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
     75     //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
     76     //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
     77     //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
     78     //IM_ASSERT(font != NULL);
     79 
     80     return self;
     81 }
     82 
     83 - (MTKView *)mtkView
     84 {
     85     return (MTKView *)self.view;
     86 }
     87 
     88 - (void)loadView
     89 {
     90     self.view = [[MTKView alloc] initWithFrame:CGRectMake(0, 0, 1200, 720)];
     91 }
     92 
     93 - (void)viewDidLoad
     94 {
     95     [super viewDidLoad];
     96 
     97     self.mtkView.device = self.device;
     98     self.mtkView.delegate = self;
     99 
    100 #if TARGET_OS_OSX
    101     // Add a tracking area in order to receive mouse events whenever the mouse is within the bounds of our view
    102     NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect
    103                                                                 options:NSTrackingMouseMoved | NSTrackingInVisibleRect | NSTrackingActiveAlways
    104                                                                   owner:self
    105                                                                userInfo:nil];
    106     [self.view addTrackingArea:trackingArea];
    107 
    108     // If we want to receive key events, we either need to be in the responder chain of the key view,
    109     // or else we can install a local monitor. The consequence of this heavy-handed approach is that
    110     // we receive events for all controls, not just Dear ImGui widgets. If we had native controls in our
    111     // window, we'd want to be much more careful than just ingesting the complete event stream.
    112     // To match the behavior of other backends, we pass every event down to the OS.
    113     NSEventMask eventMask = NSEventMaskKeyDown | NSEventMaskKeyUp | NSEventMaskFlagsChanged | NSEventTypeScrollWheel;
    114     [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^NSEvent * _Nullable(NSEvent *event)
    115     {
    116         ImGui_ImplOSX_HandleEvent(event, self.view);
    117         return event;
    118     }];
    119 
    120     ImGui_ImplOSX_Init();
    121 
    122 #endif
    123 }
    124 
    125 #pragma mark - Interaction
    126 
    127 #if TARGET_OS_OSX
    128 
    129 - (void)mouseMoved:(NSEvent *)event {
    130     ImGui_ImplOSX_HandleEvent(event, self.view);
    131 }
    132 
    133 - (void)mouseDown:(NSEvent *)event {
    134     ImGui_ImplOSX_HandleEvent(event, self.view);
    135 }
    136 
    137 - (void)rightMouseDown:(NSEvent *)event {
    138     ImGui_ImplOSX_HandleEvent(event, self.view);
    139 }
    140 
    141 - (void)otherMouseDown:(NSEvent *)event {
    142     ImGui_ImplOSX_HandleEvent(event, self.view);
    143 }
    144 
    145 - (void)mouseUp:(NSEvent *)event {
    146     ImGui_ImplOSX_HandleEvent(event, self.view);
    147 }
    148 
    149 - (void)rightMouseUp:(NSEvent *)event {
    150     ImGui_ImplOSX_HandleEvent(event, self.view);
    151 }
    152 
    153 - (void)otherMouseUp:(NSEvent *)event {
    154     ImGui_ImplOSX_HandleEvent(event, self.view);
    155 }
    156 
    157 - (void)mouseDragged:(NSEvent *)event {
    158     ImGui_ImplOSX_HandleEvent(event, self.view);
    159 }
    160 
    161 - (void)rightMouseDragged:(NSEvent *)event {
    162     ImGui_ImplOSX_HandleEvent(event, self.view);
    163 }
    164 
    165 - (void)otherMouseDragged:(NSEvent *)event {
    166     ImGui_ImplOSX_HandleEvent(event, self.view);
    167 }
    168 
    169 - (void)scrollWheel:(NSEvent *)event {
    170     ImGui_ImplOSX_HandleEvent(event, self.view);
    171 }
    172 
    173 #else
    174 
    175 // This touch mapping is super cheesy/hacky. We treat any touch on the screen
    176 // as if it were a depressed left mouse button, and we don't bother handling
    177 // multitouch correctly at all. This causes the "cursor" to behave very erratically
    178 // when there are multiple active touches. But for demo purposes, single-touch
    179 // interaction actually works surprisingly well.
    180 - (void)updateIOWithTouchEvent:(UIEvent *)event
    181 {
    182     UITouch *anyTouch = event.allTouches.anyObject;
    183     CGPoint touchLocation = [anyTouch locationInView:self.view];
    184     ImGuiIO &io = ImGui::GetIO();
    185     io.MousePos = ImVec2(touchLocation.x, touchLocation.y);
    186 
    187     BOOL hasActiveTouch = NO;
    188     for (UITouch *touch in event.allTouches)
    189     {
    190         if (touch.phase != UITouchPhaseEnded && touch.phase != UITouchPhaseCancelled)
    191         {
    192             hasActiveTouch = YES;
    193             break;
    194         }
    195     }
    196     io.MouseDown[0] = hasActiveTouch;
    197 }
    198 
    199 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    200 {
    201     [self updateIOWithTouchEvent:event];
    202 }
    203 
    204 - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    205 {
    206     [self updateIOWithTouchEvent:event];
    207 }
    208 
    209 - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    210 {
    211     [self updateIOWithTouchEvent:event];
    212 }
    213 
    214 - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    215 {
    216     [self updateIOWithTouchEvent:event];
    217 }
    218 
    219 #endif
    220 
    221 #pragma mark - MTKViewDelegate
    222 
    223 - (void)drawInMTKView:(MTKView*)view
    224 {
    225     ImGuiIO& io = ImGui::GetIO();
    226     io.DisplaySize.x = view.bounds.size.width;
    227     io.DisplaySize.y = view.bounds.size.height;
    228 
    229 #if TARGET_OS_OSX
    230     CGFloat framebufferScale = view.window.screen.backingScaleFactor ?: NSScreen.mainScreen.backingScaleFactor;
    231 #else
    232     CGFloat framebufferScale = view.window.screen.scale ?: UIScreen.mainScreen.scale;
    233 #endif
    234     io.DisplayFramebufferScale = ImVec2(framebufferScale, framebufferScale);
    235 
    236     io.DeltaTime = 1 / float(view.preferredFramesPerSecond ?: 60);
    237 
    238     id<MTLCommandBuffer> commandBuffer = [self.commandQueue commandBuffer];
    239 
    240     // Our state (make them static = more or less global) as a convenience to keep the example terse.
    241     static bool show_demo_window = true;
    242     static bool show_another_window = false;
    243     static float clear_color[4] = { 0.28f, 0.36f, 0.5f, 1.0f };
    244 
    245     MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
    246     if (renderPassDescriptor != nil)
    247     {
    248         renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(clear_color[0] * clear_color[3], clear_color[1] * clear_color[3], clear_color[2] * clear_color[3], clear_color[3]);
    249 
    250         // Here, you could do additional rendering work, including other passes as necessary.
    251 
    252         id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
    253         [renderEncoder pushDebugGroup:@"ImGui demo"];
    254 
    255         // Start the Dear ImGui frame
    256         ImGui_ImplMetal_NewFrame(renderPassDescriptor);
    257 #if TARGET_OS_OSX
    258         ImGui_ImplOSX_NewFrame(view);
    259 #endif
    260         ImGui::NewFrame();
    261 
    262         // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
    263         if (show_demo_window)
    264             ImGui::ShowDemoWindow(&show_demo_window);
    265 
    266         // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
    267         {
    268             static float f = 0.0f;
    269             static int counter = 0;
    270 
    271             ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.
    272 
    273             ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
    274             ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
    275             ImGui::Checkbox("Another Window", &show_another_window);
    276 
    277             ImGui::SliderFloat("float", &f, 0.0f, 1.0f);            // Edit 1 float using a slider from 0.0f to 1.0f
    278             ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
    279 
    280             if (ImGui::Button("Button"))                            // Buttons return true when clicked (most widgets return true when edited/activated)
    281                 counter++;
    282             ImGui::SameLine();
    283             ImGui::Text("counter = %d", counter);
    284 
    285             ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
    286             ImGui::End();
    287         }
    288 
    289         // 3. Show another simple window.
    290         if (show_another_window)
    291         {
    292             ImGui::Begin("Another Window", &show_another_window);   // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
    293             ImGui::Text("Hello from another window!");
    294             if (ImGui::Button("Close Me"))
    295                 show_another_window = false;
    296             ImGui::End();
    297         }
    298 
    299         // Rendering
    300         ImGui::Render();
    301         ImDrawData* draw_data = ImGui::GetDrawData();
    302         ImGui_ImplMetal_RenderDrawData(draw_data, commandBuffer, renderEncoder);
    303 
    304         [renderEncoder popDebugGroup];
    305         [renderEncoder endEncoding];
    306 
    307         [commandBuffer presentDrawable:view.currentDrawable];
    308     }
    309 
    310     [commandBuffer commit];
    311 }
    312 
    313 - (void)mtkView:(MTKView*)view drawableSizeWillChange:(CGSize)size
    314 {
    315 }
    316 
    317 @end
    318 
    319 #pragma mark - Application Delegate
    320 
    321 #if TARGET_OS_OSX
    322 
    323 @interface AppDelegate : NSObject <NSApplicationDelegate>
    324 @property (nonatomic, strong) NSWindow *window;
    325 @end
    326 
    327 @implementation AppDelegate
    328 
    329 - (instancetype)init
    330 {
    331     if (self = [super init])
    332     {
    333         NSViewController *rootViewController = [[ViewController alloc] initWithNibName:nil bundle:nil];
    334         self.window = [[NSWindow alloc] initWithContentRect:NSZeroRect
    335                                                   styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable | NSWindowStyleMaskMiniaturizable
    336                                                     backing:NSBackingStoreBuffered
    337                                                       defer:NO];
    338         self.window.contentViewController = rootViewController;
    339         [self.window orderFront:self];
    340         [self.window center];
    341         [self.window becomeKeyWindow];
    342     }
    343     return self;
    344 }
    345 
    346 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
    347 {
    348     return YES;
    349 }
    350 
    351 @end
    352 
    353 #else
    354 
    355 @interface AppDelegate : UIResponder <UIApplicationDelegate>
    356 @property (strong, nonatomic) UIWindow *window;
    357 @end
    358 
    359 @implementation AppDelegate
    360 
    361 - (BOOL)application:(UIApplication *)application
    362     didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey,id> *)launchOptions
    363 {
    364     UIViewController *rootViewController = [[ViewController alloc] init];
    365     self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
    366     self.window.rootViewController = rootViewController;
    367     [self.window makeKeyAndVisible];
    368     return YES;
    369 }
    370 
    371 @end
    372 
    373 #endif
    374 
    375 #pragma mark - main()
    376 
    377 #if TARGET_OS_OSX
    378 
    379 int main(int argc, const char * argv[])
    380 {
    381     return NSApplicationMain(argc, argv);
    382 }
    383 
    384 #else
    385 
    386 int main(int argc, char * argv[])
    387 {
    388     @autoreleasepool
    389     {
    390         return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    391     }
    392 }
    393 
    394 #endif