Skip to content

Commit

Permalink
Merge pull request #305 from steinbergmedia/develop
Browse files Browse the repository at this point in the history
Prepare 4.13.0 Release
  • Loading branch information
scheffle committed Aug 16, 2023
2 parents ea0364b + fb7060c commit 65c353f
Show file tree
Hide file tree
Showing 121 changed files with 8,825 additions and 3,958 deletions.
54 changes: 54 additions & 0 deletions vstgui/contrib/datepicker.h
@@ -0,0 +1,54 @@
// 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

#pragma once

#include "../lib/iexternalview.h"
#include <functional>
#include <memory>

//------------------------------------------------------------------------
namespace VSTGUI {
namespace ExternalView {

//------------------------------------------------------------------------
class DatePicker : public ViewAdapter
{
public:
DatePicker ();
~DatePicker () noexcept;

struct Date
{
int32_t day {0};
int32_t month {0};
int32_t year {0};
};
void setDate (Date date);

using ChangeCallback = std::function<void (Date)>;
void setChangeCallback (const ChangeCallback& callback);

private:
bool platformViewTypeSupported (PlatformViewType type) override;
bool attach (void* parent, PlatformViewType parentViewType) override;
bool remove () override;

void setViewSize (IntRect frame, IntRect visible) override;
void setContentScaleFactor (double scaleFactor) override;

void setMouseEnabled (bool state) override;

void takeFocus () override;
void looseFocus () override;

void setTookFocusCallback (const TookFocusCallback& callback) override;

struct Impl;
std::unique_ptr<Impl> impl;
};

//------------------------------------------------------------------------
} // ExternalView
} // VSTGUI
196 changes: 196 additions & 0 deletions vstgui/contrib/datepicker.mm
@@ -0,0 +1,196 @@
// 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 "datepicker.h"
#import "externalview_nsview.h"

//------------------------------------------------------------------------
namespace VSTGUI {
namespace ExternalView {

//------------------------------------------------------------------------
struct DatePickerDelegate : RuntimeObjCClass<DatePickerDelegate>
{
using DoneCallback = std::function<void ()>;
using ValidateCallback = std::function<void (NSDate**, NSTimeInterval*)>;

static constexpr const auto DoneCallbackVarName = "DoneCallback";
static constexpr const auto ValidateCallbackVarName = "ValidateCallback";

static id allocAndInit (DoneCallback&& doneCallback, ValidateCallback&& callback)
{
id obj = Base::alloc ();
initWithCallbacks (obj, std::move (doneCallback), std::move (callback));
return obj;
}

static Class CreateClass ()
{
return ObjCClassBuilder ()
.init ("DatePickerDelegate", [NSObject class])
.addProtocol ("NSDatePickerCellDelegate")
.addMethod (@selector (datePickerCell:validateProposedDateValue:timeInterval:),
validate)
.addMethod (@selector (complete:), complete)
.addIvar<ValidateCallback> (ValidateCallbackVarName)
.addIvar<DoneCallback> (DoneCallbackVarName)
.finalize ();
}

static id initWithCallbacks (id self, DoneCallback&& doneCallback, ValidateCallback&& callback)
{
if ((self = makeInstance (self).callSuper<id (), id> (@selector (init))))
{
auto instance = makeInstance (self);
if (auto var = instance.getVariable<DoneCallback> (DoneCallbackVarName))
var->set (doneCallback);
if (auto var = instance.getVariable<ValidateCallback> (ValidateCallbackVarName))
var->set (callback);
}
return self;
}

static void complete (id self, SEL cmd, id sender)
{
if (auto var = makeInstance (self).getVariable<DoneCallback> (DoneCallbackVarName))
{
const auto& callback = var->get ();
if (callback)
callback ();
}
}

static void validate (id self, SEL cmd, NSDatePickerCell* datePickerCell,
NSDate* _Nonnull* _Nonnull proposedDateValue,
NSTimeInterval* _Nullable proposedTimeInterval)
{
if (auto var = makeInstance (self).getVariable<ValidateCallback> (ValidateCallbackVarName))
{
const auto& callback = var->get ();
if (callback)
callback (proposedDateValue, proposedTimeInterval);
}
}
};

//------------------------------------------------------------------------
struct DatePicker::Impl : ExternalNSViewBase<NSDatePicker>
{
using Base::Base;

id delegate {nil};
ChangeCallback changeCallback;

#if !__has_feature(objc_arc)
~Impl () noexcept
{
if (delegate)
[delegate release];
}
#endif
};

//------------------------------------------------------------------------
DatePicker::DatePicker ()
{
impl = std::make_unique<Impl> ([[NSDatePicker alloc] initWithFrame: {0., 0., 10., 10.}]);
impl->view.datePickerStyle = NSDatePickerStyleTextField;
impl->view.datePickerMode = NSDatePickerModeSingle;
impl->view.datePickerElements = NSDatePickerElementFlagYearMonthDay;
if (@available (macOS 10.15.4, *))
impl->view.presentsCalendarOverlay = YES;
impl->view.dateValue = [NSDate date];
impl->view.calendar = [NSCalendar currentCalendar];
[impl->container addSubview:impl->view];

impl->delegate = DatePickerDelegate::allocAndInit (
[impl = impl.get ()] () {
if (impl->changeCallback)
{
auto dateValue = impl->view.dateValue;
auto calendar = impl->view.calendar;
auto components = [calendar
components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay
fromDate:dateValue];
Date date;
date.day = static_cast<int32_t> (components.day);
date.month = static_cast<int32_t> (components.month);
date.year = static_cast<int32_t> (components.year);
impl->changeCallback (date);
}
},
[] (NSDate** date, NSTimeInterval* time) {
// TODO: add validation mechanism
});
impl->view.delegate = impl->delegate;
impl->view.target = impl->delegate;
impl->view.action = @selector (complete:);
}

//------------------------------------------------------------------------
DatePicker::~DatePicker () noexcept {}

//------------------------------------------------------------------------
void DatePicker::setDate (Date date)
{
auto calendar = impl->view.calendar;
auto dateComponents = [NSDateComponents new];
dateComponents.calendar = calendar;
dateComponents.day = date.day;
dateComponents.month = date.month;
dateComponents.year = date.year;
impl->view.dateValue = [calendar dateFromComponents:dateComponents];
}

//------------------------------------------------------------------------
void DatePicker::setChangeCallback (const ChangeCallback& callback)
{
impl->changeCallback = callback;
}

//------------------------------------------------------------------------
bool DatePicker::platformViewTypeSupported (PlatformViewType type)
{
return impl->platformViewTypeSupported (type);
}

//------------------------------------------------------------------------
bool DatePicker::attach (void* parent, PlatformViewType parentViewType)
{
return impl->attach (parent, parentViewType);
}

//------------------------------------------------------------------------
bool DatePicker::remove () { return impl->remove (); }

//------------------------------------------------------------------------
void DatePicker::setViewSize (IntRect frame, IntRect visible)
{
impl->setViewSize (frame, visible);
}

//------------------------------------------------------------------------
void DatePicker::setContentScaleFactor (double scaleFactor)
{
impl->setContentScaleFactor (scaleFactor);
}

//------------------------------------------------------------------------
void DatePicker::setMouseEnabled (bool state) { impl->setMouseEnabled (state); }

//------------------------------------------------------------------------
void DatePicker::takeFocus () { impl->takeFocus (); }

//------------------------------------------------------------------------
void DatePicker::looseFocus () { impl->looseFocus (); }

//------------------------------------------------------------------------
void DatePicker::setTookFocusCallback (const TookFocusCallback& callback)
{
impl->setTookFocusCallback (callback);
}

//------------------------------------------------------------------------
} // ExternalView
} // VSTGUI
132 changes: 132 additions & 0 deletions vstgui/contrib/datepicker_win32.cpp
@@ -0,0 +1,132 @@

#include "datepicker.h"
#include "externalview_hwnd.h"
#include "vstgui/lib/platform/win32/win32factory.h"

#include <CommCtrl.h>

//------------------------------------------------------------------------
namespace VSTGUI {
namespace ExternalView {

//------------------------------------------------------------------------
struct DatePicker::Impl : ExternalHWNDBase
{
using Base::Base;

~Impl () noexcept
{
if (font)
DeleteObject (font);
}

ChangeCallback changeCallback;
HFONT font {nullptr};
};

//------------------------------------------------------------------------
DatePicker::DatePicker ()
{
auto hInstance = getPlatformFactory ().asWin32Factory ()->getInstance ();
impl = std::make_unique<Impl> (hInstance);
impl->child = CreateWindowExW (0, DATETIMEPICK_CLASS, TEXT ("DateTime"),
WS_BORDER | WS_CHILD | WS_VISIBLE | DTS_SHORTDATEFORMAT, 0, 0,
80, 20, impl->container.getHWND (), NULL, hInstance, NULL);
impl->container.setWindowProc ([this] (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message)
{
case WM_NOTIFY:
{
LPNMHDR hdr = reinterpret_cast<LPNMHDR> (lParam);
switch (hdr->code)
{
case DTN_DATETIMECHANGE:
{
LPNMDATETIMECHANGE lpChange = reinterpret_cast<LPNMDATETIMECHANGE> (lParam);
if (impl->changeCallback)
{
Date date;
date.day = lpChange->st.wDay;
date.month = lpChange->st.wMonth;
date.year = lpChange->st.wYear;
impl->changeCallback (date);
}
break;
}
}
break;
}
}
return DefWindowProc (hwnd, message, wParam, lParam);
});
}

//------------------------------------------------------------------------
DatePicker::~DatePicker () noexcept {}

//------------------------------------------------------------------------
void DatePicker::setDate (Date date)
{
SYSTEMTIME st = {};
st.wDay = date.day;
st.wMonth = date.month;
st.wYear = date.year;
DateTime_SetSystemtime (impl->child, GDT_VALID, &st);
}

//------------------------------------------------------------------------
void DatePicker::setChangeCallback (const ChangeCallback& callback)
{
impl->changeCallback = callback;
}

//------------------------------------------------------------------------
bool DatePicker::platformViewTypeSupported (PlatformViewType type)
{
return impl->platformViewTypeSupported (type);
}

//------------------------------------------------------------------------
bool DatePicker::attach (void* parent, PlatformViewType parentViewType)
{
return impl->attach (parent, parentViewType);
}

//------------------------------------------------------------------------
bool DatePicker::remove () { return impl->remove (); }

//------------------------------------------------------------------------
void DatePicker::setViewSize (IntRect frame, IntRect visible)
{
impl->setViewSize (frame, visible);
}

//------------------------------------------------------------------------
void DatePicker::setContentScaleFactor (double scaleFactor)
{
if (impl->font)
DeleteObject (impl->font);
auto logFont = NonClientMetrics::get ().lfCaptionFont;
logFont.lfHeight = static_cast<LONG> (std::round (logFont.lfHeight * scaleFactor));
impl->font = CreateFontIndirect (&logFont);
if (impl->font)
SendMessage (impl->child, WM_SETFONT, (WPARAM)impl->font, 0);
}

//------------------------------------------------------------------------
void DatePicker::setMouseEnabled (bool state) { impl->setMouseEnabled (state); }

//------------------------------------------------------------------------
void DatePicker::takeFocus () { impl->takeFocus (); }

//------------------------------------------------------------------------
void DatePicker::looseFocus () { impl->looseFocus (); }

void DatePicker::setTookFocusCallback (const TookFocusCallback& callback)
{
impl->setTookFocusCallback (callback);
}

//------------------------------------------------------------------------
} // ExternalView
} // VSTGUI

0 comments on commit 65c353f

Please sign in to comment.