// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #import "evbutton.h" #import "externalview_nsview.h" #import "../lib/platform/mac/macstring.h" //------------------------------------------------------------------------ namespace VSTGUI { namespace ExternalView { //------------------------------------------------------------------------ struct ButtonDelegate : RuntimeObjCClass { using ActionCallback = std::function; static constexpr const auto ActionCallbackVarName = "ActionCallback"; static id allocAndInit (ActionCallback&& actionCallback) { id obj = Base::alloc (); initWithCallbacks (obj, std::move (actionCallback)); return obj; } static Class CreateClass () { return ObjCClassBuilder () .init ("ButtonDelegate", [NSObject class]) .addMethod (@selector (onAction:), onAction) .addIvar (ActionCallbackVarName) .finalize (); } static id initWithCallbacks (id self, ActionCallback&& actionCallback) { if ((self = makeInstance (self).callSuper (@selector (init)))) { auto instance = makeInstance (self); if (auto var = instance.getVariable (ActionCallbackVarName)) var->set (actionCallback); } return self; } static void onAction (id self, SEL cmd, id sender) { if (auto var = makeInstance (self).getVariable (ActionCallbackVarName)) { const auto& callback = var->get (); if (callback) callback (); } } }; //------------------------------------------------------------------------ struct Button::Impl : ExternalNSViewBase, IControlViewExtension { using Base::Base; id delegate {nil}; EditCallbacks callbacks {}; #if !__has_feature(objc_arc) ~Impl () noexcept { if (delegate) [delegate release]; } #endif bool setValue (double value) override { if (value < 0.5) view.state = NSControlStateValueOff; else if (value == 0.5) view.state = NSControlStateValueMixed; else view.state = NSControlStateValueOn; return true; } bool setEditCallbacks (const EditCallbacks& editCallbacks) override { callbacks = editCallbacks; return true; } }; //------------------------------------------------------------------------ Button::Button (Type type, const UTF8String& inTitle) { NSString* title = fromUTF8String (inTitle); ButtonDelegate::ActionCallback actionCallback = [this] () { double value = 0.; switch (impl->view.state) { case NSControlStateValueOn: value = 1.; break; case NSControlStateValueOff: value = 0.; break; case NSControlStateValueMixed: value = 0.5; break; } if (impl->callbacks.beginEdit) impl->callbacks.beginEdit (); if (impl->callbacks.performEdit) impl->callbacks.performEdit (value); if (impl->callbacks.endEdit) impl->callbacks.endEdit (); }; NSButton* button = {}; switch (type) { case Type::Checkbox: { button = [NSButton checkboxWithTitle:title target:nullptr action:nullptr]; break; } case Type::Push: { button = [NSButton buttonWithTitle:title target:nullptr action:nullptr]; [button setButtonType:NSButtonTypeMomentaryLight]; actionCallback = [this] () { if (impl->callbacks.beginEdit) impl->callbacks.beginEdit (); if (impl->callbacks.performEdit) impl->callbacks.performEdit (1.); if (impl->callbacks.endEdit) impl->callbacks.endEdit (); if (impl->callbacks.beginEdit) impl->callbacks.beginEdit (); if (impl->callbacks.performEdit) impl->callbacks.performEdit (0.); if (impl->callbacks.endEdit) impl->callbacks.endEdit (); }; break; } case Type::OnOff: { button = [NSButton buttonWithTitle:title target:nullptr action:nullptr]; [button setButtonType:NSButtonTypePushOnPushOff]; break; } case Type::Radio: { button = [NSButton radioButtonWithTitle:title target:nullptr action:nullptr]; break; } } [button sizeToFit]; impl = std::make_unique (button); impl->delegate = ButtonDelegate::allocAndInit (std::move (actionCallback)); impl->view.target = impl->delegate; impl->view.action = @selector (onAction:); [impl->container addSubview:impl->view]; [button retain]; } //------------------------------------------------------------------------ Button::~Button () noexcept = default; //------------------------------------------------------------------------ bool Button::platformViewTypeSupported (PlatformViewType type) { return impl->platformViewTypeSupported (type); } //------------------------------------------------------------------------ bool Button::attach (void* parent, PlatformViewType parentViewType) { return impl->attach (parent, parentViewType); } //------------------------------------------------------------------------ bool Button::remove () { return impl->remove (); } //------------------------------------------------------------------------ void Button::setViewSize (IntRect frame, IntRect visible) { static constexpr const NSControlSize controlSizes[] = {NSControlSizeRegular, NSControlSizeSmall, NSControlSizeMini}; for (auto i = 0; i < std::size (controlSizes); i++) { impl->view.controlSize = controlSizes[i]; auto size = [impl->view sizeThatFits:NSMakeSize (frame.size.width, frame.size.height)]; if (size.height <= frame.size.height) break; } impl->setViewSize (frame, visible); } //------------------------------------------------------------------------ void Button::setContentScaleFactor (double scaleFactor) { impl->setContentScaleFactor (scaleFactor); } //------------------------------------------------------------------------ void Button::setMouseEnabled (bool state) { impl->setMouseEnabled (state); } //------------------------------------------------------------------------ void Button::takeFocus () { impl->takeFocus (); } //------------------------------------------------------------------------ void Button::looseFocus () { impl->looseFocus (); } //------------------------------------------------------------------------ void Button::setTookFocusCallback (const TookFocusCallback& callback) { impl->setTookFocusCallback (callback); } //------------------------------------------------------------------------ bool Button::setValue (double value) { return impl->setValue (value); } //------------------------------------------------------------------------ bool Button::setEditCallbacks (const EditCallbacks& callbacks) { return impl->setEditCallbacks (callbacks); } //------------------------------------------------------------------------ } // ExternalView } // VSTGUI