refactor: rewrite gtk backend from scratch (#24)

This commit is contained in:
Amr Bashir 2022-12-05 13:32:34 +02:00 committed by GitHub
parent 93fcdfbb04
commit 7de46e4b5a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 908 additions and 1460 deletions

View file

@ -13,7 +13,7 @@ use tao::platform::windows::{EventLoopBuilderExtWindows, WindowExtWindows};
use tao::{ use tao::{
event::{ElementState, Event, MouseButton, WindowEvent}, event::{ElementState, Event, MouseButton, WindowEvent},
event_loop::{ControlFlow, EventLoopBuilder}, event_loop::{ControlFlow, EventLoopBuilder},
window::WindowBuilder, window::{Window, WindowBuilder},
}; };
fn main() { fn main() {
@ -148,7 +148,7 @@ fn main() {
window_id, window_id,
.. ..
} => { } => {
if window_id == window.id() { if window_id == window2.id() {
x = position.x; x = position.x;
y = position.y; y = position.y;
} }
@ -164,12 +164,7 @@ fn main() {
.. ..
} => { } => {
if window_id == window2.id() { if window_id == window2.id() {
#[cfg(target_os = "windows")] show_context_menu(&window2, &file_m, x, y);
window_m.show_context_menu_for_hwnd(window2.hwnd() as _, x, y);
#[cfg(target_os = "linux")]
window_m.show_context_menu_for_gtk_window(window2.gtk_window(), x, y);
#[cfg(target_os = "macos")]
menu_bar.show_context_menu_for_nsview(window2.ns_view() as _, x, y);
} }
} }
Event::MainEventsCleared => { Event::MainEventsCleared => {
@ -180,9 +175,18 @@ fn main() {
if let Ok(event) = menu_channel.try_recv() { if let Ok(event) = menu_channel.try_recv() {
if event.id == custom_i_1.id() { if event.id == custom_i_1.id() {
file_m.insert(&MenuItem::new("New Menu Item", false, None), 2); file_m.insert(&MenuItem::new("New Menu Item", true, None), 2);
} }
println!("{:?}", event); println!("{:?}", event);
} }
}) })
} }
fn show_context_menu(window: &Window, menu: &dyn ContextMenu, x: f64, y: f64) {
#[cfg(target_os = "windows")]
menu.show_context_menu_for_hwnd(window.hwnd() as _, x, y);
#[cfg(target_os = "linux")]
menu.show_context_menu_for_gtk_window(window.gtk_window(), x, y);
#[cfg(target_os = "macos")]
menu.show_context_menu_for_nsview(window.ns_view() as _, x, y);
}

View file

@ -11,7 +11,7 @@ use winit::platform::windows::{EventLoopBuilderExtWindows, WindowExtWindows};
use winit::{ use winit::{
event::{ElementState, Event, MouseButton, WindowEvent}, event::{ElementState, Event, MouseButton, WindowEvent},
event_loop::{ControlFlow, EventLoopBuilder}, event_loop::{ControlFlow, EventLoopBuilder},
window::WindowBuilder, window::{Window, WindowBuilder},
}; };
fn main() { fn main() {
@ -143,7 +143,7 @@ fn main() {
window_id, window_id,
.. ..
} => { } => {
if window_id == window.id() { if window_id == window2.id() {
x = position.x; x = position.x;
y = position.y; y = position.y;
} }
@ -159,10 +159,7 @@ fn main() {
.. ..
} => { } => {
if window_id == window2.id() { if window_id == window2.id() {
#[cfg(target_os = "windows")] show_context_menu(&window2, &window_m, x, y);
window_m.show_context_menu_for_hwnd(window2.hwnd(), x, y);
#[cfg(target_os = "macos")]
menu_bar.show_context_menu_for_nsview(window2.ns_view() as _, x, y);
} }
} }
Event::MainEventsCleared => { Event::MainEventsCleared => {
@ -179,3 +176,10 @@ fn main() {
} }
}) })
} }
fn show_context_menu(window: &Window, menu: &dyn ContextMenu, x: f64, y: f64) {
#[cfg(target_os = "windows")]
menu.show_context_menu_for_hwnd(window.hwnd() as _, x, y);
#[cfg(target_os = "macos")]
menu.show_context_menu_for_nsview(window.ns_view() as _, x, y);
}

View file

@ -12,6 +12,12 @@ pub enum Error {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
#[error("This menu has not been initialized for this gtk window`")] #[error("This menu has not been initialized for this gtk window`")]
NotInitialized, NotInitialized,
#[cfg(windows)]
#[error("This menu has already been initialized for this hwnd`")]
AlreadyInitialized,
#[cfg(target_os = "linux")]
#[error("This menu has already been initialized for this gtk window`")]
AlreadyInitialized,
#[error("{0}")] #[error("{0}")]
AcceleratorParseError(String), AcceleratorParseError(String),
#[error("Cannot map {0} to gdk key")] #[error("Cannot map {0} to gdk key")]

View file

@ -126,8 +126,8 @@ pub use submenu::Submenu;
pub enum MenuItemType { pub enum MenuItemType {
Submenu, Submenu,
Normal, Normal,
Check,
Predefined, Predefined,
Check,
} }
impl Default for MenuItemType { impl Default for MenuItemType {

View file

@ -1,4 +1,4 @@
use crate::{ContextMenu, MenuItemExt}; use crate::{util::AddOp, ContextMenu, MenuItemExt};
/// A root menu that can be added to a Window on Windows and Linux /// A root menu that can be added to a Window on Windows and Linux
/// and used as the app global menu on macOS. /// and used as the app global menu on macOS.
@ -11,48 +11,6 @@ impl Default for Menu {
} }
} }
impl ContextMenu for Menu {
#[cfg(target_os = "windows")]
fn hpopupmenu(&self) -> windows_sys::Win32::UI::WindowsAndMessaging::HMENU {
self.0.hpopupmenu()
}
#[cfg(target_os = "windows")]
fn show_context_menu_for_hwnd(&self, hwnd: isize, x: f64, y: f64) {
self.0.show_context_menu_for_hwnd(hwnd, x, y)
}
#[cfg(target_os = "windows")]
fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
self.0.attach_menu_subclass_for_hwnd(hwnd)
}
#[cfg(target_os = "windows")]
fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) {
self.0.detach_menu_subclass_from_hwnd(hwnd)
}
#[cfg(target_os = "linux")]
fn show_context_menu_for_gtk_window(&self, w: &gtk::ApplicationWindow, x: f64, y: f64) {
self.0.show_context_menu_for_gtk_window(w, x, y)
}
#[cfg(target_os = "linux")]
fn gtk_context_menu(&self) -> gtk::Menu {
self.0.gtk_context_menu()
}
#[cfg(target_os = "macos")]
fn show_context_menu_for_nsview(&self, view: cocoa::base::id, x: f64, y: f64) {
self.0.show_context_menu_for_nsview(view, x, y)
}
#[cfg(target_os = "macos")]
fn ns_menu(&self) -> *mut std::ffi::c_void {
self.0.ns_menu()
}
}
impl Menu { impl Menu {
/// Creates a new menu. /// Creates a new menu.
pub fn new() -> Self { pub fn new() -> Self {
@ -74,7 +32,7 @@ impl Menu {
/// ///
/// [`Submenu`]: crate::Submenu /// [`Submenu`]: crate::Submenu
pub fn append(&self, item: &dyn MenuItemExt) { pub fn append(&self, item: &dyn MenuItemExt) {
self.0.append(item) self.0.add_menu_item(item, AddOp::Append)
} }
/// Add menu items to the end of this menu. It calls [`Menu::append`] in a loop internally. /// Add menu items to the end of this menu. It calls [`Menu::append`] in a loop internally.
@ -98,7 +56,7 @@ impl Menu {
/// ///
/// [`Submenu`]: crate::Submenu /// [`Submenu`]: crate::Submenu
pub fn prepend(&self, item: &dyn MenuItemExt) { pub fn prepend(&self, item: &dyn MenuItemExt) {
self.0.prepend(item) self.0.add_menu_item(item, AddOp::Insert(0))
} }
/// Add menu items to the beginning of this menu. It calls [`Menu::insert_items`] with position of `0` internally. /// Add menu items to the beginning of this menu. It calls [`Menu::insert_items`] with position of `0` internally.
@ -120,7 +78,7 @@ impl Menu {
/// ///
/// [`Submenu`]: crate::Submenu /// [`Submenu`]: crate::Submenu
pub fn insert(&self, item: &dyn MenuItemExt, position: usize) { pub fn insert(&self, item: &dyn MenuItemExt, position: usize) {
self.0.insert(item, position) self.0.add_menu_item(item, AddOp::Insert(position))
} }
/// Insert menu items at the specified `postion` in the menu. /// Insert menu items at the specified `postion` in the menu.
@ -160,7 +118,7 @@ impl Menu {
/// ///
/// Panics if the gtk event loop hasn't been initialized on the thread. /// Panics if the gtk event loop hasn't been initialized on the thread.
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn init_for_gtk_window<W>(&self, w: &W) -> std::rc::Rc<gtk::Box> pub fn init_for_gtk_window<W>(&self, w: &W) -> crate::Result<gtk::Box>
where where
W: gtk::prelude::IsA<gtk::ApplicationWindow>, W: gtk::prelude::IsA<gtk::ApplicationWindow>,
W: gtk::prelude::IsA<gtk::Container>, W: gtk::prelude::IsA<gtk::Container>,
@ -194,7 +152,7 @@ impl Menu {
/// } /// }
/// ``` /// ```
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn init_for_hwnd(&self, hwnd: isize) { pub fn init_for_hwnd(&self, hwnd: isize) -> crate::Result<()> {
self.0.init_for_hwnd(hwnd) self.0.init_for_hwnd(hwnd)
} }
@ -264,3 +222,45 @@ impl Menu {
self.0.remove_for_nsapp() self.0.remove_for_nsapp()
} }
} }
impl ContextMenu for Menu {
#[cfg(target_os = "windows")]
fn hpopupmenu(&self) -> windows_sys::Win32::UI::WindowsAndMessaging::HMENU {
self.0.hpopupmenu()
}
#[cfg(target_os = "windows")]
fn show_context_menu_for_hwnd(&self, hwnd: isize, x: f64, y: f64) {
self.0.show_context_menu_for_hwnd(hwnd, x, y)
}
#[cfg(target_os = "windows")]
fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
self.0.attach_menu_subclass_for_hwnd(hwnd)
}
#[cfg(target_os = "windows")]
fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) {
self.0.detach_menu_subclass_from_hwnd(hwnd)
}
#[cfg(target_os = "linux")]
fn show_context_menu_for_gtk_window(&self, w: &gtk::ApplicationWindow, x: f64, y: f64) {
self.0.show_context_menu_for_gtk_window(w, x, y)
}
#[cfg(target_os = "linux")]
fn gtk_context_menu(&self) -> gtk::Menu {
self.0.gtk_context_menu()
}
#[cfg(target_os = "macos")]
fn show_context_menu_for_nsview(&self, view: cocoa::base::id, x: f64, y: f64) {
self.0.show_context_menu_for_nsview(view, x, y)
}
#[cfg(target_os = "macos")]
fn ns_menu(&self) -> *mut std::ffi::c_void {
self.0.ns_menu()
}
}

File diff suppressed because it is too large Load diff

View file

@ -147,19 +147,7 @@ impl Menu {
} }
} }
pub fn append(&self, item: &dyn crate::MenuItemExt) { pub fn add_menu_item(&self, item: &dyn crate::MenuItemExt, op: AddOp) {
self.add_menu_item(item, AddOp::Append)
}
pub fn prepend(&self, item: &dyn crate::MenuItemExt) {
self.add_menu_item(item, AddOp::Insert(0))
}
pub fn insert(&self, item: &dyn crate::MenuItemExt, position: usize) {
self.add_menu_item(item, AddOp::Insert(position))
}
fn add_menu_item(&self, item: &dyn crate::MenuItemExt, op: AddOp) {
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(self.id); let ns_menu_item: *mut Object = item.make_ns_item_for_menu(self.id);
let child: Rc<RefCell<MenuChild>> = item.get_child(); let child: Rc<RefCell<MenuChild>> = item.get_child();
@ -333,19 +321,7 @@ impl Submenu {
ns_menu_item ns_menu_item
} }
pub fn append(&self, item: &dyn crate::MenuItemExt) { pub fn add_menu_item(&self, item: &dyn crate::MenuItemExt, op: AddOp) {
self.add_menu_item(item, AddOp::Append)
}
pub fn prepend(&self, item: &dyn crate::MenuItemExt) {
self.add_menu_item(item, AddOp::Insert(0))
}
pub fn insert(&self, item: &dyn crate::MenuItemExt, position: usize) {
self.add_menu_item(item, AddOp::Insert(position))
}
fn add_menu_item(&self, item: &dyn crate::MenuItemExt, op: AddOp) {
let mut self_ = self.0.borrow_mut(); let mut self_ = self.0.borrow_mut();
let item_child: Rc<RefCell<MenuChild>> = item.get_child(); let item_child: Rc<RefCell<MenuChild>> = item.get_child();

View file

@ -182,19 +182,7 @@ impl Menu {
} }
} }
pub fn append(&self, item: &dyn crate::MenuItemExt) { pub fn add_menu_item(&self, item: &dyn crate::MenuItemExt, op: AddOp) {
self.add_menu_item(item, AddOp::Append)
}
pub fn prepend(&self, item: &dyn crate::MenuItemExt) {
self.add_menu_item(item, AddOp::Insert(0))
}
pub fn insert(&self, item: &dyn crate::MenuItemExt, position: usize) {
self.add_menu_item(item, AddOp::Insert(position))
}
fn add_menu_item(&self, item: &dyn crate::MenuItemExt, op: AddOp) {
let mut flags = 0; let mut flags = 0;
let child = match item.type_() { let child = match item.type_() {
MenuItemType::Submenu => { MenuItemType::Submenu => {
@ -422,7 +410,11 @@ impl Menu {
self.hpopupmenu self.hpopupmenu
} }
pub fn init_for_hwnd(&self, hwnd: isize) { pub fn init_for_hwnd(&self, hwnd: isize) -> crate::Result<()> {
if self.hwnds.borrow().iter().any(|h| *h == hwnd) {
return Err(crate::Error::AlreadyInitialized);
}
self.hwnds.borrow_mut().push(hwnd); self.hwnds.borrow_mut().push(hwnd);
unsafe { unsafe {
SetMenu(hwnd, self.hmenu); SetMenu(hwnd, self.hmenu);
@ -434,6 +426,8 @@ impl Menu {
); );
DrawMenuBar(hwnd); DrawMenuBar(hwnd);
}; };
Ok(())
} }
pub fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) { pub fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
@ -528,19 +522,7 @@ impl Submenu {
self.0.borrow().hpopupmenu self.0.borrow().hpopupmenu
} }
pub fn append(&self, item: &dyn crate::MenuItemExt) { pub fn add_menu_item(&self, item: &dyn crate::MenuItemExt, op: AddOp) {
self.add_menu_item(item, AddOp::Append)
}
pub fn prepend(&self, item: &dyn crate::MenuItemExt) {
self.add_menu_item(item, AddOp::Insert(0))
}
pub fn insert(&self, item: &dyn crate::MenuItemExt, position: usize) {
self.add_menu_item(item, AddOp::Insert(position))
}
fn add_menu_item(&self, item: &dyn crate::MenuItemExt, op: AddOp) {
let mut flags = 0; let mut flags = 0;
let child = match item.type_() { let child = match item.type_() {
MenuItemType::Submenu => { MenuItemType::Submenu => {

View file

@ -1,4 +1,4 @@
use crate::{ContextMenu, MenuItemExt, MenuItemType}; use crate::{util::AddOp, ContextMenu, MenuItemExt, MenuItemType};
/// A menu that can be added to a [`Menu`] or another [`Submenu`]. /// A menu that can be added to a [`Menu`] or another [`Submenu`].
/// ///
@ -19,48 +19,6 @@ unsafe impl MenuItemExt for Submenu {
} }
} }
impl ContextMenu for Submenu {
#[cfg(target_os = "windows")]
fn hpopupmenu(&self) -> windows_sys::Win32::UI::WindowsAndMessaging::HMENU {
self.0.hpopupmenu()
}
#[cfg(target_os = "windows")]
fn show_context_menu_for_hwnd(&self, hwnd: isize, x: f64, y: f64) {
self.0.show_context_menu_for_hwnd(hwnd, x, y)
}
#[cfg(target_os = "windows")]
fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
self.0.attach_menu_subclass_for_hwnd(hwnd)
}
#[cfg(target_os = "windows")]
fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) {
self.0.detach_menu_subclass_from_hwnd(hwnd)
}
#[cfg(target_os = "linux")]
fn show_context_menu_for_gtk_window(&self, w: &gtk::ApplicationWindow, x: f64, y: f64) {
self.0.show_context_menu_for_gtk_window(w, x, y)
}
#[cfg(target_os = "linux")]
fn gtk_context_menu(&self) -> gtk::Menu {
self.0.gtk_context_menu()
}
#[cfg(target_os = "macos")]
fn show_context_menu_for_nsview(&self, view: cocoa::base::id, x: f64, y: f64) {
self.0.show_context_menu_for_nsview(view, x, y)
}
#[cfg(target_os = "macos")]
fn ns_menu(&self) -> *mut std::ffi::c_void {
self.0.ns_menu()
}
}
impl Submenu { impl Submenu {
/// Create a new submenu. /// Create a new submenu.
/// ///
@ -84,7 +42,7 @@ impl Submenu {
/// Add a menu item to the end of this menu. /// Add a menu item to the end of this menu.
pub fn append(&self, item: &dyn MenuItemExt) { pub fn append(&self, item: &dyn MenuItemExt) {
self.0.append(item) self.0.add_menu_item(item, AddOp::Append)
} }
/// Add menu items to the end of this submenu. It calls [`Submenu::append`] in a loop. /// Add menu items to the end of this submenu. It calls [`Submenu::append`] in a loop.
@ -96,7 +54,7 @@ impl Submenu {
/// Add a menu item to the beginning of this submenu. /// Add a menu item to the beginning of this submenu.
pub fn prepend(&self, item: &dyn MenuItemExt) { pub fn prepend(&self, item: &dyn MenuItemExt) {
self.0.prepend(item) self.0.add_menu_item(item, AddOp::Insert(0))
} }
/// Add menu items to the beginning of this submenu. /// Add menu items to the beginning of this submenu.
@ -109,7 +67,7 @@ impl Submenu {
/// Insert a menu item at the specified `postion` in the submenu. /// Insert a menu item at the specified `postion` in the submenu.
pub fn insert(&self, item: &dyn MenuItemExt, position: usize) { pub fn insert(&self, item: &dyn MenuItemExt, position: usize) {
self.0.insert(item, position) self.0.add_menu_item(item, AddOp::Insert(position))
} }
/// Insert menu items at the specified `postion` in the submenu. /// Insert menu items at the specified `postion` in the submenu.
@ -171,3 +129,45 @@ impl Submenu {
self.0.set_help_menu_for_nsapp() self.0.set_help_menu_for_nsapp()
} }
} }
impl ContextMenu for Submenu {
#[cfg(target_os = "windows")]
fn hpopupmenu(&self) -> windows_sys::Win32::UI::WindowsAndMessaging::HMENU {
self.0.hpopupmenu()
}
#[cfg(target_os = "windows")]
fn show_context_menu_for_hwnd(&self, hwnd: isize, x: f64, y: f64) {
self.0.show_context_menu_for_hwnd(hwnd, x, y)
}
#[cfg(target_os = "windows")]
fn attach_menu_subclass_for_hwnd(&self, hwnd: isize) {
self.0.attach_menu_subclass_for_hwnd(hwnd)
}
#[cfg(target_os = "windows")]
fn detach_menu_subclass_from_hwnd(&self, hwnd: isize) {
self.0.detach_menu_subclass_from_hwnd(hwnd)
}
#[cfg(target_os = "linux")]
fn show_context_menu_for_gtk_window(&self, w: &gtk::ApplicationWindow, x: f64, y: f64) {
self.0.show_context_menu_for_gtk_window(w, x, y)
}
#[cfg(target_os = "linux")]
fn gtk_context_menu(&self) -> gtk::Menu {
self.0.gtk_context_menu()
}
#[cfg(target_os = "macos")]
fn show_context_menu_for_nsview(&self, view: cocoa::base::id, x: f64, y: f64) {
self.0.show_context_menu_for_nsview(view, x, y)
}
#[cfg(target_os = "macos")]
fn ns_menu(&self) -> *mut std::ffi::c_void {
self.0.ns_menu()
}
}