#include "noesis_shim.h"
#include <NsCore/Noesis.h>
#include <NsCore/Ptr.h>
#include <NsCore/Delegate.h>
#include <NsCore/DynamicCast.h>
#include <NsGui/BaseButton.h>
#include <NsGui/DataObject.h>
#include <NsGui/DependencyObject.h>
#include <NsGui/DragDrop.h>
#include <NsGui/Enums.h>
#include <NsGui/Events.h>
#include <NsGui/FrameworkElement.h>
#include <NsGui/Path.h>
#include <NsGui/RoutedEvent.h>
#include <NsGui/Selector.h>
#include <NsGui/StreamGeometry.h>
#include <NsGui/StreamGeometryContext.h>
#include <NsGui/TextBlock.h>
#include <NsGui/TextBox.h>
#include <NsGui/UIElement.h>
#include <NsGui/UIElementEvents.h>
#include <NsCore/Symbol.h>
#include <NsDrawing/Point.h>
#include <NsDrawing/Size.h>
#include <NsDrawing/Thickness.h>
#include <string.h>
namespace {
class RustClickHandler {
public:
RustClickHandler(noesis_click_fn cb, void* userdata, Noesis::BaseButton* button)
: mCb(cb), mUserdata(userdata), mButton(button)
{
if (mButton) {
mButton->AddReference();
}
}
~RustClickHandler() {
if (mButton) {
mButton->Release();
}
}
RustClickHandler(const RustClickHandler&) = delete;
RustClickHandler& operator=(const RustClickHandler&) = delete;
void OnClick(Noesis::BaseComponent* , const Noesis::RoutedEventArgs& ) {
if (mCb) {
mCb(mUserdata);
}
}
Noesis::BaseButton* button() const { return mButton; }
private:
noesis_click_fn mCb;
void* mUserdata;
Noesis::BaseButton* mButton; };
}
extern "C" void* noesis_framework_element_find_name(void* element, const char* name) {
if (!element || !name) return nullptr;
auto* fe = static_cast<Noesis::FrameworkElement*>(element);
Noesis::BaseComponent* found = fe->FindName(name);
if (!found) return nullptr;
auto* result = Noesis::DynamicCast<Noesis::FrameworkElement*>(found);
if (!result) return nullptr;
result->AddReference();
return result;
}
extern "C" bool noesis_framework_element_register_name(
void* element, const char* name, void* object) {
if (!element || !name) return false;
auto* fe = Noesis::DynamicCast<Noesis::FrameworkElement*>(
static_cast<Noesis::BaseComponent*>(element));
if (!fe) return false;
fe->RegisterName(name, static_cast<Noesis::BaseComponent*>(object));
return true;
}
extern "C" bool noesis_framework_element_unregister_name(void* element, const char* name) {
if (!element || !name) return false;
auto* fe = Noesis::DynamicCast<Noesis::FrameworkElement*>(
static_cast<Noesis::BaseComponent*>(element));
if (!fe) return false;
fe->UnregisterName(name);
return true;
}
extern "C" const char* noesis_framework_element_get_name(void* element) {
if (!element) return nullptr;
return static_cast<Noesis::FrameworkElement*>(element)->GetName();
}
extern "C" void noesis_framework_element_set_visibility(void* element, bool visible) {
if (!element) return;
auto* fe = static_cast<Noesis::FrameworkElement*>(element);
fe->SetVisibility(visible ? Noesis::Visibility_Visible : Noesis::Visibility_Collapsed);
}
extern "C" void noesis_framework_element_set_margin(
void* element, float left, float top, float right, float bottom)
{
if (!element) return;
auto* fe = static_cast<Noesis::FrameworkElement*>(element);
fe->SetMargin(Noesis::Thickness(left, top, right, bottom));
}
extern "C" void* noesis_subscribe_click(
void* element, noesis_click_fn cb, void* userdata)
{
if (!element || !cb) return nullptr;
auto* fe = static_cast<Noesis::FrameworkElement*>(element);
auto* button = Noesis::DynamicCast<Noesis::BaseButton*>(fe);
if (!button) return nullptr;
auto* handler = new RustClickHandler(cb, userdata, button);
button->Click() += Noesis::MakeDelegate(handler, &RustClickHandler::OnClick);
return handler;
}
extern "C" void noesis_unsubscribe_click(void* token) {
if (!token) return;
auto* handler = static_cast<RustClickHandler*>(token);
if (auto* button = handler->button()) {
button->Click() -= Noesis::MakeDelegate(handler, &RustClickHandler::OnClick);
}
delete handler;
}
namespace {
class RustKeyDownHandler {
public:
RustKeyDownHandler(noesis_keydown_fn cb, void* userdata, Noesis::UIElement* element)
: mCb(cb), mUserdata(userdata), mElement(element)
{
if (mElement) {
mElement->AddReference();
}
}
~RustKeyDownHandler() {
if (mElement) {
mElement->Release();
}
}
RustKeyDownHandler(const RustKeyDownHandler&) = delete;
RustKeyDownHandler& operator=(const RustKeyDownHandler&) = delete;
void OnKeyDown(Noesis::BaseComponent* , const Noesis::KeyEventArgs& args) {
if (!mCb) return;
bool handled = false;
mCb(mUserdata, static_cast<int32_t>(args.key), &handled);
if (handled) {
args.handled = true;
}
}
Noesis::UIElement* element() const { return mElement; }
private:
noesis_keydown_fn mCb;
void* mUserdata;
Noesis::UIElement* mElement; };
}
extern "C" void* noesis_subscribe_keydown(
void* element, noesis_keydown_fn cb, void* userdata)
{
if (!element || !cb) return nullptr;
auto* fe = static_cast<Noesis::FrameworkElement*>(element);
auto* uie = Noesis::DynamicCast<Noesis::UIElement*>(fe);
if (!uie) return nullptr;
auto* handler = new RustKeyDownHandler(cb, userdata, uie);
uie->KeyDown() += Noesis::MakeDelegate(handler, &RustKeyDownHandler::OnKeyDown);
return handler;
}
extern "C" void noesis_unsubscribe_keydown(void* token) {
if (!token) return;
auto* handler = static_cast<RustKeyDownHandler*>(token);
if (auto* uie = handler->element()) {
uie->KeyDown() -= Noesis::MakeDelegate(handler, &RustKeyDownHandler::OnKeyDown);
}
delete handler;
}
namespace {
class RustSelectionChangedHandler {
public:
RustSelectionChangedHandler(noesis_selection_changed_fn cb, void* userdata,
Noesis::Selector* selector)
: mCb(cb), mUserdata(userdata), mSelector(selector)
{
if (mSelector) {
mSelector->AddReference();
}
}
~RustSelectionChangedHandler() {
if (mSelector) {
mSelector->Release();
}
}
RustSelectionChangedHandler(const RustSelectionChangedHandler&) = delete;
RustSelectionChangedHandler& operator=(const RustSelectionChangedHandler&) = delete;
void OnSelectionChanged(Noesis::BaseComponent* ,
const Noesis::SelectionChangedEventArgs& ) {
if (mCb) {
mCb(mUserdata);
}
}
Noesis::Selector* selector() const { return mSelector; }
private:
noesis_selection_changed_fn mCb;
void* mUserdata;
Noesis::Selector* mSelector; };
}
extern "C" void* noesis_subscribe_selection_changed(
void* element, noesis_selection_changed_fn cb, void* userdata)
{
if (!element || !cb) return nullptr;
auto* selector = Noesis::DynamicCast<Noesis::Selector*>(
static_cast<Noesis::BaseComponent*>(element));
if (!selector) return nullptr;
auto* handler = new RustSelectionChangedHandler(cb, userdata, selector);
selector->SelectionChanged() +=
Noesis::MakeDelegate(handler, &RustSelectionChangedHandler::OnSelectionChanged);
return handler;
}
extern "C" void noesis_unsubscribe_selection_changed(void* token) {
if (!token) return;
auto* handler = static_cast<RustSelectionChangedHandler*>(token);
if (auto* selector = handler->selector()) {
selector->SelectionChanged() -=
Noesis::MakeDelegate(handler, &RustSelectionChangedHandler::OnSelectionChanged);
}
delete handler;
}
extern "C" const char* noesis_text_get(void* element) {
if (!element) return nullptr;
auto* fe = static_cast<Noesis::FrameworkElement*>(element);
if (auto* tb = Noesis::DynamicCast<Noesis::TextBox*>(fe)) {
return tb->GetText();
}
if (auto* tbk = Noesis::DynamicCast<Noesis::TextBlock*>(fe)) {
return tbk->GetText();
}
return nullptr;
}
extern "C" bool noesis_text_set(void* element, const char* text) {
if (!element) return false;
const char* safe = text ? text : "";
auto* fe = static_cast<Noesis::FrameworkElement*>(element);
if (auto* tb = Noesis::DynamicCast<Noesis::TextBox*>(fe)) {
tb->SetText(safe);
return true;
}
if (auto* tbk = Noesis::DynamicCast<Noesis::TextBlock*>(fe)) {
tbk->SetText(safe);
return true;
}
return false;
}
extern "C" bool noesis_text_caret_to_end(void* element) {
if (!element) return false;
auto* fe = static_cast<Noesis::FrameworkElement*>(element);
auto* tb = Noesis::DynamicCast<Noesis::TextBox*>(fe);
if (!tb) return false;
const char* current = tb->GetText();
const int32_t len = current ? static_cast<int32_t>(strlen(current)) : 0;
tb->SetCaretIndex(len);
return true;
}
extern "C" bool noesis_focus_element(void* element) {
if (!element) return false;
auto* fe = static_cast<Noesis::FrameworkElement*>(element);
auto* uie = Noesis::DynamicCast<Noesis::UIElement*>(fe);
if (!uie) return false;
return uie->Focus();
}
extern "C" bool noesis_path_set_points(void* element, const float* xy, uint32_t count) {
if (!element || !xy || count < 2) return false;
auto* fe = static_cast<Noesis::FrameworkElement*>(element);
auto* path = Noesis::DynamicCast<Noesis::Path*>(fe);
if (!path) return false;
Noesis::Ptr<Noesis::StreamGeometry> geometry = Noesis::MakePtr<Noesis::StreamGeometry>();
{
Noesis::StreamGeometryContext ctx = geometry->Open();
ctx.BeginFigure(Noesis::Point(xy[0], xy[1]), false );
for (uint32_t i = 1; i < count; ++i) {
ctx.LineTo(Noesis::Point(xy[2 * i], xy[2 * i + 1]));
}
ctx.Close();
}
path->SetData(geometry);
return true;
}
namespace {
enum DmArgKind : int32_t {
ARG_ROUTED = 0, ARG_MOUSE = 1, ARG_MOUSE_BUTTON = 2, ARG_MOUSE_WHEEL = 3, ARG_KEY = 4, ARG_SIZE_CHANGED = 5, ARG_TEXT_INPUT = 6, ARG_FOCUS_CHANGED = 7, ARG_DRAG = 8, ARG_MANIP_STARTED = 9, ARG_MANIP_DELTA = 10, ARG_MANIP_COMPLETED = 11, ARG_MANIP_INERTIA = 12, };
struct DmEventArgs {
int32_t kind;
const Noesis::RoutedEventArgs* args;
};
struct EventEntry {
const char* name;
const Noesis::RoutedEvent* const* slot;
int32_t kind;
};
const EventEntry kEvents[] = {
{"MouseEnter", &Noesis::UIElement::MouseEnterEvent, ARG_MOUSE},
{"MouseLeave", &Noesis::UIElement::MouseLeaveEvent, ARG_MOUSE},
{"MouseMove", &Noesis::UIElement::MouseMoveEvent, ARG_MOUSE},
{"PreviewMouseMove", &Noesis::UIElement::PreviewMouseMoveEvent, ARG_MOUSE},
{"GotMouseCapture", &Noesis::UIElement::GotMouseCaptureEvent, ARG_MOUSE},
{"LostMouseCapture", &Noesis::UIElement::LostMouseCaptureEvent, ARG_MOUSE},
{"MouseDown", &Noesis::UIElement::MouseDownEvent, ARG_MOUSE_BUTTON},
{"MouseUp", &Noesis::UIElement::MouseUpEvent, ARG_MOUSE_BUTTON},
{"MouseLeftButtonDown", &Noesis::UIElement::MouseLeftButtonDownEvent, ARG_MOUSE_BUTTON},
{"MouseLeftButtonUp", &Noesis::UIElement::MouseLeftButtonUpEvent, ARG_MOUSE_BUTTON},
{"MouseRightButtonDown", &Noesis::UIElement::MouseRightButtonDownEvent, ARG_MOUSE_BUTTON},
{"MouseRightButtonUp", &Noesis::UIElement::MouseRightButtonUpEvent, ARG_MOUSE_BUTTON},
{"PreviewMouseDown", &Noesis::UIElement::PreviewMouseDownEvent, ARG_MOUSE_BUTTON},
{"PreviewMouseUp", &Noesis::UIElement::PreviewMouseUpEvent, ARG_MOUSE_BUTTON},
{"PreviewMouseLeftButtonDown", &Noesis::UIElement::PreviewMouseLeftButtonDownEvent, ARG_MOUSE_BUTTON},
{"PreviewMouseLeftButtonUp", &Noesis::UIElement::PreviewMouseLeftButtonUpEvent, ARG_MOUSE_BUTTON},
{"PreviewMouseRightButtonDown",&Noesis::UIElement::PreviewMouseRightButtonDownEvent,ARG_MOUSE_BUTTON},
{"PreviewMouseRightButtonUp", &Noesis::UIElement::PreviewMouseRightButtonUpEvent, ARG_MOUSE_BUTTON},
{"MouseWheel", &Noesis::UIElement::MouseWheelEvent, ARG_MOUSE_WHEEL},
{"PreviewMouseWheel", &Noesis::UIElement::PreviewMouseWheelEvent, ARG_MOUSE_WHEEL},
{"KeyDown", &Noesis::UIElement::KeyDownEvent, ARG_KEY},
{"KeyUp", &Noesis::UIElement::KeyUpEvent, ARG_KEY},
{"PreviewKeyDown", &Noesis::UIElement::PreviewKeyDownEvent, ARG_KEY},
{"PreviewKeyUp", &Noesis::UIElement::PreviewKeyUpEvent, ARG_KEY},
{"TextInput", &Noesis::UIElement::TextInputEvent, ARG_TEXT_INPUT},
{"PreviewTextInput", &Noesis::UIElement::PreviewTextInputEvent, ARG_TEXT_INPUT},
{"GotFocus", &Noesis::UIElement::GotFocusEvent, ARG_ROUTED},
{"LostFocus", &Noesis::UIElement::LostFocusEvent, ARG_ROUTED},
{"GotKeyboardFocus", &Noesis::UIElement::GotKeyboardFocusEvent, ARG_FOCUS_CHANGED},
{"LostKeyboardFocus", &Noesis::UIElement::LostKeyboardFocusEvent, ARG_FOCUS_CHANGED},
{"PreviewGotKeyboardFocus", &Noesis::UIElement::PreviewGotKeyboardFocusEvent, ARG_FOCUS_CHANGED},
{"PreviewLostKeyboardFocus", &Noesis::UIElement::PreviewLostKeyboardFocusEvent, ARG_FOCUS_CHANGED},
{"Loaded", &Noesis::FrameworkElement::LoadedEvent, ARG_ROUTED},
{"Unloaded", &Noesis::FrameworkElement::UnloadedEvent, ARG_ROUTED},
{"SizeChanged", &Noesis::FrameworkElement::SizeChangedEvent, ARG_SIZE_CHANGED},
{"TouchDown", &Noesis::UIElement::TouchDownEvent, ARG_ROUTED},
{"TouchMove", &Noesis::UIElement::TouchMoveEvent, ARG_ROUTED},
{"TouchUp", &Noesis::UIElement::TouchUpEvent, ARG_ROUTED},
{"TouchEnter", &Noesis::UIElement::TouchEnterEvent, ARG_ROUTED},
{"TouchLeave", &Noesis::UIElement::TouchLeaveEvent, ARG_ROUTED},
{"Tapped", &Noesis::UIElement::TappedEvent, ARG_ROUTED},
{"DoubleTapped", &Noesis::UIElement::DoubleTappedEvent, ARG_ROUTED},
{"Holding", &Noesis::UIElement::HoldingEvent, ARG_ROUTED},
{"RightTapped", &Noesis::UIElement::RightTappedEvent, ARG_ROUTED},
{"ManipulationStarting", &Noesis::UIElement::ManipulationStartingEvent, ARG_ROUTED},
{"ManipulationStarted", &Noesis::UIElement::ManipulationStartedEvent, ARG_MANIP_STARTED},
{"ManipulationDelta", &Noesis::UIElement::ManipulationDeltaEvent, ARG_MANIP_DELTA},
{"ManipulationInertiaStarting", &Noesis::UIElement::ManipulationInertiaStartingEvent, ARG_MANIP_INERTIA},
{"ManipulationCompleted", &Noesis::UIElement::ManipulationCompletedEvent, ARG_MANIP_COMPLETED},
{"DragEnter", &Noesis::UIElement::DragEnterEvent, ARG_DRAG},
{"DragOver", &Noesis::UIElement::DragOverEvent, ARG_DRAG},
{"DragLeave", &Noesis::UIElement::DragLeaveEvent, ARG_DRAG},
{"Drop", &Noesis::UIElement::DropEvent, ARG_DRAG},
{"PreviewDragEnter", &Noesis::UIElement::PreviewDragEnterEvent, ARG_DRAG},
{"PreviewDragOver", &Noesis::UIElement::PreviewDragOverEvent, ARG_DRAG},
{"PreviewDragLeave", &Noesis::UIElement::PreviewDragLeaveEvent, ARG_DRAG},
{"PreviewDrop", &Noesis::UIElement::PreviewDropEvent, ARG_DRAG},
};
const Noesis::RoutedEvent* LookupEvent(
Noesis::UIElement* element, const char* name, int32_t& outKind)
{
for (const EventEntry& e : kEvents) {
if (strcmp(e.name, name) == 0) {
outKind = e.kind;
return *e.slot;
}
}
Noesis::Symbol sym(name, Noesis::Symbol::NullIfNotFound());
if (sym.IsNull()) return nullptr;
const Noesis::RoutedEvent* ev = Noesis::FindRoutedEvent(element->GetClassType(), sym);
if (ev) {
outKind = ARG_ROUTED;
}
return ev;
}
class RustRoutedHandler {
public:
RustRoutedHandler(noesis_routed_event_fn cb, void* userdata, Noesis::UIElement* element,
const Noesis::RoutedEvent* ev, int32_t kind, bool handledToo)
: mCb(cb), mUserdata(userdata), mElement(element), mEvent(ev), mKind(kind),
mHandledToo(handledToo)
{
if (mElement) {
mElement->AddReference();
}
}
~RustRoutedHandler() {
if (mElement) {
mElement->Release();
}
}
RustRoutedHandler(const RustRoutedHandler&) = delete;
RustRoutedHandler& operator=(const RustRoutedHandler&) = delete;
void OnEvent(Noesis::BaseComponent* , const Noesis::RoutedEventArgs& args) {
if (!mCb) return;
if (!mHandledToo && args.handled) return;
DmEventArgs wrap{mKind, &args};
bool handled = args.handled;
mCb(mUserdata, &wrap, &handled);
if (handled) {
args.handled = true;
}
}
Noesis::UIElement* element() const { return mElement; }
const Noesis::RoutedEvent* event() const { return mEvent; }
private:
noesis_routed_event_fn mCb;
void* mUserdata;
Noesis::UIElement* mElement; const Noesis::RoutedEvent* mEvent;
int32_t mKind;
bool mHandledToo;
};
}
extern "C" void* noesis_subscribe_event(
void* element, const char* event_name, bool handled_too, noesis_routed_event_fn cb,
void* userdata)
{
if (!element || !event_name || !cb) return nullptr;
auto* uie = Noesis::DynamicCast<Noesis::UIElement*>(static_cast<Noesis::BaseComponent*>(element));
if (!uie) return nullptr;
int32_t kind = ARG_ROUTED;
const Noesis::RoutedEvent* ev = LookupEvent(uie, event_name, kind);
if (!ev) return nullptr;
auto* handler = new RustRoutedHandler(cb, userdata, uie, ev, kind, handled_too);
uie->AddHandler(ev, Noesis::MakeDelegate(handler, &RustRoutedHandler::OnEvent));
return handler;
}
extern "C" void noesis_unsubscribe_event(void* token) {
if (!token) return;
auto* handler = static_cast<RustRoutedHandler*>(token);
if (auto* uie = handler->element()) {
uie->RemoveHandler(handler->event(),
Noesis::MakeDelegate(handler, &RustRoutedHandler::OnEvent));
}
delete handler;
}
extern "C" bool noesis_mouse_args_position(const void* args, float* x, float* y) {
if (!args) return false;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_MOUSE && w->kind != ARG_MOUSE_BUTTON &&
w->kind != ARG_MOUSE_WHEEL) {
return false;
}
auto* m = static_cast<const Noesis::MouseEventArgs*>(w->args);
if (x) *x = m->position.x;
if (y) *y = m->position.y;
return true;
}
extern "C" int32_t noesis_mouse_button_args_button(const void* args) {
if (!args) return -1;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_MOUSE_BUTTON) return -1;
auto* m = static_cast<const Noesis::MouseButtonEventArgs*>(w->args);
return static_cast<int32_t>(m->changedButton);
}
extern "C" int32_t noesis_mouse_wheel_args_delta(const void* args) {
if (!args) return 0;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_MOUSE_WHEEL) return 0;
auto* m = static_cast<const Noesis::MouseWheelEventArgs*>(w->args);
return static_cast<int32_t>(m->wheelRotation);
}
extern "C" int32_t noesis_key_args_key(const void* args) {
if (!args) return -1;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_KEY) return -1;
auto* k = static_cast<const Noesis::KeyEventArgs*>(w->args);
return static_cast<int32_t>(k->key);
}
extern "C" int32_t noesis_text_args_ch(const void* args) {
if (!args) return -1;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_TEXT_INPUT) return -1;
auto* t = static_cast<const Noesis::TextCompositionEventArgs*>(w->args);
return static_cast<int32_t>(t->ch);
}
extern "C" bool noesis_size_changed_args_new_size(const void* args, float* width, float* height) {
if (!args) return false;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_SIZE_CHANGED) return false;
auto* s = static_cast<const Noesis::SizeChangedEventArgs*>(w->args);
if (width) *width = s->newSize.width;
if (height) *height = s->newSize.height;
return true;
}
extern "C" void* noesis_routed_args_source(const void* args) {
if (!args) return nullptr;
auto* w = static_cast<const DmEventArgs*>(args);
return w->args ? w->args->source : nullptr;
}
extern "C" void* noesis_routed_events_focus_old(const void* args) {
if (!args) return nullptr;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_FOCUS_CHANGED) return nullptr;
auto* f = static_cast<const Noesis::KeyboardFocusChangedEventArgs*>(w->args);
return f->oldFocus;
}
extern "C" void* noesis_routed_events_focus_new(const void* args) {
if (!args) return nullptr;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_FOCUS_CHANGED) return nullptr;
auto* f = static_cast<const Noesis::KeyboardFocusChangedEventArgs*>(w->args);
return f->newFocus;
}
extern "C" bool noesis_routed_events_drag_effects(
const void* args, uint32_t* effects, uint32_t* allowed, uint32_t* key_states)
{
if (!args) return false;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_DRAG) return false;
auto* d = static_cast<const Noesis::DragEventArgs*>(w->args);
if (effects) *effects = d->effects;
if (allowed) *allowed = d->allowedEffects;
if (key_states) *key_states = d->keyStates;
return true;
}
extern "C" bool noesis_routed_events_drag_set_effects(const void* args, uint32_t effects) {
if (!args) return false;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_DRAG) return false;
auto* d = static_cast<const Noesis::DragEventArgs*>(w->args);
d->effects = effects;
return true;
}
extern "C" void* noesis_routed_events_drag_data(const void* args) {
if (!args) return nullptr;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_DRAG) return nullptr;
auto* d = static_cast<const Noesis::DragEventArgs*>(w->args);
return d->data;
}
extern "C" bool noesis_routed_events_drag_position(
const void* args, void* relative_to, float* x, float* y)
{
if (!args || !relative_to) return false;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_DRAG) return false;
auto* d = static_cast<const Noesis::DragEventArgs*>(w->args);
Noesis::Point p = d->GetPosition(static_cast<Noesis::UIElement*>(relative_to));
if (x) *x = p.x;
if (y) *y = p.y;
return true;
}
extern "C" bool noesis_routed_events_manip_origin(const void* args, float* x, float* y) {
if (!args) return false;
auto* w = static_cast<const DmEventArgs*>(args);
Noesis::Point origin;
switch (w->kind) {
case ARG_MANIP_STARTED:
origin = static_cast<const Noesis::ManipulationStartedEventArgs*>(w->args)->manipulationOrigin;
break;
case ARG_MANIP_DELTA:
origin = static_cast<const Noesis::ManipulationDeltaEventArgs*>(w->args)->manipulationOrigin;
break;
case ARG_MANIP_COMPLETED:
origin = static_cast<const Noesis::ManipulationCompletedEventArgs*>(w->args)->manipulationOrigin;
break;
case ARG_MANIP_INERTIA:
origin = static_cast<const Noesis::ManipulationInertiaStartingEventArgs*>(w->args)->manipulationOrigin;
break;
default:
return false;
}
if (x) *x = origin.x;
if (y) *y = origin.y;
return true;
}
extern "C" bool noesis_routed_events_manip_delta(
const void* args, float* tx, float* ty, float* scale, float* rotation, float* ex, float* ey)
{
if (!args) return false;
auto* w = static_cast<const DmEventArgs*>(args);
const Noesis::ManipulationDelta* m = nullptr;
if (w->kind == ARG_MANIP_DELTA) {
m = &static_cast<const Noesis::ManipulationDeltaEventArgs*>(w->args)->deltaManipulation;
} else if (w->kind == ARG_MANIP_COMPLETED) {
m = &static_cast<const Noesis::ManipulationCompletedEventArgs*>(w->args)->totalManipulation;
} else {
return false;
}
if (tx) *tx = m->translation.x;
if (ty) *ty = m->translation.y;
if (scale) *scale = m->scale;
if (rotation) *rotation = m->rotation;
if (ex) *ex = m->expansion.x;
if (ey) *ey = m->expansion.y;
return true;
}
extern "C" bool noesis_routed_events_manip_cumulative(
const void* args, float* tx, float* ty, float* scale, float* rotation, float* ex, float* ey)
{
if (!args) return false;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind != ARG_MANIP_DELTA) return false;
const Noesis::ManipulationDelta& m =
static_cast<const Noesis::ManipulationDeltaEventArgs*>(w->args)->cumulativeManipulation;
if (tx) *tx = m.translation.x;
if (ty) *ty = m.translation.y;
if (scale) *scale = m.scale;
if (rotation) *rotation = m.rotation;
if (ex) *ex = m.expansion.x;
if (ey) *ey = m.expansion.y;
return true;
}
extern "C" bool noesis_routed_events_manip_velocities(
const void* args, float* angular, float* lx, float* ly, float* ex, float* ey)
{
if (!args) return false;
auto* w = static_cast<const DmEventArgs*>(args);
const Noesis::ManipulationVelocities* v = nullptr;
switch (w->kind) {
case ARG_MANIP_DELTA:
v = &static_cast<const Noesis::ManipulationDeltaEventArgs*>(w->args)->velocities;
break;
case ARG_MANIP_COMPLETED:
v = &static_cast<const Noesis::ManipulationCompletedEventArgs*>(w->args)->finalVelocities;
break;
case ARG_MANIP_INERTIA:
v = &static_cast<const Noesis::ManipulationInertiaStartingEventArgs*>(w->args)->initialVelocities;
break;
default:
return false;
}
if (angular) *angular = v->angularVelocity;
if (lx) *lx = v->linearVelocity.x;
if (ly) *ly = v->linearVelocity.y;
if (ex) *ex = v->expansionVelocity.x;
if (ey) *ey = v->expansionVelocity.y;
return true;
}
extern "C" int32_t noesis_routed_events_manip_is_inertial(const void* args) {
if (!args) return -1;
auto* w = static_cast<const DmEventArgs*>(args);
if (w->kind == ARG_MANIP_DELTA) {
return static_cast<const Noesis::ManipulationDeltaEventArgs*>(w->args)->isInertial ? 1 : 0;
}
if (w->kind == ARG_MANIP_COMPLETED) {
return static_cast<const Noesis::ManipulationCompletedEventArgs*>(w->args)->isInertial ? 1 : 0;
}
return -1;
}
extern "C" bool noesis_routed_events_do_drag_drop(
void* source, void* data, uint32_t allowed_effects)
{
if (!source || !data) return false;
auto* dep = Noesis::DynamicCast<Noesis::DependencyObject*>(
static_cast<Noesis::BaseComponent*>(source));
if (!dep) return false;
Noesis::DragDrop::DoDragDrop(dep, static_cast<Noesis::BaseComponent*>(data), allowed_effects);
return true;
}
namespace {
class RustDataObjectHandler {
public:
enum Kind { Copying, Pasting };
RustDataObjectHandler(noesis_data_object_fn cb, void* userdata,
Noesis::UIElement* element, Kind kind)
: mCb(cb), mUserdata(userdata), mElement(element), mKind(kind)
{
if (mElement) {
mElement->AddReference();
}
}
~RustDataObjectHandler() {
if (mElement) {
mElement->Release();
}
}
RustDataObjectHandler(const RustDataObjectHandler&) = delete;
RustDataObjectHandler& operator=(const RustDataObjectHandler&) = delete;
void OnCopying(Noesis::BaseComponent* ,
const Noesis::DataObjectCopyingEventArgs& e) {
if (!mCb) return;
bool cancel = e.commandCancelled;
mCb(mUserdata, e.dataObject.GetPtr(), e.isDragDrop, &cancel);
e.commandCancelled = cancel;
}
void OnPasting(Noesis::BaseComponent* ,
const Noesis::DataObjectPastingEventArgs& e) {
if (!mCb) return;
bool cancel = e.commandCancelled;
mCb(mUserdata, e.dataObject.GetPtr(), e.isDragDrop, &cancel);
e.commandCancelled = cancel;
}
Noesis::UIElement* element() const { return mElement; }
Kind kind() const { return mKind; }
private:
noesis_data_object_fn mCb;
void* mUserdata;
Noesis::UIElement* mElement; Kind mKind;
};
}
extern "C" void* noesis_routed_events_add_copying_handler(
void* element, noesis_data_object_fn cb, void* userdata)
{
if (!element || !cb) return nullptr;
auto* uie = Noesis::DynamicCast<Noesis::UIElement*>(
static_cast<Noesis::BaseComponent*>(element));
if (!uie) return nullptr;
auto* handler = new RustDataObjectHandler(cb, userdata, uie, RustDataObjectHandler::Copying);
Noesis::DataObject::AddCopyingHandler(
uie, Noesis::MakeDelegate(handler, &RustDataObjectHandler::OnCopying));
return handler;
}
extern "C" void* noesis_routed_events_add_pasting_handler(
void* element, noesis_data_object_fn cb, void* userdata)
{
if (!element || !cb) return nullptr;
auto* uie = Noesis::DynamicCast<Noesis::UIElement*>(
static_cast<Noesis::BaseComponent*>(element));
if (!uie) return nullptr;
auto* handler = new RustDataObjectHandler(cb, userdata, uie, RustDataObjectHandler::Pasting);
Noesis::DataObject::AddPastingHandler(
uie, Noesis::MakeDelegate(handler, &RustDataObjectHandler::OnPasting));
return handler;
}
extern "C" void noesis_routed_events_remove_data_object_handler(void* token) {
if (!token) return;
auto* handler = static_cast<RustDataObjectHandler*>(token);
if (auto* uie = handler->element()) {
if (handler->kind() == RustDataObjectHandler::Copying) {
Noesis::DataObject::RemoveCopyingHandler(
uie, Noesis::MakeDelegate(handler, &RustDataObjectHandler::OnCopying));
} else {
Noesis::DataObject::RemovePastingHandler(
uie, Noesis::MakeDelegate(handler, &RustDataObjectHandler::OnPasting));
}
}
delete handler;
}
namespace {
class RustLifecycleHandler {
public:
RustLifecycleHandler(noesis_lifecycle_fn cb, void* userdata,
Noesis::FrameworkElement* element, const char* name)
: mCb(cb), mUserdata(userdata), mElement(element)
{
if (mElement) {
mElement->AddReference();
}
const size_t n = strlen(name);
mName = new char[n + 1];
memcpy(mName, name, n + 1);
}
~RustLifecycleHandler() {
if (mElement) {
mElement->Release();
}
delete[] mName;
}
RustLifecycleHandler(const RustLifecycleHandler&) = delete;
RustLifecycleHandler& operator=(const RustLifecycleHandler&) = delete;
void OnEvent(Noesis::BaseComponent* , const Noesis::EventArgs& ) {
if (mCb) mCb(mUserdata);
}
void OnDpEvent(Noesis::BaseComponent* ,
const Noesis::DependencyPropertyChangedEventArgs& ) {
if (mCb) mCb(mUserdata);
}
Noesis::FrameworkElement* element() const { return mElement; }
const char* name() const { return mName; }
private:
noesis_lifecycle_fn mCb;
void* mUserdata;
Noesis::FrameworkElement* mElement; char* mName;
};
bool ApplyLifecycle(
Noesis::FrameworkElement* fe, const char* name, RustLifecycleHandler* h, bool add)
{
#define LC_PLAIN(N, ACC) \
if (strcmp(name, N) == 0) { \
if (add) fe->ACC() += Noesis::MakeDelegate(h, &RustLifecycleHandler::OnEvent); \
else fe->ACC() -= Noesis::MakeDelegate(h, &RustLifecycleHandler::OnEvent); \
return true; \
}
#define LC_DP(N, ACC) \
if (strcmp(name, N) == 0) { \
if (add) fe->ACC() += Noesis::MakeDelegate(h, &RustLifecycleHandler::OnDpEvent); \
else fe->ACC() -= Noesis::MakeDelegate(h, &RustLifecycleHandler::OnDpEvent); \
return true; \
}
LC_PLAIN("Initialized", Initialized)
LC_PLAIN("LayoutUpdated", LayoutUpdated)
LC_DP("IsEnabledChanged", IsEnabledChanged)
LC_DP("IsVisibleChanged", IsVisibleChanged)
LC_DP("IsHitTestVisibleChanged", IsHitTestVisibleChanged)
LC_DP("IsKeyboardFocusedChanged", IsKeyboardFocusedChanged)
LC_DP("IsKeyboardFocusWithinChanged", IsKeyboardFocusWithinChanged)
LC_DP("IsMouseCapturedChanged", IsMouseCapturedChanged)
LC_DP("IsMouseCaptureWithinChanged", IsMouseCaptureWithinChanged)
LC_DP("IsMouseDirectlyOverChanged", IsMouseDirectlyOverChanged)
LC_DP("FocusableChanged", FocusableChanged)
LC_DP("DataContextChanged", DataContextChanged)
#undef LC_PLAIN
#undef LC_DP
return false;
}
}
extern "C" void* noesis_subscribe_lifecycle(
void* element, const char* event_name, noesis_lifecycle_fn cb, void* userdata)
{
if (!element || !event_name || !cb) return nullptr;
auto* fe = Noesis::DynamicCast<Noesis::FrameworkElement*>(
static_cast<Noesis::BaseComponent*>(element));
if (!fe) return nullptr;
auto* handler = new RustLifecycleHandler(cb, userdata, fe, event_name);
if (!ApplyLifecycle(fe, handler->name(), handler, true)) {
delete handler;
return nullptr;
}
return handler;
}
extern "C" void noesis_unsubscribe_lifecycle(void* token) {
if (!token) return;
auto* handler = static_cast<RustLifecycleHandler*>(token);
if (auto* fe = handler->element()) {
ApplyLifecycle(fe, handler->name(), handler, false);
}
delete handler;
}
#ifdef NOESIS_TEST_UTILS
extern "C" void noesis_routed_events_test_raise_drag(
void* element, noesis_routed_event_fn cb, void* userdata)
{
if (!element || !cb) return;
auto* uie = static_cast<Noesis::UIElement*>(element);
Noesis::DragEventArgs args(uie, Noesis::UIElement::DropEvent, uie,
Noesis::DragDropKeyStates_ControlKey,
Noesis::DragDropEffects_All, uie, Noesis::Point(12.0f, 34.0f));
args.effects = Noesis::DragDropEffects_Copy;
DmEventArgs wrap{ARG_DRAG, &args};
bool handled = false;
cb(userdata, &wrap, &handled);
}
extern "C" void noesis_routed_events_test_raise_manip_delta(
void* element, noesis_routed_event_fn cb, void* userdata)
{
if (!element || !cb) return;
auto* uie = static_cast<Noesis::UIElement*>(element);
Noesis::ManipulationDelta delta{Noesis::Point(3.0f, 4.0f), 15.0f, 2.0f, Noesis::Point(5.0f, 7.0f)};
Noesis::ManipulationDelta cumulative{Noesis::Point(6.0f, 8.0f), 30.0f, 4.0f, Noesis::Point(50.0f, 70.0f)};
Noesis::ManipulationVelocities velocities{1.5f, Noesis::Point(0.1f, 0.2f), Noesis::Point(0.5f, 0.6f)};
Noesis::ManipulationDeltaEventArgs args(uie, Noesis::UIElement::ManipulationDeltaEvent,
nullptr, Noesis::Point(100.0f, 200.0f), delta, cumulative, velocities,
true, Noesis::ArrayRef<Noesis::Manipulator>());
DmEventArgs wrap{ARG_MANIP_DELTA, &args};
bool handled = false;
cb(userdata, &wrap, &handled);
}
extern "C" void noesis_routed_events_test_raise_manip_completed(
void* element, noesis_routed_event_fn cb, void* userdata)
{
if (!element || !cb) return;
auto* uie = static_cast<Noesis::UIElement*>(element);
Noesis::ManipulationDelta total{Noesis::Point(1.0f, 2.0f), 45.0f, 3.0f, Noesis::Point(11.0f, 13.0f)};
Noesis::ManipulationVelocities velocities{2.5f, Noesis::Point(1.1f, 1.2f), Noesis::Point(1.5f, 1.6f)};
Noesis::ManipulationCompletedEventArgs args(uie, Noesis::UIElement::ManipulationCompletedEvent,
nullptr, Noesis::Point(100.0f, 200.0f), velocities, total, false,
Noesis::ArrayRef<Noesis::Manipulator>());
DmEventArgs wrap{ARG_MANIP_COMPLETED, &args};
bool handled = false;
cb(userdata, &wrap, &handled);
}
#endif