use crate::utility::adjust_window_rect;
use crate::*;
use tokio::sync::{mpsc, oneshot};
use windows::core::{HSTRING, PCWSTR};
use windows::Win32::{
Foundation::{HINSTANCE, HWND, LPARAM, POINT, RECT, WPARAM},
Graphics::Gdi::{
GetStockObject, MonitorFromPoint, RedrawWindow, HBRUSH, MONITOR_DEFAULTTOPRIMARY,
RDW_INVALIDATE, WHITE_BRUSH,
},
System::LibraryLoader::GetModuleHandleW,
UI::HiDpi::{GetDpiForMonitor, GetDpiForWindow, MDT_DEFAULT},
UI::Shell::DragAcceptFiles,
UI::WindowsAndMessaging::{
CreateWindowExW, LoadCursorW, PostMessageW, RegisterClassExW, ShowWindow, ShowWindowAsync,
CS_HREDRAW, CS_VREDRAW, ICON_BIG, ICON_SMALL, IDC_ARROW, SW_HIDE, SW_MINIMIZE, SW_RESTORE,
SW_SHOW, SW_SHOWMAXIMIZED, WINDOW_EX_STYLE, WINDOW_STYLE, WM_CLOSE, WM_SETICON,
WNDCLASSEXW, WS_CAPTION, WS_CHILD, WS_EX_NOREDIRECTIONBITMAP, WS_MAXIMIZEBOX,
WS_MINIMIZEBOX, WS_OVERLAPPED, WS_OVERLAPPEDWINDOW, WS_POPUP, WS_SYSMENU, WS_THICKFRAME,
},
};
const WINDOW_CLASS_NAME: PCWSTR = windows::core::w!("witas_window_class");
pub(crate) fn register_class() {
unsafe {
let wc = WNDCLASSEXW {
cbSize: std::mem::size_of::<WNDCLASSEXW>() as _,
style: CS_VREDRAW | CS_HREDRAW,
lpfnWndProc: Some(procedure::window_proc),
hInstance: HINSTANCE(GetModuleHandleW(None).unwrap().0),
hCursor: LoadCursorW(None, IDC_ARROW).unwrap(),
lpszClassName: WINDOW_CLASS_NAME,
hbrBackground: HBRUSH(GetStockObject(WHITE_BRUSH).0),
..Default::default()
};
if RegisterClassExW(&wc) == 0 {
panic!("RegisterClassExW failed");
}
}
}
fn get_dpi_from_point(pt: ScreenPosition) -> u32 {
let mut dpi_x = 0;
let mut dpi_y = 0;
unsafe {
GetDpiForMonitor(
MonitorFromPoint(POINT { x: pt.x, y: pt.y }, MONITOR_DEFAULTTOPRIMARY),
MDT_DEFAULT,
&mut dpi_x,
&mut dpi_y,
)
.unwrap_or(());
}
dpi_x
}
pub(crate) struct WindowProperties {
pub visible_ime_candidate_window: bool,
pub imm_context: ime::ImmContext,
pub minimized: bool,
pub maximized: bool,
pub cursor: Cursor,
pub parent: Option<Window>,
pub auto_close: bool,
}
pub trait Style {
fn style(&self) -> WINDOW_STYLE;
fn ex_style(&self) -> WINDOW_EX_STYLE;
}
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct BorderlessStyle {
ex_style: WINDOW_EX_STYLE,
}
impl BorderlessStyle {
#[inline]
pub fn no_redirection_bitmap(mut self, enabled: bool) -> Self {
if enabled {
self.ex_style |= WS_EX_NOREDIRECTIONBITMAP;
} else {
self.ex_style &= !WS_EX_NOREDIRECTIONBITMAP;
}
self
}
}
impl Style for BorderlessStyle {
#[inline]
fn style(&self) -> WINDOW_STYLE {
WS_POPUP
}
#[inline]
fn ex_style(&self) -> WINDOW_EX_STYLE {
WINDOW_EX_STYLE::default()
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct WindowStyle {
style: WINDOW_STYLE,
ex_style: WINDOW_EX_STYLE,
}
impl WindowStyle {
#[inline]
pub fn dialog() -> Self {
Self {
style: WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,
ex_style: WINDOW_EX_STYLE::default(),
}
}
#[inline]
pub fn borderless() -> BorderlessStyle {
BorderlessStyle::default()
}
#[inline]
pub fn resizable(mut self, resizable: bool) -> Self {
if resizable {
self.style |= WS_THICKFRAME;
} else {
self.style &= !WS_THICKFRAME;
}
self
}
#[inline]
pub fn has_minimize_box(mut self, has_box: bool) -> Self {
if has_box {
self.style |= WS_MINIMIZEBOX;
} else {
self.style &= !WS_MINIMIZEBOX;
}
self
}
#[inline]
pub fn has_maximize_box(mut self, has_box: bool) -> Self {
if has_box {
self.style |= WS_MAXIMIZEBOX;
} else {
self.style &= !WS_MAXIMIZEBOX;
}
self
}
#[inline]
pub fn no_redirection_bitmap(mut self, enabled: bool) -> Self {
if enabled {
self.ex_style |= WS_EX_NOREDIRECTIONBITMAP;
} else {
self.ex_style &= !WS_EX_NOREDIRECTIONBITMAP;
}
self
}
}
impl Default for WindowStyle {
#[inline]
fn default() -> Self {
Self {
style: WS_OVERLAPPEDWINDOW,
ex_style: WINDOW_EX_STYLE::default(),
}
}
}
impl Style for WindowStyle {
#[inline]
fn style(&self) -> WINDOW_STYLE {
self.style
}
#[inline]
fn ex_style(&self) -> WINDOW_EX_STYLE {
self.ex_style
}
}
pub(crate) type OutputWithWindow<T> = (T, Window);
pub(crate) type UnboundedSender<T> = mpsc::UnboundedSender<OutputWithWindow<T>>;
pub(crate) type UnboundedReceiver<T> = mpsc::UnboundedReceiver<OutputWithWindow<T>>;
pub struct WindowBuilder<Title = (), Sz = (), Tx = ()> {
title: Title,
position: ScreenPosition,
size: Sz,
style: Box<dyn Style + Send>,
visibility: bool,
enable_ime: bool,
visible_ime_candidate_window: bool,
accept_drop_files: bool,
icon: Option<Icon>,
parent: Option<Window>,
tx: Tx,
raw_input_tx: Option<UnboundedSender<raw_input::RawInputEvent>>,
auto_close: bool,
}
impl WindowBuilder<(), (), ()> {
fn new() -> Self {
Self {
title: (),
position: (0, 0).into(),
size: (),
style: Box::<WindowStyle>::default(),
visibility: true,
enable_ime: true,
visible_ime_candidate_window: true,
accept_drop_files: false,
icon: None,
parent: None,
tx: (),
raw_input_tx: None,
auto_close: true,
}
}
}
impl<Title, Sz, Rx> WindowBuilder<Title, Sz, Rx> {
#[inline]
pub fn title(self, title: impl Into<String>) -> WindowBuilder<String, Sz, Rx> {
WindowBuilder {
title: title.into(),
position: self.position,
size: self.size,
style: self.style,
visibility: self.visibility,
enable_ime: self.enable_ime,
visible_ime_candidate_window: self.visible_ime_candidate_window,
accept_drop_files: self.accept_drop_files,
icon: self.icon,
parent: self.parent,
tx: self.tx,
raw_input_tx: self.raw_input_tx,
auto_close: self.auto_close,
}
}
#[inline]
pub fn position(mut self, position: impl Into<ScreenPosition>) -> Self {
self.position = position.into();
self
}
#[inline]
pub fn inner_size<T>(self, size: T) -> WindowBuilder<Title, T, Rx>
where
T: ToPhysical<u32, Output<u32> = PhysicalSize<u32>>,
{
WindowBuilder {
title: self.title,
position: self.position,
size,
style: self.style,
visibility: self.visibility,
enable_ime: self.enable_ime,
visible_ime_candidate_window: self.visible_ime_candidate_window,
accept_drop_files: self.accept_drop_files,
icon: self.icon,
parent: self.parent,
tx: self.tx,
raw_input_tx: self.raw_input_tx,
auto_close: self.auto_close,
}
}
#[inline]
pub fn set_receiver(
self,
rx: &EventReceiver,
) -> WindowBuilder<Title, Sz, UnboundedSender<Event>> {
WindowBuilder {
title: self.title,
position: self.position,
size: self.size,
style: self.style,
visibility: self.visibility,
enable_ime: self.enable_ime,
visible_ime_candidate_window: self.visible_ime_candidate_window,
accept_drop_files: self.accept_drop_files,
icon: self.icon,
parent: self.parent,
tx: rx.tx.clone(),
raw_input_tx: self.raw_input_tx,
auto_close: self.auto_close,
}
}
#[inline]
pub fn set_raw_input_receiver(
mut self,
raw_input_rx: &RawInputEventRecevier,
) -> WindowBuilder<Title, Sz, Rx> {
self.raw_input_tx = Some(raw_input_rx.tx.clone());
self
}
#[inline]
pub fn style(mut self, style: impl Style + Send + 'static) -> Self {
self.style = Box::new(style);
self
}
#[inline]
pub fn visible(mut self, visibility: bool) -> Self {
self.visibility = visibility;
self
}
#[inline]
pub fn ime(mut self, enable: bool) -> Self {
self.enable_ime = enable;
self
}
#[inline]
pub fn visible_ime_candidate_window(mut self, visible: bool) -> Self {
self.visible_ime_candidate_window = visible;
self
}
#[inline]
pub fn accept_drop_files(mut self, accept: bool) -> Self {
self.accept_drop_files = accept;
self
}
#[inline]
pub fn icon(mut self, icon: Icon) -> Self {
self.icon = Some(icon);
self
}
#[inline]
pub fn parent(mut self, parent: Option<&Window>) -> Self {
self.parent = parent.cloned();
self
}
#[inline]
pub fn auto_close(mut self, auto_close: bool) -> Self {
self.auto_close = auto_close;
self
}
}
impl<Sz> WindowBuilder<String, Sz, UnboundedSender<Event>>
where
Sz: ToPhysical<u32, Output<u32> = PhysicalSize<u32>>,
{
#[inline]
pub fn build(self) -> Build<Sz> {
Build {
builder: Some(self),
rx: None,
}
}
}
impl<Sz> std::future::IntoFuture for WindowBuilder<String, Sz, UnboundedSender<Event>>
where
Sz: ToPhysical<u32, Output<u32> = PhysicalSize<u32>> + Send + Unpin + 'static,
{
type Output = Result<Window>;
type IntoFuture = Build<Sz>;
#[inline]
fn into_future(self) -> Self::IntoFuture {
self.build()
}
}
pub struct Receiver<T> {
tx: UnboundedSender<T>,
rx: UnboundedReceiver<T>,
}
impl<T> Receiver<T> {
#[allow(clippy::new_without_default)]
#[inline]
pub fn new() -> Self {
let (tx, rx) = mpsc::unbounded_channel();
Self { tx, rx }
}
#[inline]
pub async fn recv(&mut self) -> OutputWithWindow<T> {
self.rx.recv().await.unwrap()
}
#[inline]
pub fn try_recv(&mut self) -> Result<Option<OutputWithWindow<T>>> {
match self.rx.try_recv() {
Ok(event) => Ok(Some(event)),
Err(mpsc::error::TryRecvError::Empty) => Ok(None),
Err(mpsc::error::TryRecvError::Disconnected) => Err(Error::UiThreadClosed),
}
}
#[inline]
pub fn blocking_recv(&mut self) -> OutputWithWindow<T> {
self.rx.blocking_recv().unwrap()
}
}
pub type EventReceiver = Receiver<Event>;
pub type RawInputEventRecevier = Receiver<raw_input::RawInputEvent>;
type BuildResult = Result<HWND>;
pub struct Build<Sz> {
builder: Option<WindowBuilder<String, Sz, UnboundedSender<Event>>>,
rx: Option<mpsc::UnboundedReceiver<BuildResult>>,
}
impl<Sz> std::future::Future for Build<Sz>
where
Sz: ToPhysical<u32, Output<u32> = PhysicalSize<u32>> + std::marker::Unpin + Send + 'static,
{
type Output = Result<Window>;
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context,
) -> std::task::Poll<Self::Output> {
let this = self.get_mut();
if let Some(builder) = this.builder.take() {
let (tx, rx) = mpsc::unbounded_channel::<BuildResult>();
let waker = cx.waker().clone();
let create_window = move || unsafe {
let title: HSTRING = builder.title.into();
let dpi = get_dpi_from_point(builder.position);
let size = builder.size.to_physical(dpi);
let style = builder.style.style();
let ex_style = builder.style.ex_style();
let rc = adjust_window_rect(size, style, false, ex_style, dpi);
let hinst = GetModuleHandleW(None).unwrap();
let hwnd = CreateWindowExW(
ex_style,
WINDOW_CLASS_NAME,
&title,
style,
builder.position.x,
builder.position.y,
rc.right - rc.left,
rc.bottom - rc.top,
None,
None,
hinst,
None,
);
if hwnd == HWND(0) {
tx.send(Err(Error::from_win32())).unwrap_or(());
waker.wake();
return;
}
DragAcceptFiles(hwnd, builder.accept_drop_files);
let props = WindowProperties {
visible_ime_candidate_window: builder.visible_ime_candidate_window,
imm_context: ime::ImmContext::new(hwnd),
minimized: false,
maximized: false,
cursor: Cursor::default(),
parent: builder.parent,
auto_close: builder.auto_close,
};
if builder.enable_ime {
props.imm_context.enable();
} else {
props.imm_context.disable();
}
if let Some(icon) = builder.icon {
if let Ok(big) = load_icon(&icon, hinst) {
PostMessageW(hwnd, WM_SETICON, WPARAM(ICON_BIG as _), LPARAM(big.0 as _))
.ok();
}
if let Ok(small) = load_small_icon(&icon, hinst) {
PostMessageW(
hwnd,
WM_SETICON,
WPARAM(ICON_SMALL as _),
LPARAM(small.0 as _),
)
.ok();
}
}
if builder.raw_input_tx.is_some() {
raw_input::register_devices(hwnd, raw_input::WindowState::Foreground)
.unwrap_or(());
}
Context::register_window(hwnd, props, builder.tx, builder.raw_input_tx);
if builder.visibility {
ShowWindow(hwnd, SW_SHOW);
}
tx.send(Ok(hwnd)).unwrap_or(());
waker.wake();
};
UiThread::send_task(create_window);
this.rx = Some(rx);
return std::task::Poll::Pending;
}
match this.rx.as_mut().unwrap().try_recv() {
Ok(ret) => std::task::Poll::Ready(ret.map(|hwnd| Window { hwnd })),
Err(mpsc::error::TryRecvError::Empty) => std::task::Poll::Pending,
Err(mpsc::error::TryRecvError::Disconnected) => {
std::task::Poll::Ready(Err(Error::UiThreadClosed))
}
}
}
}
pub struct InnerWindowBuilder<Pos = (), Sz = (), Tx = ()> {
position: Pos,
size: Sz,
visibility: bool,
enable_ime: bool,
visible_ime_candidate_window: bool,
accept_drop_files: bool,
enable_raw_input: bool,
parent: HWND,
tx: Tx,
raw_input_tx: Option<UnboundedSender<raw_input::RawInputEvent>>,
}
impl InnerWindowBuilder<(), (), ()> {
fn new(parent: HWND) -> Self {
Self {
position: (),
size: (),
visibility: true,
enable_ime: true,
visible_ime_candidate_window: true,
accept_drop_files: false,
enable_raw_input: false,
parent,
tx: (),
raw_input_tx: None,
}
}
}
impl<Pos, Sz, Tx> InnerWindowBuilder<Pos, Sz, Tx> {
#[inline]
pub fn position<T>(self, position: T) -> InnerWindowBuilder<T, Sz, Tx>
where
T: ToPhysical<i32, Output<i32> = PhysicalPosition<i32>>,
{
InnerWindowBuilder {
position,
size: self.size,
visibility: self.visibility,
enable_ime: self.enable_ime,
visible_ime_candidate_window: self.visible_ime_candidate_window,
accept_drop_files: self.accept_drop_files,
enable_raw_input: self.enable_raw_input,
parent: self.parent,
tx: self.tx,
raw_input_tx: self.raw_input_tx,
}
}
#[inline]
pub fn size<T>(self, size: T) -> InnerWindowBuilder<Pos, T, Tx>
where
T: ToPhysical<u32, Output<u32> = PhysicalSize<u32>>,
{
InnerWindowBuilder {
position: self.position,
size,
visibility: self.visibility,
enable_ime: self.enable_ime,
visible_ime_candidate_window: self.visible_ime_candidate_window,
accept_drop_files: self.accept_drop_files,
enable_raw_input: self.enable_raw_input,
parent: self.parent,
tx: self.tx,
raw_input_tx: self.raw_input_tx,
}
}
#[inline]
pub fn set_receiver(
self,
rx: &EventReceiver,
) -> InnerWindowBuilder<Pos, Sz, UnboundedSender<Event>> {
InnerWindowBuilder {
position: self.position,
size: self.size,
visibility: self.visibility,
enable_ime: self.enable_ime,
visible_ime_candidate_window: self.visible_ime_candidate_window,
accept_drop_files: self.accept_drop_files,
enable_raw_input: self.enable_raw_input,
parent: self.parent,
tx: rx.tx.clone(),
raw_input_tx: self.raw_input_tx,
}
}
#[inline]
pub fn raw_input_receiver(
mut self,
raw_input_rx: &RawInputEventRecevier,
) -> InnerWindowBuilder<Pos, Sz, Tx> {
self.raw_input_tx = Some(raw_input_rx.tx.clone());
self
}
#[inline]
pub fn visible(mut self, visibility: bool) -> Self {
self.visibility = visibility;
self
}
#[inline]
pub fn ime(mut self, enable: bool) -> Self {
self.enable_ime = enable;
self
}
#[inline]
pub fn visible_ime_candidate_window(mut self, visible: bool) -> Self {
self.visible_ime_candidate_window = visible;
self
}
#[inline]
pub fn accept_drop_files(mut self, accept: bool) -> Self {
self.accept_drop_files = accept;
self
}
#[inline]
pub fn enable_raw_input(mut self, enable: bool) -> Self {
self.enable_raw_input = enable;
self
}
}
impl<Pos, Sz> InnerWindowBuilder<Pos, Sz, UnboundedSender<Event>>
where
Pos: ToPhysical<i32, Output<i32> = PhysicalPosition<i32>>,
Sz: ToPhysical<u32, Output<u32> = PhysicalSize<u32>>,
{
#[inline]
pub fn build(self) -> BuildInnerWindow<Pos, Sz> {
BuildInnerWindow {
builder: Some(self),
rx: None,
}
}
}
impl<Pos, Sz> std::future::IntoFuture for InnerWindowBuilder<Pos, Sz, UnboundedSender<Event>>
where
Pos: ToPhysical<i32, Output<i32> = PhysicalPosition<i32>> + Send + Unpin + 'static,
Sz: ToPhysical<u32, Output<u32> = PhysicalSize<u32>> + Send + Unpin + 'static,
{
type Output = Result<Window>;
type IntoFuture = BuildInnerWindow<Pos, Sz>;
#[inline]
fn into_future(self) -> Self::IntoFuture {
self.build()
}
}
pub struct BuildInnerWindow<Pos, Sz> {
builder: Option<InnerWindowBuilder<Pos, Sz, UnboundedSender<Event>>>,
rx: Option<mpsc::UnboundedReceiver<BuildResult>>,
}
impl<Pos, Sz> std::future::Future for BuildInnerWindow<Pos, Sz>
where
Pos: ToPhysical<i32, Output<i32> = PhysicalPosition<i32>> + Send + Unpin + 'static,
Sz: ToPhysical<u32, Output<u32> = PhysicalSize<u32>> + Send + Unpin + 'static,
{
type Output = Result<Window>;
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context,
) -> std::task::Poll<Self::Output> {
let this = self.get_mut();
if let Some(builder) = this.builder.take() {
let (tx, rx) = mpsc::unbounded_channel::<BuildResult>();
let waker = cx.waker().clone();
let create_window = move || unsafe {
let title: HSTRING = "".into();
let dpi = GetDpiForWindow(builder.parent);
let hinst = GetModuleHandleW(None).unwrap();
let position = builder.position.to_physical(dpi as _);
let size = builder.size.to_physical(dpi).cast::<i32>().unwrap();
let hwnd = CreateWindowExW(
WINDOW_EX_STYLE(0),
WINDOW_CLASS_NAME,
&title,
WS_CHILD,
position.x,
position.y,
size.width,
size.height,
builder.parent,
None,
hinst,
None,
);
if hwnd == HWND(0) {
tx.send(Err(Error::from_win32())).unwrap_or(());
waker.wake();
return;
}
DragAcceptFiles(hwnd, builder.accept_drop_files);
let props = WindowProperties {
visible_ime_candidate_window: builder.visible_ime_candidate_window,
imm_context: ime::ImmContext::new(hwnd),
minimized: false,
maximized: false,
cursor: Cursor::default(),
parent: None,
auto_close: true,
};
if builder.enable_ime {
props.imm_context.enable();
} else {
props.imm_context.disable();
}
if builder.raw_input_tx.is_some() {
raw_input::register_devices(hwnd, raw_input::WindowState::Foreground)
.unwrap_or(());
}
Context::register_window(hwnd, props, builder.tx, builder.raw_input_tx);
if builder.visibility {
ShowWindow(hwnd, SW_SHOW);
}
tx.send(Ok(hwnd)).unwrap_or(());
waker.wake();
};
UiThread::send_task(create_window);
this.rx = Some(rx);
return std::task::Poll::Pending;
}
match this.rx.as_mut().unwrap().try_recv() {
Ok(ret) => std::task::Poll::Ready(ret.map(|hwnd| Window { hwnd })),
Err(mpsc::error::TryRecvError::Empty) => std::task::Poll::Pending,
Err(mpsc::error::TryRecvError::Disconnected) => {
std::task::Poll::Ready(Err(Error::UiThreadClosed))
}
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Window {
hwnd: HWND,
}
impl Window {
pub(crate) fn new(hwnd: HWND) -> Self {
Self { hwnd }
}
#[inline]
pub fn builder() -> WindowBuilder {
crate::init();
WindowBuilder::new()
}
#[inline]
pub fn inner_window_builder(window: &Window) -> InnerWindowBuilder {
InnerWindowBuilder::new(HWND(window.raw_handle() as _))
}
#[inline]
pub async fn position(&self) -> Option<ScreenPosition> {
if self.is_closed() {
return None;
}
let hwnd = self.hwnd;
let (tx, rx) = oneshot::channel();
UiThread::send_task(move || {
let rc = utility::get_window_rect(hwnd);
tx.send((rc.left, rc.top).into()).unwrap_or(());
});
rx.await.ok()
}
#[inline]
pub async fn inner_size(&self) -> Option<PhysicalSize<u32>> {
if self.is_closed() {
return None;
}
let hwnd = self.hwnd;
let (tx, rx) = oneshot::channel();
UiThread::send_task(move || {
let rc = utility::get_client_rect(hwnd);
tx.send(((rc.right - rc.left) as u32, (rc.bottom - rc.top) as u32).into())
.unwrap_or(());
});
rx.await.ok()
}
#[inline]
pub async fn dpi(&self) -> Option<u32> {
if self.is_closed() {
return None;
}
let hwnd = self.hwnd;
let (tx, rx) = oneshot::channel();
UiThread::send_task(move || unsafe {
let dpi = GetDpiForWindow(hwnd);
tx.send(dpi).unwrap_or(());
});
rx.await.ok()
}
#[inline]
pub async fn scale_factor(&self) -> Option<f32> {
self.dpi().await.map(|dpi| dpi as f32 / DEFAULT_DPI as f32)
}
#[inline]
pub fn show(&self) {
unsafe {
ShowWindowAsync(self.hwnd, SW_SHOW);
}
}
#[inline]
pub fn hide(&self) {
unsafe {
ShowWindowAsync(self.hwnd, SW_HIDE);
}
}
#[inline]
pub fn minimize(&self) {
unsafe {
ShowWindowAsync(self.hwnd, SW_MINIMIZE);
}
}
#[inline]
pub fn maximize(&self) {
unsafe {
ShowWindowAsync(self.hwnd, SW_SHOWMAXIMIZED);
}
}
#[inline]
pub fn restore(&self) {
unsafe {
ShowWindowAsync(self.hwnd, SW_RESTORE);
}
}
#[inline]
pub fn accept_drop_files(&self, accept: bool) {
let hwnd = self.hwnd;
UiThread::send_task(move || unsafe {
DragAcceptFiles(hwnd, accept);
});
}
#[inline]
pub fn ime(&self, enable: bool) {
let hwnd = self.hwnd;
UiThread::send_task(move || {
Context::set_window_property(hwnd, |props| {
if enable {
props.imm_context.enable();
} else {
props.imm_context.disable();
}
});
});
}
#[inline]
pub fn set_cursor(&self, cursor: Cursor) {
let hwnd = self.hwnd;
UiThread::send_task(move || {
Context::set_window_property(hwnd, |props| {
props.cursor = cursor;
props.cursor.set();
})
});
}
#[inline]
pub fn close(&self) {
unsafe {
if !self.is_closed() {
PostMessageW(self.hwnd, WM_CLOSE, WPARAM(0), LPARAM(0)).ok();
}
}
}
#[inline]
pub fn is_closed(&self) -> bool {
Context::window_is_closed(self.hwnd)
}
#[inline]
pub fn redraw(&self, invalid_rect: Option<PhysicalRect<i32>>) {
let hwnd = self.hwnd;
UiThread::send_task(move || unsafe {
let rc: Option<RECT> = invalid_rect.map(|rc| rc.into());
let p = rc.as_ref().map(|p| p as *const _);
RedrawWindow(hwnd, p, None, RDW_INVALIDATE);
});
}
#[inline]
pub fn raw_handle(&self) -> *const std::ffi::c_void {
self.hwnd.0 as _
}
}
impl std::hash::Hash for Window {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write_isize(self.hwnd.0);
}
}