slint/api/cpp/include/vtable.h
Simon Hausmann c428601370
Add support for select-all(), cut(), copy() and paste() functions on text input elements (#2804)
In the compiler this is still very primitive, but an attempt to start a
generic interface. The basic assumption is that all item functions will
eventually need access to the window adapter and itemrc. Support for
additional arguments is still missing.

Also missing is support for the function access via rtti in the
interpreter, hence the hardcoding at the moment.
2023-06-01 16:04:53 +02:00

221 lines
5.2 KiB
C++

// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
#pragma once
#include <cstddef>
#include <new>
#include <algorithm>
#include <optional>
#include <atomic>
#ifdef __APPLE__
# include <AvailabilityMacros.h>
#endif
namespace vtable {
template<typename T>
struct VRefMut
{
const T *vtable;
void *instance;
};
struct Layout
{
std::size_t size;
std::size_t align;
};
// For the C++'s purpose, they are all the same
template<typename T>
using VRef = VRefMut<T>;
template<typename T>
using Pin = T;
template<typename T>
struct VBox
{
const T *vtable = nullptr;
void *instance = nullptr;
VBox(const VBox &) = delete;
VBox() = default;
VBox &operator=(const VBox &) = delete;
~VBox()
{
if (vtable && instance) {
vtable->drop({ vtable, instance });
}
}
};
struct AllowPin;
template<typename Base, typename T, typename Flag = void>
struct VOffset
{
const T *vtable;
std::uintptr_t offset;
};
template<typename VTable, typename X>
struct VRcInner
{
template<typename VTable_, typename X_>
friend class VRc;
template<typename VTable_, typename X_>
friend class VWeak;
private:
VRcInner() : layout {} { }
const VTable *vtable = &X::static_vtable;
std::atomic<int> strong_ref = 1;
std::atomic<int> weak_ref = 1;
std::uint16_t data_offset = offsetof(VRcInner, data);
union {
X data;
Layout layout;
};
void *data_ptr() { return reinterpret_cast<char *>(this) + data_offset; }
~VRcInner() = delete;
};
struct Dyn
{
};
template<typename VTable, typename X = Dyn>
class VRc
{
VRcInner<VTable, X> *inner;
VRc(VRcInner<VTable, X> *inner) : inner(inner) { }
template<typename VTable_, typename X_>
friend class VWeak;
public:
~VRc()
{
if (!--inner->strong_ref) {
Layout layout = inner->vtable->drop_in_place({ inner->vtable, &inner->data });
layout.size += inner->data_offset;
layout.align = std::max<size_t>(layout.align, alignof(VRcInner<VTable, Dyn>));
inner->layout = layout;
if (!--inner->weak_ref) {
inner->vtable->dealloc(inner->vtable, reinterpret_cast<uint8_t *>(inner), layout);
}
}
}
VRc(const VRc &other) : inner(other.inner) { inner->strong_ref++; }
VRc &operator=(const VRc &other)
{
if (inner == other.inner)
return *this;
this->~VRc();
new (this) VRc(other);
return *this;
}
/// Construct a new VRc holding an X.
///
/// The type X must have a static member `static_vtable` of type VTable
template<typename... Args>
static VRc make(Args... args)
{
#if !defined(__APPLE__) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
auto mem = ::operator new(sizeof(VRcInner<VTable, X>),
static_cast<std::align_val_t>(alignof(VRcInner<VTable, X>)));
#else
auto mem = ::operator new(sizeof(VRcInner<VTable, X>));
#endif
auto inner = new (mem) VRcInner<VTable, X>;
new (&inner->data) X(args...);
return VRc(inner);
}
const X *operator->() const
{
return &inner->data;
}
const X &operator*() const
{
return inner->data;
}
X *operator->()
{
return &inner->data;
}
X &operator*()
{
return inner->data;
}
const VRc<VTable, Dyn> &into_dyn() const
{
return *reinterpret_cast<const VRc<VTable, Dyn> *>(this);
}
VRef<VTable> borrow() const
{
return { inner->vtable, inner->data_ptr() };
}
friend bool operator==(const VRc &a, const VRc &b)
{
return a.inner == b.inner;
}
friend bool operator!=(const VRc &a, const VRc &b)
{
return a.inner != b.inner;
}
const VTable *vtable() const
{
return inner->vtable;
}
};
template<typename VTable, typename X = Dyn>
class VWeak
{
VRcInner<VTable, X> *inner = nullptr;
public:
VWeak() = default;
~VWeak()
{
if (inner && !--inner->weak_ref) {
inner->vtable->dealloc(inner->vtable, reinterpret_cast<uint8_t *>(inner),
inner->layout);
}
}
VWeak(const VWeak &other) : inner(other.inner) { inner && inner->weak_ref++; }
VWeak(const VRc<VTable, X> &other) : inner(other.inner) { inner && inner->weak_ref++; }
VWeak &operator=(const VWeak &other)
{
if (inner == other.inner)
return *this;
this->~VWeak();
new (this) VWeak(other);
return *this;
}
std::optional<VRc<VTable, X>> lock() const
{
if (!inner || inner->strong_ref == 0)
return {};
inner->strong_ref++;
return { VRc<VTable, X>(inner) };
}
const VWeak<VTable, Dyn> &into_dyn() const
{
return *reinterpret_cast<const VWeak<VTable, Dyn> *>(this);
}
friend bool operator==(const VWeak &a, const VWeak &b) { return a.inner == b.inner; }
friend bool operator!=(const VWeak &a, const VWeak &b) { return a.inner != b.inner; }
const VTable *vtable() const { return inner ? inner->vtable : nullptr; }
};
} // namespace vtable