mirror of
https://github.com/italicsjenga/muda.git
synced 2024-12-23 20:11:29 +11:00
implement windows
This commit is contained in:
parent
bac4dbd1ab
commit
832e4964e7
|
@ -13,6 +13,7 @@ features = [
|
|||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_Foundation",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_UI_Shell"
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
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,
|
||||
encode_wide(entry.title()).as_mut_ptr(),
|
||||
);
|
||||
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)
|
||||
}
|
||||
|
|
19
src/util.rs
19
src/util.rs
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue