Rework UI architecture and blendshape animation
[opengl.git] / ui.mm
diff --git a/ui.mm b/ui.mm
index c2a0c6f50ace0e0b495d7b0a1c6c8a6c5315089c..26be31d5c05eb41fee3c19e34c000e3d0f532445 100644 (file)
--- a/ui.mm
+++ b/ui.mm
+#import "ui.hpp"
 #import <Cocoa/Cocoa.h>
-#import "ui.h"
 
 @interface Controller : NSObject
-- (instancetype)initWithCallback:(void(*)(int, float))callback;
+- (instancetype)initWithBlendshapeNames:(NSArray<NSString *> *)names
+                                                          delegate:(ControlWindowDelegate *)delegate;
 - (void)sliderMoved:(id)sender;
-
+@property(readonly) NSView *view;
 @end
 
 @implementation Controller {
-       void (*weightChanged)(int, float);
+       size_t numBlendshapes;
+       ControlWindowDelegate *delegate;
+       NSMutableArray<NSControl *> *sliders;
 }
 
-- (instancetype)initWithCallback:(void(*)(int, float))callback {
+- (instancetype)initWithBlendshapeNames:(NSArray<NSString *> *)names
+                                                          delegate:(ControlWindowDelegate *)d {
        if (self = [super init]) {
-               self->weightChanged = callback;
+               self->numBlendshapes = [names count];
+               self->delegate = d;
        }
+
+       sliders = [NSMutableArray arrayWithCapacity:numBlendshapes];
+       NSStackView *stackView = [NSStackView stackViewWithViews:sliders];
+
+       for (int i = 0; i < numBlendshapes; i++) {
+               NSSlider *slider = [NSSlider sliderWithTarget:self
+                                                                                          action:@selector(sliderMoved:)];
+               [slider setTag:i];
+               sliders[i] = slider;
+
+               NSTextField *label = [NSTextField labelWithString:names[i]];
+               NSStackView *rowStackView =
+                       [NSStackView stackViewWithViews:@[ label, slider ]];
+               [stackView addView:rowStackView inGravity:NSStackViewGravityTop];
+       }
+
+       NSButton *solveButton = [NSButton buttonWithTitle:@"Solve weights"
+                                                                                          target:self
+                                                                                          action:@selector(solveWeights:)];
+       NSButton *resetButton = [NSButton buttonWithTitle:@"Reset weights"
+                                                                                          target:self
+                                                                                          action:@selector(resetWeights:)];
+       NSStackView *buttonStack =
+               [NSStackView stackViewWithViews:@[ solveButton, resetButton ]];
+       [stackView addView:buttonStack inGravity:NSStackViewGravityTop];
+
+       NSButton *manipsButton =
+               [NSButton buttonWithTitle:@"Reset Manipulators"
+                                                  target:self
+                                                  action:@selector(resetManipulators:)];
+       [stackView addView:manipsButton inGravity:NSStackViewGravityTop];
+
+       NSButton *animCheckbox =
+               [NSButton checkboxWithTitle:@"Play animation"
+                                                        target:self
+                                                        action:@selector(animationPlayback:)];
+       [stackView addView:animCheckbox inGravity:NSStackViewGravityTop];
+
+       [stackView setOrientation:NSUserInterfaceLayoutOrientationVertical];
+       [stackView setEdgeInsets:NSEdgeInsetsMake(16, 16, 16, 16)];
+       _view = stackView;
+
        return self;
 }
 
 - (void)sliderMoved:(id)sender {
        NSSlider *slider = (NSSlider *)sender;
-       weightChanged([slider tag], [slider floatValue]);
+       delegate->weightChanged([slider tag], [slider floatValue]);
+}
+
+- (void)solveWeights:(id)sender {
+       std::vector<float> newWeights(numBlendshapes);
+       delegate->solveWeights(newWeights);
+       for (int i = 0; i < numBlendshapes; i++)
+               [sliders[i] setFloatValue:newWeights[i]];
 }
+
+- (void)resetWeights:(id)sender {
+       for (int i = 0; i < numBlendshapes; i++) {
+               [sliders[i] setFloatValue:0];
+               delegate->weightChanged(i, 0);
+       }
+}
+
+- (void)resetManipulators:(id)sender {
+       delegate->resetManipulators();
+}
+
+- (void)animationPlayback:(id)sender {
+       bool play = [(NSButton *)sender state] == NSControlStateValueOn;
+       for (int i = 0; i < [sliders count]; i++)
+               [sliders[i] setEnabled: !play];
+       delegate->playbackChanged(play);
+}
+
+- (void)updateWeights:(NSArray<NSNumber *> *)newWeights {
+       for (int i = 0; i < [newWeights count]; i++)
+               [sliders[i] setFloatValue:[newWeights[i] floatValue]];
+}
+
 @end
 
-void createControlWindow(size_t numBlendshapes, const char **names, void(*weightChanged)(int, float)) {
-       Controller *controller = [[Controller alloc] initWithCallback: weightChanged];
+ControlWindow createControlWindow(std::vector<std::string> names,
+                                                                 ControlWindowDelegate *delegate) {
+       NSMutableArray<NSString *> *nsNames =
+               [NSMutableArray arrayWithCapacity:names.size()];
+       for (auto &n : names)
+               [nsNames addObject:[NSString stringWithUTF8String:n.c_str()]];
+       Controller *controller =
+               [[Controller alloc] initWithBlendshapeNames:(NSArray *)nsNames
+                                                                                  delegate:delegate];
 
-       NSWindowStyleMask mask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable | NSWindowStyleMaskUtilityWindow;
-       NSWindow *window = [[NSPanel alloc] initWithContentRect: NSMakeRect(20, 20, 340, 420)
-                                               styleMask:mask backing:NSBackingStoreBuffered defer:NO];
+       NSWindowStyleMask mask = NSWindowStyleMaskTitled |
+                                                        NSWindowStyleMaskClosable |
+                                                        NSWindowStyleMaskUtilityWindow;
+       NSWindow *window =
+               [[NSPanel alloc] initWithContentRect:NSMakeRect(20, 20, 340, 420)
+                                                                  styleMask:mask
+                                                                        backing:NSBackingStoreBuffered
+                                                                          defer:NO];
        [window makeKeyAndOrderFront:nil];
        [window setTitle:@"Blendshapes"];
+       [window setContentView:[controller view]];
 
-       NSMutableArray<NSView *> *sliders = [NSMutableArray arrayWithCapacity: numBlendshapes];
+       return {window, controller};
+}
 
-       for (int i = 0; i < numBlendshapes; i++) {
-               NSSlider *slider = [NSSlider sliderWithTarget: controller action:@selector(sliderMoved:)];
-               [slider setTag: i];
-               NSTextField *label = [NSTextField labelWithString: [NSString stringWithUTF8String: names[i]]];
-               NSStackView *stackView = [NSStackView stackViewWithViews: @[label, slider]];
-               sliders[i] = stackView;
+bool isPanelFocused(ControlWindow cw) {
+       NSWindow *win = (NSWindow *)cw.window;
+       return [win isKeyWindow];
 }
 
-       NSStackView *stackView = [NSStackView stackViewWithViews: sliders];
-       [stackView setOrientation: NSUserInterfaceLayoutOrientationVertical];
-       [stackView setEdgeInsets: NSEdgeInsetsMake(16, 16, 16, 16)];
-       [window setContentView: stackView];
+void updateWeights(ControlWindow *cw, std::vector<float> weights) {
+       Controller *c = (Controller *)cw->controller;
+       NSMutableArray<NSNumber *> *nsWeights =
+               [NSMutableArray arrayWithCapacity:weights.size()];
+       for (int i = 0; i < weights.size(); i++)
+               nsWeights[i] = [NSNumber numberWithFloat:weights[i]];
+       [c updateWeights:(NSArray *)nsWeights];
 }