use cxx::let_cxx_string;
use crate::ffi;
use crate::signal::{self, SignalHandle};
use crate::widget::AsWidget;
pub struct Menu {
ptr: *mut ffi::QMenu,
has_parent: bool,
signal_handles: Vec<SignalHandle>,
}
impl Menu {
pub fn new(title: impl Into<String>) -> MenuBuilder {
MenuBuilder::new(title.into())
}
#[doc(hidden)]
#[allow(dead_code)]
pub(crate) fn from_raw(ptr: *mut ffi::QMenu) -> Self {
debug_assert!(!ptr.is_null());
Self { ptr, has_parent: true, signal_handles: Vec::new() }
}
pub fn menu_ptr(&self) -> *mut ffi::QMenu {
self.ptr
}
}
impl AsWidget for Menu {
fn widget_ptr(&self) -> *mut ffi::QWidget {
debug_assert!(!self.ptr.is_null());
unsafe { ffi::toQWidget_QMenu(self.ptr) }
}
fn set_has_parent(&mut self) { self.has_parent = true; }
}
impl Drop for Menu {
fn drop(&mut self) {
if self.ptr.is_null() { return; }
if self.has_parent {
for h in self.signal_handles.drain(..) {
unsafe { h.reclaim(); }
}
} else {
for h in self.signal_handles.drain(..) {
unsafe { h.reclaim(); }
}
unsafe { ffi::QMenu_delete(self.ptr) };
}
self.ptr = std::ptr::null_mut();
}
}
pub struct MenuBuilder {
title: String,
actions: Vec<(String, Box<dyn Fn()>)>,
parent: Option<*mut ffi::QWidget>,
}
impl MenuBuilder {
fn new(title: String) -> Self {
Self { title, actions: Vec::new(), parent: None }
}
pub fn action<F: Fn() + 'static>(mut self, text: impl Into<String>, f: F) -> Self {
self.actions.push((text.into(), Box::new(f)));
self
}
pub fn parent(mut self, parent: &dyn AsWidget) -> Self {
self.parent = Some(parent.widget_ptr());
self
}
pub fn build(self) -> Menu {
let_cxx_string!(c_title = &self.title);
let ptr = unsafe {
ffi::QMenu_new(&c_title, self.parent.unwrap_or(std::ptr::null_mut()))
};
debug_assert!(!ptr.is_null());
let mut menu = Menu {
ptr,
has_parent: self.parent.is_some(),
signal_handles: Vec::new(),
};
for (text, cb) in self.actions {
let_cxx_string!(c_text = &text);
let handle = signal::leak_void(cb);
unsafe {
ffi::QMenu_addAction(ptr, &c_text, handle.token);
}
menu.signal_handles.push(handle);
}
menu
}
}
pub struct MenuBar {
ptr: *mut ffi::QMenuBar,
has_parent: bool,
menus: Vec<Menu>,
}
impl MenuBar {
pub fn new() -> MenuBarBuilder {
MenuBarBuilder::new()
}
#[doc(hidden)]
#[allow(dead_code)]
pub(crate) fn from_raw(ptr: *mut ffi::QMenuBar) -> Self {
debug_assert!(!ptr.is_null());
Self { ptr, has_parent: true, menus: Vec::new() }
}
}
impl AsWidget for MenuBar {
fn widget_ptr(&self) -> *mut ffi::QWidget {
debug_assert!(!self.ptr.is_null());
unsafe { ffi::toQWidget_QMenuBar(self.ptr) }
}
fn set_has_parent(&mut self) { self.has_parent = true; }
}
impl Drop for MenuBar {
fn drop(&mut self) {
if self.ptr.is_null() { return; }
if !self.has_parent {
self.menus.clear();
unsafe { ffi::QMenuBar_delete(self.ptr) };
}
self.ptr = std::ptr::null_mut();
}
}
pub struct MenuBarBuilder {
menus: Vec<Menu>,
parent: Option<*mut ffi::QWidget>,
}
impl MenuBarBuilder {
fn new() -> Self {
Self { menus: Vec::new(), parent: None }
}
pub fn add_menu(mut self, menu: Menu) -> Self {
self.menus.push(menu);
self
}
pub fn parent(mut self, parent: &dyn AsWidget) -> Self {
self.parent = Some(parent.widget_ptr());
self
}
pub fn build(self) -> MenuBar {
let ptr = unsafe {
ffi::QMenuBar_new(self.parent.unwrap_or(std::ptr::null_mut()))
};
debug_assert!(!ptr.is_null());
let mut menubar = MenuBar {
ptr,
has_parent: self.parent.is_some(),
menus: Vec::new(),
};
for menu in self.menus {
unsafe {
ffi::QMenuBar_addMenu(ptr, menu.menu_ptr());
}
menubar.menus.push(menu);
}
menubar
}
}