+#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];
}