implement windows

This commit is contained in:
amrbashir 2022-05-06 18:38:39 +02:00
parent bac4dbd1ab
commit 832e4964e7
No known key found for this signature in database
GPG key ID: BBD7A47A2003FF33
7 changed files with 180 additions and 100 deletions

View file

@ -13,6 +13,7 @@ features = [
"Win32_UI_WindowsAndMessaging",
"Win32_Foundation",
"Win32_Graphics_Gdi",
"Win32_UI_Shell"
]
[target.'cfg(target_os = "linux")'.dependencies]

View file

@ -40,7 +40,7 @@ fn main() {
let menu_channel = menu_event_receiver();
let mut open_item_disabled = false;
let counter = 0;
let mut counter = 0;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
@ -49,8 +49,9 @@ fn main() {
match event.id {
_ if event.id == save_item.id() => {
println!("Save menu item triggered");
counter += 1;
save_item.set_label(format!("Save triggered {counter} times"));
if !open_item_disabled {
println!("Open item disabled!");
open_item.set_enabled(false);

View file

@ -11,7 +11,7 @@ fn main() {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
let window2 = WindowBuilder::new().build(&event_loop).unwrap();
let _window2 = WindowBuilder::new().build(&event_loop).unwrap();
let mut menu_bar = Menu::new();
let mut file_menu = menu_bar.add_submenu("File", true);
@ -28,12 +28,12 @@ fn main() {
#[cfg(target_os = "windows")]
{
menu_bar.init_for_hwnd(window.hwnd() as _);
menu_bar.init_for_hwnd(window2.hwnd() as _);
menu_bar.init_for_hwnd(_window2.hwnd() as _);
}
let menu_channel = menu_event_receiver();
let mut open_item_disabled = false;
let counter = 0;
let mut counter = 0;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
@ -42,8 +42,9 @@ fn main() {
match event.id {
_ if event.id == save_item.id() => {
println!("Save menu item triggered");
counter += 1;
save_item.set_label(format!("Save triggered {counter} times"));
if !open_item_disabled {
println!("Open item disabled!");
open_item.set_enabled(false);

View file

@ -34,6 +34,11 @@ impl Menu {
{
self.0.init_for_gtk_window(w)
}
#[cfg(target_os = "windows")]
pub fn init_for_hwnd(&self, hwnd: isize) {
self.0.init_for_hwnd(hwnd)
}
}
#[derive(Clone)]

View file

@ -1,9 +1,11 @@
#![cfg(target_os = "linux")]
use crate::util::Counter;
use gtk::{prelude::*, Orientation};
use parking_lot::Mutex;
use std::sync::Arc;
const COUNTER: Counter = Counter::new();
static COUNTER: Counter = Counter::new();
enum MenuEntryType {
Submenu,

View file

@ -1,137 +1,196 @@
use windows_sys::Win32::UI::WindowsAndMessaging::{
AppendMenuW, CreateMenu, EnableMenuItem, SetMenu, SetMenuItemInfoW, HMENU, MENUITEMINFOW,
MF_BYCOMMAND, MF_DISABLED, MF_ENABLED, MF_GRAYED, MF_POPUP, MF_STRING, MIIM_STRING,
#![cfg(target_os = "windows")]
use crate::util::{encode_wide, Counter, LOWORD};
use windows_sys::Win32::{
Foundation::{HWND, LPARAM, LRESULT, WPARAM},
UI::{
Shell::{DefSubclassProc, SetWindowSubclass},
WindowsAndMessaging::{
AppendMenuW, CreateMenu, EnableMenuItem, SetMenu, SetMenuItemInfoW, MENUITEMINFOW,
MF_DISABLED, MF_ENABLED, MF_GRAYED, MF_POPUP, MIIM_STRING, WM_COMMAND,
},
},
};
use crate::{util::encode_wide, MenuEntry, IDS_COUNTER};
static COUNTER: Counter = Counter::new();
pub struct MenuBar(HMENU);
pub struct Menu(isize);
impl MenuBar {
impl Menu {
pub fn new() -> Self {
Self(unsafe { CreateMenu() })
}
pub fn add_entry<M: MenuEntry>(&mut self, entry: &mut M) {
let mut flags = 0;
let id;
if entry.is_menu() {
flags |= MF_POPUP;
let menu = entry.platform_menu().unwrap();
id = menu.hmenu as _;
menu.parent = self.0;
} else {
flags |= MF_STRING;
let item = entry.platform_item().unwrap();
id = item.id as _;
item.parent = self.0;
};
if !entry.enabled() {
pub fn add_submenu(&mut self, label: impl AsRef<str>, enabled: bool) -> Submenu {
let hmenu = unsafe { CreateMenu() };
let mut flags = MF_POPUP;
if !enabled {
flags |= MF_GRAYED;
}
unsafe {
AppendMenuW(self.0, flags, id, encode_wide(entry.title()).as_mut_ptr());
AppendMenuW(
self.0,
flags,
hmenu as _,
encode_wide(label.as_ref()).as_ptr(),
)
};
Submenu {
label: label.as_ref().to_string(),
enabled,
hmenu,
parent_hmenu: self.0,
}
}
pub fn init_for_hwnd(&self, hwnd: isize) {
unsafe { SetMenu(hwnd, self.0) };
unsafe {
SetMenu(hwnd, self.0);
SetWindowSubclass(hwnd, Some(menu_subclass_proc), 22, 0);
};
}
}
pub struct Menu {
hmenu: HMENU,
parent: HMENU,
#[derive(Clone)]
pub struct Submenu {
label: String,
enabled: bool,
hmenu: isize,
parent_hmenu: isize,
}
impl Menu {
pub fn new(_title: impl Into<String>) -> Self {
Self {
hmenu: unsafe { CreateMenu() },
parent: 0,
}
impl Submenu {
pub fn label(&self) -> String {
self.label.clone()
}
pub fn id(&self) -> u64 {
self.hmenu as u64
pub fn set_label(&mut self, label: impl AsRef<str>) {
self.label = label.as_ref().to_string();
let mut info: MENUITEMINFOW = unsafe { std::mem::zeroed() };
info.cbSize = std::mem::size_of::<MENUITEMINFOW>() as _;
info.fMask = MIIM_STRING;
info.dwTypeData = encode_wide(&self.label).as_mut_ptr();
unsafe { SetMenuItemInfoW(self.parent_hmenu, self.hmenu as u32, false.into(), &info) };
}
pub fn set_title(&mut self, title: impl Into<String>) {
let mut item_info: MENUITEMINFOW = unsafe { std::mem::zeroed() };
item_info.cbSize = std::mem::size_of::<MENUITEMINFOW>() as _;
item_info.fMask = MIIM_STRING;
item_info.dwTypeData = encode_wide(title.into()).as_mut_ptr();
unsafe { SetMenuItemInfoW(self.parent, self.hmenu as u32, false.into(), &item_info) };
pub fn enabled(&self) -> bool {
self.enabled
}
pub fn set_enabled(&mut self, enabled: bool) {
let enabled = if enabled { MF_ENABLED } else { MF_DISABLED };
unsafe { EnableMenuItem(self.parent, self.hmenu as u32, MF_BYCOMMAND | enabled) };
self.enabled = enabled;
unsafe {
EnableMenuItem(
self.parent_hmenu,
self.hmenu as _,
if enabled { MF_ENABLED } else { MF_DISABLED },
)
};
}
pub fn add_entry<M: MenuEntry>(&mut self, entry: &mut M) {
let mut flags = 0;
let id;
if entry.is_menu() {
flags |= MF_POPUP;
let menu = entry.platform_menu().unwrap();
id = menu.hmenu as _;
menu.parent = self.hmenu;
} else {
flags |= MF_STRING;
let item = entry.platform_item().unwrap();
id = item.id as _;
item.parent = self.hmenu;
};
if !entry.enabled() {
pub fn add_submenu(&mut self, label: impl AsRef<str>, enabled: bool) -> Submenu {
let hmenu = unsafe { CreateMenu() };
let mut flags = MF_POPUP;
if !enabled {
flags |= MF_GRAYED;
}
unsafe {
AppendMenuW(
self.hmenu,
flags,
id,
encode_wide(entry.title()).as_mut_ptr(),
);
hmenu as _,
encode_wide(label.as_ref()).as_ptr(),
)
};
Submenu {
label: label.as_ref().to_string(),
enabled,
hmenu,
parent_hmenu: self.hmenu,
}
}
pub fn add_text_item(&mut self, label: impl AsRef<str>, enabled: bool) -> TextMenuItem {
let id = COUNTER.next();
let mut flags = MF_POPUP;
if !enabled {
flags |= MF_GRAYED;
}
unsafe {
AppendMenuW(
self.hmenu,
flags,
id as _,
encode_wide(label.as_ref()).as_ptr(),
)
};
TextMenuItem {
label: label.as_ref().to_string(),
enabled,
id,
parent_hmenu: self.hmenu,
}
}
}
pub struct MenuItem {
#[derive(Clone)]
pub struct TextMenuItem {
label: String,
enabled: bool,
id: u64,
parent: HMENU,
parent_hmenu: isize,
}
impl MenuItem {
pub fn new(_title: impl Into<String>) -> Self {
Self {
id: IDS_COUNTER.next(),
parent: 0,
}
impl TextMenuItem {
pub fn label(&self) -> String {
self.label.clone()
}
pub fn set_label(&mut self, label: impl AsRef<str>) {
self.label = label.as_ref().to_string();
let mut info: MENUITEMINFOW = unsafe { std::mem::zeroed() };
info.cbSize = std::mem::size_of::<MENUITEMINFOW>() as _;
info.fMask = MIIM_STRING;
info.dwTypeData = encode_wide(&self.label).as_mut_ptr();
unsafe { SetMenuItemInfoW(self.parent_hmenu, self.id as u32, false.into(), &info) };
}
pub fn enabled(&self) -> bool {
self.enabled
}
pub fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
unsafe {
EnableMenuItem(
self.parent_hmenu,
self.id as _,
if enabled { MF_ENABLED } else { MF_DISABLED },
)
};
}
pub fn id(&self) -> u64 {
self.id
}
pub fn set_title(&self, title: impl Into<String>) {
let mut item_info: MENUITEMINFOW = unsafe { std::mem::zeroed() };
item_info.cbSize = std::mem::size_of::<MENUITEMINFOW>() as _;
item_info.fMask = MIIM_STRING;
item_info.dwTypeData = encode_wide(title.into()).as_mut_ptr();
unsafe { SetMenuItemInfoW(self.parent, self.id as u32, false.into(), &item_info) };
}
pub fn set_enabled(&self, enabled: bool) {
let enabled = if enabled { MF_ENABLED } else { MF_DISABLED };
unsafe { EnableMenuItem(self.parent, self.id as u32, MF_BYCOMMAND | enabled) };
}
}
unsafe extern "system" fn menu_subclass_proc(
hwnd: HWND,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
_uidsubclass: usize,
_dwrefdata: usize,
) -> LRESULT {
let id = LOWORD(wparam as _);
if msg == WM_COMMAND && 0 < id && (id as u64) < COUNTER.current() {
let _ = crate::MENU_CHANNEL.0.send(crate::MenuEvent { id: id as _ });
};
DefSubclassProc(hwnd, msg, wparam, lparam)
}

View file

@ -1,14 +1,19 @@
use std::sync::atomic::{AtomicU64, Ordering};
pub(crate) struct Counter(AtomicU64);
pub struct Counter(AtomicU64);
impl Counter {
pub(crate) const fn new() -> Self {
pub const fn new() -> Self {
Self(AtomicU64::new(1))
}
pub(crate) fn next(&self) -> u64 {
self.0.fetch_add(1, Ordering::Release)
pub fn next(&self) -> u64 {
self.0.fetch_add(1, Ordering::Relaxed)
}
#[allow(unused)]
pub fn current(&self) -> u64 {
self.0.load(Ordering::Relaxed)
}
}
@ -18,3 +23,9 @@ pub fn encode_wide(string: impl AsRef<std::ffi::OsStr>) -> Vec<u16> {
.chain(std::iter::once(0))
.collect()
}
#[cfg(target_os = "windows")]
#[allow(non_snake_case)]
pub fn LOWORD(dword: u32) -> u16 {
(dword & 0xFFFF) as u16
}