C++: adapt to menu bar API change

The MenuVTable is now a VRc (and it was a Box before)
This commit is contained in:
Olivier Goffart 2025-07-29 11:45:02 +02:00
parent 19bc3c8f82
commit ab674f33fb
6 changed files with 95 additions and 70 deletions

View file

@ -3,11 +3,6 @@
#pragma once
#if defined(__GNUC__) || defined(__clang__)
// In C++17, it is conditionally supported, but still valid for all compiler we care
# pragma GCC diagnostic ignored "-Winvalid-offsetof"
#endif
#include "slint_internal.h"
#include "slint_platform_internal.h"
#include "slint_qt_internal.h"
@ -44,23 +39,16 @@ inline cbindgen_private::Rect convert_anonymous_rect(std::tuple<float, float, fl
return cbindgen_private::Rect { .x = x, .y = y, .width = w, .height = h };
}
inline void dealloc(const ItemTreeVTable *, uint8_t *ptr, [[maybe_unused]] vtable::Layout layout)
inline void dealloc(const ItemTreeVTable *vtable, uint8_t *ptr,
[[maybe_unused]] vtable::Layout layout)
{
#ifdef __cpp_sized_deallocation
::operator delete(reinterpret_cast<void *>(ptr), layout.size,
static_cast<std::align_val_t>(layout.align));
#elif !defined(__APPLE__) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
::operator delete(reinterpret_cast<void *>(ptr), static_cast<std::align_val_t>(layout.align));
#else
::operator delete(reinterpret_cast<void *>(ptr));
#endif
vtable::dealloc(vtable, ptr, layout);
}
template<typename T>
inline vtable::Layout drop_in_place(ItemTreeRef item_tree)
{
reinterpret_cast<T *>(item_tree.instance)->~T();
return vtable::Layout { sizeof(T), alignof(T) };
return vtable::drop_in_place<ItemTreeVTable, T>(item_tree);
}
#if !defined(DOXYGEN)
@ -184,6 +172,19 @@ item_layout_info(VT *itemvtable, ItemType *item_ptr, cbindgen_private::Orientati
namespace private_api {
template<typename T>
union MaybeUninitialized {
T value;
~MaybeUninitialized() { }
MaybeUninitialized() { }
T take()
{
T result = std::move(value);
value.~T();
return result;
}
};
inline void setup_popup_menu_from_menu_item_tree(
const ItemTreeRc &menu_item_tree,
Property<std::shared_ptr<Model<cbindgen_private::MenuEntry>>> &entries,
@ -193,26 +194,23 @@ inline void setup_popup_menu_from_menu_item_tree(
{
using cbindgen_private::MenuEntry;
using cbindgen_private::MenuVTable;
auto shared = std::make_shared<vtable::VBox<MenuVTable>>(nullptr, nullptr);
cbindgen_private::slint_menus_create_wrapper(&menu_item_tree, &*shared);
MaybeUninitialized<vtable::VRc<MenuVTable>> maybe;
cbindgen_private::slint_menus_create_wrapper(&menu_item_tree, &maybe.value);
auto shared = maybe.take();
entries.set_binding([shared] {
vtable::VRefMut<MenuVTable> ref { shared->vtable, shared->instance };
SharedVector<MenuEntry> entries_sv;
shared->vtable->sub_menu(ref, nullptr, &entries_sv);
shared.vtable()->sub_menu(shared.borrow(), nullptr, &entries_sv);
std::vector<MenuEntry> entries_vec(entries_sv.begin(), entries_sv.end());
return std::make_shared<VectorModel<MenuEntry>>(std::move(entries_vec));
});
sub_menu.set_handler([shared](const auto &entry) {
vtable::VRefMut<MenuVTable> ref { shared->vtable, shared->instance };
SharedVector<MenuEntry> entries_sv;
shared->vtable->sub_menu(ref, &entry, &entries_sv);
shared.vtable()->sub_menu(shared.borrow(), &entry, &entries_sv);
std::vector<MenuEntry> entries_vec(entries_sv.begin(), entries_sv.end());
return std::make_shared<VectorModel<MenuEntry>>(std::move(entries_vec));
});
activated.set_handler([shared](const auto &entry) {
vtable::VRefMut<MenuVTable> ref { shared->vtable, shared->instance };
shared->vtable->activate(ref, &entry);
});
activated.set_handler(
[shared](const auto &entry) { shared.vtable()->activate(shared.borrow(), &entry); });
}
inline SharedString translate(const SharedString &original, const SharedString &context,

View file

@ -17,6 +17,39 @@ namespace private_api {
using ItemTreeRc = vtable::VRc<cbindgen_private::ItemTreeVTable>;
using slint::LogicalPosition;
template<typename Component, typename SubMenu, typename Activated>
struct MenuWrapper
{
Component component;
SubMenu submenu;
Activated activated;
static cbindgen_private::MenuVTable static_vtable;
};
template<typename Component, typename SubMenu, typename Activated>
inline cbindgen_private::MenuVTable MenuWrapper<Component, SubMenu, Activated>::static_vtable {
.sub_menu =
[](auto data, const cbindgen_private::MenuEntry *entry,
slint::SharedVector<cbindgen_private::MenuEntry> *result) {
auto wrapper = reinterpret_cast<MenuWrapper *>(data.instance);
auto model = wrapper->submenu(wrapper->component, entry);
result->clear();
if (model) {
auto count = model->row_count();
for (size_t i = 0; i < count; ++i) {
result->push_back(*model->row_data(i));
}
}
},
.activate =
[](auto data, const cbindgen_private::MenuEntry *entry) {
auto wrapper = reinterpret_cast<MenuWrapper *>(data.instance);
wrapper->activated(wrapper->component, *entry);
},
.drop_in_place = vtable::drop_in_place<cbindgen_private::MenuVTable, MenuWrapper>,
.dealloc = vtable::dealloc<cbindgen_private::MenuVTable>,
};
class WindowAdapterRc
{
public:
@ -62,35 +95,10 @@ public:
if (!supports_native_menu_bar()) {
return;
}
struct MenuWrapper
{
Component component;
SubMenu submenu;
Activated activated;
};
static cbindgen_private::MenuVTable menu_vtable = {
.drop = [](auto data) { delete reinterpret_cast<MenuWrapper *>(data.instance); },
.sub_menu =
[](auto data, const cbindgen_private::MenuEntry *entry,
slint::SharedVector<cbindgen_private::MenuEntry> *result) {
auto wrapper = reinterpret_cast<MenuWrapper *>(data.instance);
auto model = wrapper->submenu(wrapper->component, entry);
result->clear();
if (model) {
auto count = model->row_count();
for (size_t i = 0; i < count; ++i) {
result->push_back(*model->row_data(i));
}
}
},
.activate =
[](auto data, const cbindgen_private::MenuEntry *entry) {
auto wrapper = reinterpret_cast<MenuWrapper *>(data.instance);
wrapper->activated(wrapper->component, *entry);
},
};
auto instance = new MenuWrapper { component, std::move(submenu), std::move(activated) };
cbindgen_private::slint_windowrc_setup_native_menu_bar(&inner, &menu_vtable, instance);
using Wrapper = MenuWrapper<Component, SubMenu, Activated>;
auto instance = vtable::VRc<cbindgen_private::MenuVTable, Wrapper>::make(
Wrapper { component, std::move(submenu), std::move(activated) });
cbindgen_private::slint_windowrc_setup_native_menu_bar(&inner, &instance.into_dyn());
}
bool text_input_focused() const { return slint_windowrc_get_text_input_focused(&inner); }

View file

@ -13,6 +13,11 @@
# include <AvailabilityMacros.h>
#endif
#if defined(__GNUC__) || defined(__clang__)
// In C++17, it is conditionally supported, but still valid for all compiler we care
# pragma GCC diagnostic ignored "-Winvalid-offsetof"
#endif
namespace vtable {
template<typename T>
@ -242,4 +247,24 @@ public:
}
};
template<typename VTable>
inline void dealloc(const VTable *, uint8_t *ptr, [[maybe_unused]] Layout layout)
{
#ifdef __cpp_sized_deallocation
::operator delete(reinterpret_cast<void *>(ptr), layout.size,
static_cast<std::align_val_t>(layout.align));
#elif !defined(__APPLE__) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
::operator delete(reinterpret_cast<void *>(ptr), static_cast<std::align_val_t>(layout.align));
#else
::operator delete(reinterpret_cast<void *>(ptr));
#endif
}
template<typename VTable, typename T>
inline Layout drop_in_place(VRefMut<VTable> item_tree)
{
reinterpret_cast<T *>(item_tree.instance)->~T();
return vtable::Layout { sizeof(T), alignof(T) };
}
} // namespace vtable

View file

@ -3737,12 +3737,10 @@ fn compile_builtin_function_call(
if ({window}.supports_native_menu_bar()) {{
auto item_tree = {item_tree_id}::create(self);
auto item_tree_dyn = item_tree.into_dyn();
vtable::VBox<slint::cbindgen_private::MenuVTable> box{{}};
slint::cbindgen_private::slint_menus_create_wrapper(&item_tree_dyn, &box);
slint::cbindgen_private::slint_windowrc_setup_native_menu_bar(&{window}.handle(), const_cast<slint::cbindgen_private::MenuVTable*>(box.vtable), box.instance);
// The ownership of the VBox is transferred to slint_windowrc_setup_native_menu_bar
box.instance = nullptr;
box.vtable = nullptr;
slint::private_api::MaybeUninitialized<vtable::VRc<slint::cbindgen_private::MenuVTable>> maybe;
slint::cbindgen_private::slint_menus_create_wrapper(&item_tree_dyn, &maybe.value);
auto vrc = maybe.take();
slint::cbindgen_private::slint_windowrc_setup_native_menu_bar(&{window}.handle(), &vrc);
}} else {{
auto item_tree = {item_tree_id}::create(self);
auto item_tree_dyn = item_tree.into_dyn();

View file

@ -27,8 +27,6 @@ use vtable::{VRef, VRefMut};
#[vtable::vtable]
#[repr(C)]
pub struct MenuVTable {
/// destructor
drop: extern "C" fn(VRefMut<MenuVTable>),
/// Return the list of items for the sub menu (or the main menu of parent is None)
sub_menu: extern "C" fn(VRef<MenuVTable>, Option<&MenuEntry>, &mut SharedVector<MenuEntry>),
/// Handler when the menu entry is activated
@ -243,9 +241,9 @@ pub mod ffi {
#[unsafe(no_mangle)]
pub unsafe extern "C" fn slint_menus_create_wrapper(
menu_tree: &ItemTreeRc,
result: *mut vtable::VBox<MenuVTable>,
result: *mut vtable::VRc<MenuVTable>,
) {
let b = vtable::VBox::<MenuVTable>::new(MenuFromItemTree::new(menu_tree.clone()));
core::ptr::write(result, b);
let vrc = vtable::VRc::into_dyn(vtable::VRc::new(MenuFromItemTree::new(menu_tree.clone())));
core::ptr::write(result, vrc);
}
}

View file

@ -1494,7 +1494,6 @@ pub mod ffi {
use crate::graphics::Size;
use crate::graphics::{IntSize, Rgba8Pixel};
use crate::SharedVector;
use core::ptr::NonNull;
/// This enum describes a low-level access to specific graphics APIs used
/// by the renderer.
@ -1847,13 +1846,12 @@ pub mod ffi {
#[unsafe(no_mangle)]
pub unsafe extern "C" fn slint_windowrc_setup_native_menu_bar(
handle: *const WindowAdapterRcOpaque,
vtable: NonNull<MenuVTable>,
menu_instance: NonNull<c_void>,
menu_instance: &vtable::VRc<MenuVTable>,
) {
let window_adapter = &*(handle as *const Rc<dyn WindowAdapter>);
window_adapter
.internal(crate::InternalToken)
.map(|x| x.setup_menubar(vtable::VBox::from_raw(vtable, menu_instance.cast())));
.map(|x| x.setup_menubar(menu_instance.clone()));
}
/// Return the default-font-size property of the WindowItem