use crate::error::UiResult;
use crate::map::Map;
use crate::window::event::WindowEvent;
use crate::window::ime::{IMEData, IME};
use crate::window::win32::handle::Win32WindowHandle;
use crate::window::win32::tray::Tray;
use crate::window::{WindowId, WindowKind, WindowType};
use crate::{Pos, Size, TrayMenu, WindowAttribute};
use std::sync::Arc;
use windows::core::PCWSTR;
use windows::Win32::Foundation::{HINSTANCE, POINT};
use windows::Win32::Graphics::Gdi::ValidateRect;
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::UI::Input::Ime::{ImmGetCompositionStringW, ImmGetContext, ImmReleaseContext, GCS_COMPSTR, GCS_RESULTSTR};
use windows::Win32::UI::Input::KeyboardAndMouse::*;
use windows::Win32::UI::Shell::{Shell_NotifyIconW, NIF_ICON, NIF_MESSAGE, NIF_TIP, NIM_ADD, NOTIFYICONDATAW};
use windows::Win32::UI::WindowsAndMessaging::*;
use crate::key::Key;
use crate::window::win32::clipboard::Win32Clipboard;
pub mod tray;
pub(crate) mod handle;
pub(crate) mod until;
mod clipboard;
const TRAY_ICON: u32 = WM_USER + 1;
const REQ_UPDATE: u32 = WM_USER + 2;
const CREATE_CHILD: u32 = WM_USER + 3;
const RE_INIT: u32 = WM_USER + 4;
const IME: u32 = WM_USER + 5;
const REQ_CLOSE: u32 = WM_USER + 6;
const USER_UPDATE: u32 = WM_USER + 7;
const RESIZE: u32 = WM_USER + 8;
pub struct Win32Window {
tray: Option<Tray>,
handles: Map<WindowId, Arc<WindowType>>,
}
impl Win32Window {
pub fn new(attr: &mut WindowAttribute, ime: Arc<IME>) -> UiResult<Win32Window> {
let handle = Win32Window::create_window(attr)?;
let window_type = WindowType {
kind: WindowKind::Win32(handle),
id: WindowId::unique_id(),
type_: WindowType::ROOT,
ime,
};
let mut handles = Map::new();
handles.insert(window_type.id, Arc::new(window_type));
let window = Win32Window {
tray: attr.tray.take(),
handles,
};
window.show_tray()?;
Ok(window)
}
pub fn last_window(&self) -> Arc<WindowType> {
self.handles.last().unwrap().clone()
}
pub fn show_tray(&self) -> UiResult<()> {
println!("show tray-{}", self.tray.is_some());
if let Some(ref tray) = self.tray {
let h_icon = match tray.icon {
None => unsafe { LoadIconW(None, IDI_APPLICATION)? }
Some(ref ip) => unsafe { until::load_tray_icon(ip) },
};
let mut tip = [0; 128];
let tip_s = until::to_wstr(&tray.hovered_text);
tip[..tip_s.len()].copy_from_slice(tip_s.as_ref());
let mut nid = NOTIFYICONDATAW {
cbSize: size_of::<NOTIFYICONDATAW>() as u32,
hWnd: self.handles[0].win32().hwnd,
uID: 1,
uFlags: NIF_MESSAGE | NIF_ICON | NIF_TIP,
uCallbackMessage: TRAY_ICON,
hIcon: h_icon,
szTip: tip,
..Default::default()
};
unsafe { Shell_NotifyIconW(NIM_ADD, &mut nid).ok()?; }
}
Ok(())
}
fn create_window(attr: &WindowAttribute) -> UiResult<Win32WindowHandle> {
let hinstance = unsafe { GetModuleHandleW(None) }?;
let class_name = until::to_wstr(&(attr.title.clone()));
let wc = WNDCLASSW {
lpfnWndProc: Some(until::wndproc),
hInstance: HINSTANCE::from(hinstance),
lpszClassName: PCWSTR(class_name.as_ptr()),
hCursor: unsafe { LoadCursorW(None, IDC_ARROW)? },
..Default::default()
};
unsafe { RegisterClassW(&wc); }
let hwnd = unsafe {
CreateWindowExW(
Default::default(),
PCWSTR(class_name.as_ptr()),
PCWSTR(until::to_wstr(&attr.title).as_ptr()),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
attr.position[0], attr.position[1],
attr.inner_size.width as i32, attr.inner_size.height as i32,
None,
None,
Some(HINSTANCE::from(hinstance)),
None,
)
}?;
Ok(Win32WindowHandle { hwnd, clipboard: Win32Clipboard })
}
pub fn create_child_window(&mut self, parent: &Arc<WindowType>, attr: &WindowAttribute) -> UiResult<Arc<WindowType>> {
let handle = Win32Window::create_window(attr)?;
let window_type = Arc::new(WindowType {
kind: WindowKind::Win32(handle),
id: WindowId::unique_id(),
type_: WindowType::CHILD,
ime: parent.ime.clone(),
});
self.handles.insert(window_type.id, window_type.clone());
Ok(window_type)
}
pub fn run(&mut self) -> (WindowId, WindowEvent) {
unsafe {
let mut msg = std::mem::zeroed::<MSG>();
let ret = GetMessageW(&mut msg, None, 0, 0);
if ret.0 == 0 { return (self.handles[0].id, WindowEvent::ReqClose); }
let window = self.handles.iter().find(|x| x.win32().hwnd == msg.hwnd);
if window.is_none() {
let _ = TranslateMessage(&msg);
DispatchMessageW(&msg);
return (WindowId(0), WindowEvent::None);
}
let window = window.unwrap();
match msg.message {
RESIZE => {
let width = until::loword(msg.lParam.0 as u32) as u32;
let height = until::hiword(msg.lParam.0 as u32) as u32;
println!("resize-{}-{}", width, height);
(window.id, WindowEvent::Resize(Size { width, height }))
}
WM_PAINT => {
println!("paint");
ValidateRect(Option::from(window.win32().hwnd), None).unwrap();
(window.id, WindowEvent::Redraw)
}
WM_KEYDOWN => {
let ctrl_pressed = (GetKeyState(VK_CONTROL.0 as i32) as u16 & 0x8000) != 0;
if ctrl_pressed && msg.wParam.0 == 'C' as usize {
(window.id, WindowEvent::KeyPress(Key::CtrlC))
} else if ctrl_pressed && msg.wParam.0 == 'V' as usize {
(window.id, WindowEvent::KeyPress(Key::CtrlV))
} else {
match VIRTUAL_KEY(msg.wParam.0 as u16) {
VK_HOME => (window.id, WindowEvent::KeyPress(Key::Home)),
VK_END => (window.id, WindowEvent::KeyPress(Key::End)),
VK_RETURN => (window.id, WindowEvent::KeyPress(Key::Enter)),
VK_LEFT => (window.id, WindowEvent::KeyPress(Key::LeftArrow)),
VK_UP => (window.id, WindowEvent::KeyPress(Key::UpArrow)),
VK_DOWN => (window.id, WindowEvent::KeyPress(Key::DownArrow)),
VK_RIGHT => (window.id, WindowEvent::KeyPress(Key::RightArrow)),
VK_DELETE => (window.id, WindowEvent::KeyPress(Key::Delete)),
VK_BACK => (window.id, WindowEvent::KeyPress(Key::Backspace)),
_ => {
let _ = TranslateMessage(&msg);
DispatchMessageW(&msg);
(window.id, WindowEvent::KeyPress(Key::Unknown))
}
}
}
}
WM_KEYUP => {
match VIRTUAL_KEY(msg.wParam.0 as u16) {
VK_HOME => (window.id, WindowEvent::KeyRelease(Key::Home)),
VK_END => (window.id, WindowEvent::KeyRelease(Key::End)),
VK_RETURN => (window.id, WindowEvent::KeyRelease(Key::Enter)),
VK_LEFT => (window.id, WindowEvent::KeyRelease(Key::LeftArrow)),
VK_UP => (window.id, WindowEvent::KeyRelease(Key::UpArrow)),
VK_DOWN => (window.id, WindowEvent::KeyRelease(Key::DownArrow)),
VK_RIGHT => (window.id, WindowEvent::KeyRelease(Key::RightArrow)),
VK_DELETE => (window.id, WindowEvent::KeyRelease(Key::Delete)),
VK_BACK => (window.id, WindowEvent::KeyRelease(Key::Backspace)),
_ => (window.id, WindowEvent::None)
}
}
WM_CHAR => {
let ch = std::char::from_u32(msg.wParam.0 as u32).unwrap_or('\0');
println!("Char input: {:?}", ch);
match ch {
'\r' => (window.id, WindowEvent::None),
_ => (window.id, WindowEvent::KeyRelease(Key::Char(ch)))
}
}
WM_LBUTTONDOWN => {
let x = until::get_x_lparam(msg.lParam) as f32;
let y = until::get_y_lparam(msg.lParam) as f32;
(window.id, WindowEvent::MousePress(Pos { x, y }))
}
WM_LBUTTONUP => {
let x = until::get_x_lparam(msg.lParam) as f32;
let y = until::get_y_lparam(msg.lParam) as f32;
(window.id, WindowEvent::MouseRelease(Pos { x, y }))
}
WM_MOUSEMOVE => {
let x = until::get_x_lparam(msg.lParam) as f32;
let y = until::get_y_lparam(msg.lParam) as f32;
(window.id, WindowEvent::MouseMove(Pos { x, y }))
}
WM_MOUSEWHEEL => {
let delta = ((msg.wParam.0 >> 16) & 0xFFFF) as i16;
(window.id, WindowEvent::MouseWheel(delta as f32))
}
REQ_UPDATE => (window.id, WindowEvent::ReqUpdate),
CREATE_CHILD => (window.id, WindowEvent::CreateChild),
RE_INIT => {
println!("re_init");
(window.id, WindowEvent::ReInit)
}
IME => {
let himc = ImmGetContext(window.win32().hwnd);
println!("ime-----{}", msg.lParam.0);
if msg.lParam.0 == 7168 || msg.lParam.0 == 2048 {
let size = ImmGetCompositionStringW(himc, GCS_RESULTSTR, None, 0);
if size > 0 {
let len = size as usize / 2;
let mut buf: Vec<u16> = vec![0; len];
ImmGetCompositionStringW(himc, GCS_RESULTSTR, Some(buf.as_mut_ptr() as *mut _), size as u32);
let s = String::from_utf16_lossy(&buf);
window.ime().ime_commit(s.chars().collect());
println!("ime2: {}", s);
ImmReleaseContext(window.win32().hwnd, himc).unwrap();
return (window.id, WindowEvent::IME(IMEData::Commit(window.ime.ime_done())));
}
}
if msg.lParam.0 == 440 {
let size = ImmGetCompositionStringW(himc, GCS_COMPSTR, None, 0);
if size > 0 {
let len = (size as usize) / 2;
let mut buf: Vec<u16> = vec![0; len];
ImmGetCompositionStringW(himc, GCS_COMPSTR, Some(buf.as_mut_ptr() as *mut _), size as u32);
let s = String::from_utf16_lossy(&buf);
println!("ime1: {}", s);
window.ime().ime_draw(s.chars().collect());
ImmReleaseContext(window.win32().hwnd, himc).unwrap();
return (window.id, WindowEvent::IME(IMEData::Preedit(window.ime.chars())));
}
}
(window.id, WindowEvent::None)
}
REQ_CLOSE => {
let wid = window.id;
let window = self.handles.remove(&wid).unwrap();
(window.id, WindowEvent::ReqClose)
}
_ => {
let _ = TranslateMessage(&msg);
DispatchMessageW(&msg);
(window.id, WindowEvent::None)
}
}
}
}
fn add_tray_menu(&self, h_menu: HMENU, id: u32, menu: &TrayMenu, flag: MENU_ITEM_FLAGS) -> UiResult<()> {
unsafe {
AppendMenuW(h_menu, flag, id as usize, PCWSTR(until::to_wstr(&menu.label).as_ptr()))?;
if let Some(ref ip) = menu.icon {
let h_icon = until::load_tray_icon(ip);
let h_bitmap = until::icon_to_bitmap(h_icon, 16, 16)?; let mut mii = MENUITEMINFOW::default();
mii.cbSize = size_of::<MENUITEMINFOW>() as u32;
mii.fMask = MIIM_BITMAP;
mii.hbmpItem = h_bitmap; SetMenuItemInfoW(h_menu, id, false, &mii)?;
}
}
Ok(())
}
pub fn show_tray_menu(&self) -> UiResult<()> {
unsafe {
if let Some(ref tray) = self.tray {
let h_menu = CreatePopupMenu()?;
for menu in &tray.menus {
if menu.children.len() == 0 {
self.add_tray_menu(h_menu, menu.id, menu, MF_STRING)?;
} else {
let sub_menu = CreatePopupMenu()?;
for child in &menu.children {
self.add_tray_menu(sub_menu, child.id, child, MF_STRING)?;
}
self.add_tray_menu(h_menu, sub_menu.0 as u32, menu, MF_POPUP)?;
}
}
let mut pt = POINT::default();
GetCursorPos(&mut pt)?;
TrackPopupMenu(
h_menu,
TPM_RIGHTBUTTON,
pt.x,
pt.y,
Some(0),
self.handles[0].win32().hwnd,
None,
).ok()?;
println!("111111111111");
DestroyMenu(h_menu)?;
}
Ok(())
}
}
}