-
Notifications
You must be signed in to change notification settings - Fork 713
NOT FUNCTIONAL: muda
context menu support
#8803
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
29e6363
92a37d6
d095406
59f1e73
7a9507f
7a6a588
084d2e4
41f9bf6
12cf3d0
11300e3
086c867
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,16 @@ | ||
// Copyright © SixtyFPS GmbH <info@slint.dev> | ||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 | ||
|
||
use super::event_loop::MudaType; | ||
use super::CustomEvent; | ||
use super::WinitWindowAdapter; | ||
use crate::SlintEvent; | ||
use core::pin::Pin; | ||
use i_slint_core::api::LogicalPosition; | ||
use i_slint_core::items::MenuEntry; | ||
use i_slint_core::menus::MenuVTable; | ||
use i_slint_core::properties::{PropertyDirtyHandler, PropertyTracker}; | ||
use muda::ContextMenu; | ||
use std::rc::Weak; | ||
use winit::event_loop::EventLoopProxy; | ||
use winit::window::Window; | ||
|
@@ -42,7 +46,7 @@ impl MudaAdapter { | |
let menu = muda::Menu::new(); | ||
|
||
muda::MenuEvent::set_event_handler(Some(move |e| { | ||
let _ = proxy.send_event(SlintEvent(crate::event_loop::CustomEvent::Muda(e))); | ||
let _ = proxy.send_event(SlintEvent(CustomEvent::Muda(e, MudaType::Menubar))); | ||
})); | ||
|
||
#[cfg(target_os = "windows")] | ||
|
@@ -67,6 +71,41 @@ impl MudaAdapter { | |
s | ||
} | ||
|
||
pub fn show_context_menu( | ||
context_menu: &vtable::VBox<MenuVTable>, | ||
winit_window: &Window, | ||
position: LogicalPosition, | ||
proxy: EventLoopProxy<SlintEvent>, | ||
) -> Self { | ||
let menu = muda::Menu::new(); | ||
|
||
muda::MenuEvent::set_event_handler(Some(move |e| { | ||
let _ = proxy.send_event(SlintEvent(CustomEvent::Muda(e, MudaType::Context))); | ||
})); | ||
|
||
let mut s = Self { entries: Default::default(), tracker: None, menu }; | ||
s.rebuild_menu(winit_window, Some(context_menu)); | ||
|
||
let position = i_slint_core::api::WindowPosition::Logical(position); | ||
let position = Some(crate::winitwindowadapter::position_to_winit(&position)); | ||
|
||
#[cfg(target_os = "windows")] | ||
{ | ||
use winit::raw_window_handle::*; | ||
if let RawWindowHandle::Win32(handle) = winit_window.window_handle().unwrap().as_raw() { | ||
unsafe { | ||
s.menu.show_context_menu_for_hwnd(handle.hwnd.get(), position); | ||
} | ||
} | ||
} | ||
#[cfg(target_os = "macos")] | ||
{ | ||
todo!(); // unsafe { menu.show_context_menu_for_nsview(nsview, position) }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if this function returned an option, you could return None there. And WindowAdatper::show_context_menu could return false to tell the runtime to fallback to the Slint based menu. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose I was assuming (perhaps incorrectly) that the macOS version of the call was a relatively simple call, and that it in practice it might be easier to just identify the right call than it would be to build a bunch of scaffolding? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nvm - saw your next comment |
||
} | ||
|
||
s | ||
} | ||
|
||
pub fn rebuild_menu( | ||
&mut self, | ||
winit_window: &Window, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2764,7 +2764,8 @@ fn compile_builtin_function_call( | |
} | ||
} | ||
BuiltinFunction::ShowPopupMenu => { | ||
let [Expression::PropertyReference(context_menu_ref), entries, position] = arguments | ||
let [Expression::PropertyReference(context_menu_ref), entries, position, Expression::BoolLiteral(no_native)] = | ||
arguments | ||
else { | ||
panic!("internal error: invalid args to ShowPopupMenu {arguments:?}") | ||
}; | ||
|
@@ -2855,7 +2856,7 @@ fn compile_builtin_function_call( | |
let entries = #entries; | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we only need this part when not using the native context menu (fallback) |
||
let _self = popup_instance_vrc.as_pin_ref(); | ||
#access_entries.set(entries); | ||
#access_entries.set(entries.clone()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need to clone here? |
||
#fw_sub_menu | ||
#fw_activated | ||
let self_weak = parent_weak.clone(); | ||
|
@@ -2868,22 +2869,37 @@ fn compile_builtin_function_call( | |
} | ||
}; | ||
let context_menu = context_menu.unwrap(); | ||
|
||
let native_impl = if *no_native { | ||
quote!() | ||
} else { | ||
quote!(if sp::WindowInner::from_pub(#window_adapter_tokens.window()).supports_native_menu_bar() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. re-using the supports_native_menu_bar for the context menu might not be needed. I can imagine platform having a native context menu but no native menubar or the opposite. |
||
sp::WindowInner::from_pub(#window_adapter_tokens.window()).show_context_menu(sp::VBox::new(popup_instance_menu), #position); | ||
} else) | ||
}; | ||
|
||
quote!({ | ||
let position = #position; | ||
let popup_instance = #popup_id::new(_self.globals.get().unwrap().clone()).unwrap(); | ||
let popup_instance_vrc = sp::VRc::map(popup_instance.clone(), |x| x); | ||
let parent_weak = _self.self_weak.get().unwrap().clone(); | ||
#init_popup | ||
#close_popup | ||
let id = sp::WindowInner::from_pub(#window_adapter_tokens.window()).show_popup( | ||
&sp::VRc::into_dyn(popup_instance.into()), | ||
position, | ||
sp::PopupClosePolicy::CloseOnClickOutside, | ||
#context_menu_rc, | ||
true, // is_menu | ||
); | ||
#context_menu.popup_id.set(Some(id)); | ||
#popup_id::user_init(popup_instance_vrc); | ||
{ | ||
let parent_weak = _self.self_weak.get().unwrap().clone(); | ||
#init_popup | ||
let popup_instance_menu = sp::ContextMenuFromItemTree::new(sp::VRc::into_dyn(popup_instance.clone()), entries.clone()); | ||
#native_impl | ||
/* else */ { | ||
#close_popup | ||
let id = sp::WindowInner::from_pub(#window_adapter_tokens.window()).show_popup( | ||
&sp::VRc::into_dyn(popup_instance.into()), | ||
position, | ||
sp::PopupClosePolicy::CloseOnClickOutside, | ||
#context_menu_rc, | ||
true, // is_menu | ||
); | ||
#context_menu.popup_id.set(Some(id)); | ||
#popup_id::user_init(popup_instance_vrc); | ||
} | ||
} | ||
}) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note that you only take care of the There, the context_menu_item_tree is in a Rc instead of a VBox. It might be worth putting them in a VRc in both branches. |
||
BuiltinFunction::SetSelectionOffsets => { | ||
|
@@ -3107,7 +3123,7 @@ fn compile_builtin_function_call( | |
}; | ||
|
||
quote!({ | ||
let menu_item_tree_instance = #item_tree_id::new(_self.self_weak.get().unwrap().clone()).unwrap(); | ||
let menu_item_tree_instance = #item_tree_id::new(_self.self_weak.get().unwrap().clone()).unwrap(); // BLGAG | ||
let menu_item_tree = sp::MenuFromItemTree::new(sp::VRc::into_dyn(menu_item_tree_instance)); | ||
#native_impl | ||
/*else*/ { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -153,14 +153,15 @@ pub async fn lower_menus( | |
|
||
let mut has_menu = false; | ||
let mut has_menubar = false; | ||
let no_native_menu = type_loader.compiler_config.no_native_menu; | ||
|
||
doc.visit_all_used_components(|component| { | ||
recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| { | ||
if matches!(&elem.borrow().builtin_type(), Some(b) if b.name == "Window") { | ||
has_menubar |= process_window(elem, &useful_menu_component, type_loader.compiler_config.no_native_menu, diag); | ||
} | ||
if matches!(&elem.borrow().builtin_type(), Some(b) if matches!(b.name.as_str(), "ContextMenuArea" | "ContextMenuInternal")) { | ||
has_menu |= process_context_menu(elem, &useful_menu_component, diag); | ||
has_menu |= process_context_menu(elem, &useful_menu_component, diag, no_native_menu); | ||
} | ||
}) | ||
}); | ||
|
@@ -169,7 +170,8 @@ pub async fn lower_menus( | |
recurse_elem_including_sub_components_no_borrow(&menubar_impl, &(), &mut |elem, _| { | ||
if matches!(&elem.borrow().builtin_type(), Some(b) if matches!(b.name.as_str(), "ContextMenuArea" | "ContextMenuInternal")) | ||
{ | ||
has_menu |= process_context_menu(elem, &useful_menu_component, diag); | ||
has_menu |= | ||
process_context_menu(elem, &useful_menu_component, diag, no_native_menu); | ||
} | ||
}); | ||
} | ||
|
@@ -197,7 +199,7 @@ pub async fn lower_menus( | |
recurse_elem_including_sub_components_no_borrow(&popup_menu_impl, &(), &mut |elem, _| { | ||
if matches!(&elem.borrow().builtin_type(), Some(b) if matches!(b.name.as_str(), "ContextMenuArea" | "ContextMenuInternal")) | ||
{ | ||
process_context_menu(elem, &useful_menu_component, diag); | ||
process_context_menu(elem, &useful_menu_component, diag, no_native_menu); | ||
} | ||
}); | ||
doc.popup_menu_impl = popup_menu_impl.into(); | ||
|
@@ -208,6 +210,7 @@ fn process_context_menu( | |
context_menu_elem: &ElementRc, | ||
components: &UsefulMenuComponents, | ||
diag: &mut BuildDiagnostics, | ||
no_native_menu: bool, | ||
) -> bool { | ||
let is_internal = matches!(&context_menu_elem.borrow().base_type, ElementType::Builtin(b) if b.name == "ContextMenuInternal"); | ||
|
||
|
@@ -303,6 +306,7 @@ fn process_context_menu( | |
index: 0, | ||
ty: crate::typeregister::logical_point_type(), | ||
}, | ||
Expression::BoolLiteral(no_native_menu), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The no_native_menu was done for the live preview because we don't want to replace the live previews's menubar by the previewed menubar. |
||
], | ||
source_location, | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably best to move that to muda.rs