mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-01 05:58:12 +08:00
Desktop: Fix missing taskbar icon on Windows (#3490)
* fix missing taskbar icon * focus window after init
This commit is contained in:
@@ -54,9 +54,11 @@ windows = { version = "0.58.0", features = [
|
|||||||
"Win32_Graphics_Dwm",
|
"Win32_Graphics_Dwm",
|
||||||
"Win32_Graphics_Gdi",
|
"Win32_Graphics_Gdi",
|
||||||
"Win32_System_LibraryLoader",
|
"Win32_System_LibraryLoader",
|
||||||
|
"Win32_System_Com",
|
||||||
"Win32_UI_Controls",
|
"Win32_UI_Controls",
|
||||||
"Win32_UI_WindowsAndMessaging",
|
"Win32_UI_WindowsAndMessaging",
|
||||||
"Win32_UI_HiDpi",
|
"Win32_UI_HiDpi",
|
||||||
|
"Win32_UI_Shell",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
# macOS-specific dependencies
|
# macOS-specific dependencies
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ fn main() {
|
|||||||
// TODO: Replace with actual version
|
// TODO: Replace with actual version
|
||||||
res.set_version_info(winres::VersionInfo::FILEVERSION, {
|
res.set_version_info(winres::VersionInfo::FILEVERSION, {
|
||||||
const MAJOR: u64 = 0;
|
const MAJOR: u64 = 0;
|
||||||
const MINOR: u64 = 999;
|
const MINOR: u64 = 0;
|
||||||
const PATCH: u64 = 0;
|
const PATCH: u64 = 0;
|
||||||
const RELEASE: u64 = 0;
|
const RELEASE: u64 = 0;
|
||||||
(MAJOR << 48) | (MINOR << 32) | (PATCH << 16) | RELEASE
|
(MAJOR << 48) | (MINOR << 32) | (PATCH << 16) | RELEASE
|
||||||
});
|
});
|
||||||
res.set("FileVersion", "0.999.0.0");
|
res.set("FileVersion", "0.0.0.0");
|
||||||
res.set("ProductVersion", "0.999.0.0");
|
res.set("ProductVersion", "0.0.0.0");
|
||||||
|
|
||||||
res.set("OriginalFilename", "Graphite.exe");
|
res.set("OriginalFilename", "Graphite.exe");
|
||||||
|
|
||||||
|
|||||||
@@ -456,6 +456,10 @@ impl ApplicationHandler for App {
|
|||||||
let render_state = RenderState::new(self.window.as_ref().unwrap(), self.wgpu_context.clone());
|
let render_state = RenderState::new(self.window.as_ref().unwrap(), self.wgpu_context.clone());
|
||||||
self.render_state = Some(render_state);
|
self.render_state = Some(render_state);
|
||||||
|
|
||||||
|
if let Some(window) = &self.window.as_ref() {
|
||||||
|
window.show();
|
||||||
|
}
|
||||||
|
|
||||||
self.resize();
|
self.resize();
|
||||||
|
|
||||||
self.desktop_wrapper.init(self.wgpu_context.clone());
|
self.desktop_wrapper.init(self.wgpu_context.clone());
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
pub(crate) const APP_NAME: &str = "Graphite";
|
pub(crate) const APP_NAME: &str = "Graphite";
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||||
pub(crate) const APP_ID: &str = "art.graphite.Graphite";
|
pub(crate) const APP_ID: &str = "art.graphite.Graphite";
|
||||||
|
|
||||||
pub(crate) const APP_DIRECTORY_NAME: &str = "graphite";
|
pub(crate) const APP_DIRECTORY_NAME: &str = "graphite";
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ impl Window {
|
|||||||
.with_min_surface_size(winit::dpi::LogicalSize::new(400, 300))
|
.with_min_surface_size(winit::dpi::LogicalSize::new(400, 300))
|
||||||
.with_surface_size(winit::dpi::LogicalSize::new(1200, 800))
|
.with_surface_size(winit::dpi::LogicalSize::new(1200, 800))
|
||||||
.with_resizable(true)
|
.with_resizable(true)
|
||||||
|
.with_visible(false)
|
||||||
.with_theme(Some(winit::window::Theme::Dark));
|
.with_theme(Some(winit::window::Theme::Dark));
|
||||||
|
|
||||||
attributes = native::NativeWindowImpl::configure(attributes, event_loop);
|
attributes = native::NativeWindowImpl::configure(attributes, event_loop);
|
||||||
@@ -67,6 +68,11 @@ impl Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn show(&self) {
|
||||||
|
self.winit_window.set_visible(true);
|
||||||
|
self.winit_window.focus_window();
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn request_redraw(&self) {
|
pub(crate) fn request_redraw(&self) {
|
||||||
self.winit_window.request_redraw();
|
self.winit_window.request_redraw();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use winit::dpi::PhysicalSize;
|
use windows::Win32::System::Com::{COINIT_APARTMENTTHREADED, CoInitializeEx};
|
||||||
|
use windows::Win32::UI::Shell::SetCurrentProcessExplicitAppUserModelID;
|
||||||
|
use windows::core::HSTRING;
|
||||||
use winit::event_loop::ActiveEventLoop;
|
use winit::event_loop::ActiveEventLoop;
|
||||||
use winit::icon::Icon;
|
|
||||||
use winit::platform::windows::{WinIcon, WindowAttributesWindows};
|
|
||||||
use winit::window::{Window, WindowAttributes};
|
use winit::window::{Window, WindowAttributes};
|
||||||
|
|
||||||
|
use crate::consts::APP_ID;
|
||||||
use crate::event::AppEventScheduler;
|
use crate::event::AppEventScheduler;
|
||||||
|
|
||||||
pub(super) struct NativeWindowImpl {
|
pub(super) struct NativeWindowImpl {
|
||||||
@@ -11,11 +12,16 @@ pub(super) struct NativeWindowImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl super::NativeWindow for NativeWindowImpl {
|
impl super::NativeWindow for NativeWindowImpl {
|
||||||
|
fn init() {
|
||||||
|
let app_id = HSTRING::from(APP_ID);
|
||||||
|
unsafe {
|
||||||
|
let _ = CoInitializeEx(None, COINIT_APARTMENTTHREADED).ok();
|
||||||
|
SetCurrentProcessExplicitAppUserModelID(&app_id).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn configure(attributes: WindowAttributes, _event_loop: &dyn ActiveEventLoop) -> WindowAttributes {
|
fn configure(attributes: WindowAttributes, _event_loop: &dyn ActiveEventLoop) -> WindowAttributes {
|
||||||
let icon = WinIcon::from_resource(1, Some(PhysicalSize::new(256, 256))).ok().map(|icon| Icon(std::sync::Arc::new(icon)));
|
attributes
|
||||||
let win_window = WindowAttributesWindows::default().with_taskbar_icon(icon);
|
|
||||||
let icon = WinIcon::from_resource(1, None).ok().map(|icon| Icon(std::sync::Arc::new(icon)));
|
|
||||||
attributes.with_window_icon(icon).with_platform_attributes(Box::new(win_window))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(window: &dyn Window, _app_event_scheduler: AppEventScheduler) -> Self {
|
fn new(window: &dyn Window, _app_event_scheduler: AppEventScheduler) -> Self {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ pub(super) struct NativeWindowHandle {
|
|||||||
impl NativeWindowHandle {
|
impl NativeWindowHandle {
|
||||||
pub(super) fn new(window: &dyn Window) -> NativeWindowHandle {
|
pub(super) fn new(window: &dyn Window) -> NativeWindowHandle {
|
||||||
// Extract Win32 HWND from winit.
|
// Extract Win32 HWND from winit.
|
||||||
let hwnd = match window.window_handle().expect("No window handle").as_raw() {
|
let main = match window.window_handle().expect("No window handle").as_raw() {
|
||||||
RawWindowHandle::Win32(h) => HWND(h.hwnd.get() as *mut std::ffi::c_void),
|
RawWindowHandle::Win32(h) => HWND(h.hwnd.get() as *mut std::ffi::c_void),
|
||||||
_ => panic!("Not a Win32 window"),
|
_ => panic!("Not a Win32 window"),
|
||||||
};
|
};
|
||||||
@@ -57,47 +57,61 @@ impl NativeWindowHandle {
|
|||||||
None,
|
None,
|
||||||
HINSTANCE(std::ptr::null_mut()),
|
HINSTANCE(std::ptr::null_mut()),
|
||||||
// Pass the main window's HWND to WM_NCCREATE so the helper can store it.
|
// Pass the main window's HWND to WM_NCCREATE so the helper can store it.
|
||||||
Some(&hwnd as *const _ as _),
|
Some(&main as *const _ as _),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.expect("CreateWindowExW failed");
|
.expect("CreateWindowExW failed");
|
||||||
|
|
||||||
// Subclass the main window.
|
// Subclass the main window.
|
||||||
// https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-setwindowlongptra
|
// https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-setwindowlongptra
|
||||||
let prev_window_message_handler = unsafe { SetWindowLongPtrW(hwnd, GWLP_WNDPROC, main_window_handle_message as isize) };
|
let prev_window_message_handler = unsafe { SetWindowLongPtrW(main, GWLP_WNDPROC, main_window_handle_message as isize) };
|
||||||
if prev_window_message_handler == 0 {
|
if prev_window_message_handler == 0 {
|
||||||
let _ = unsafe { DestroyWindow(helper) };
|
let _ = unsafe { DestroyWindow(helper) };
|
||||||
panic!("SetWindowLongPtrW failed");
|
panic!("SetWindowLongPtrW failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
let inner = NativeWindowHandle {
|
let native_handle = NativeWindowHandle {
|
||||||
main: hwnd,
|
main,
|
||||||
helper,
|
helper,
|
||||||
prev_window_message_handler,
|
prev_window_message_handler,
|
||||||
};
|
};
|
||||||
registry::insert(&inner);
|
registry::insert(&native_handle);
|
||||||
|
|
||||||
// Place the helper over the main window and show it without activation.
|
// Place the helper over the main window and show it without activation.
|
||||||
unsafe { position_helper(hwnd, helper) };
|
unsafe { position_helper(main, helper) };
|
||||||
let _ = unsafe { ShowWindow(helper, SW_SHOWNOACTIVATE) };
|
let _ = unsafe { ShowWindow(helper, SW_SHOWNOACTIVATE) };
|
||||||
|
|
||||||
// DwmExtendFrameIntoClientArea is needed to keep native window frame (but no titlebar).
|
// DwmExtendFrameIntoClientArea is needed to keep native window frame (but no titlebar).
|
||||||
// https://learn.microsoft.com/windows/win32/api/dwmapi/nf-dwmapi-dwmextendframeintoclientarea
|
// https://learn.microsoft.com/windows/win32/api/dwmapi/nf-dwmapi-dwmextendframeintoclientarea
|
||||||
// https://learn.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
|
// https://learn.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
|
||||||
let mut boarder_size: u32 = 1;
|
let mut boarder_size: u32 = 1;
|
||||||
let _ = unsafe { DwmGetWindowAttribute(hwnd, DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &mut boarder_size as *mut _ as *mut _, size_of::<u32>() as u32) };
|
let _ = unsafe { DwmGetWindowAttribute(main, DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &mut boarder_size as *mut _ as *mut _, size_of::<u32>() as u32) };
|
||||||
let margins = MARGINS {
|
let margins = MARGINS {
|
||||||
cxLeftWidth: 0,
|
cxLeftWidth: 0,
|
||||||
cxRightWidth: 0,
|
cxRightWidth: 0,
|
||||||
cyBottomHeight: 0,
|
cyBottomHeight: 0,
|
||||||
cyTopHeight: boarder_size as i32,
|
cyTopHeight: boarder_size as i32,
|
||||||
};
|
};
|
||||||
let _ = unsafe { DwmExtendFrameIntoClientArea(hwnd, &margins) };
|
let _ = unsafe { DwmExtendFrameIntoClientArea(main, &margins) };
|
||||||
|
|
||||||
|
let hinst = unsafe { GetModuleHandleW(None) }.unwrap();
|
||||||
|
|
||||||
|
// Set taskbar icon
|
||||||
|
if let Ok(big) = unsafe { LoadImageW(hinst, PCWSTR(1usize as *const u16), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), LR_SHARED) } {
|
||||||
|
unsafe { SetClassLongPtrW(main, GCLP_HICON, big.0 as isize) };
|
||||||
|
unsafe { SendMessageW(main, WM_SETICON, WPARAM(ICON_BIG as usize), LPARAM(big.0 as isize)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set window icon
|
||||||
|
if let Ok(small) = unsafe { LoadImageW(hinst, PCWSTR(1usize as *const u16), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED) } {
|
||||||
|
unsafe { SetClassLongPtrW(main, GCLP_HICONSM, small.0 as isize) };
|
||||||
|
unsafe { SendMessageW(main, WM_SETICON, WPARAM(ICON_SMALL as usize), LPARAM(small.0 as isize)) };
|
||||||
|
}
|
||||||
|
|
||||||
// Force window update
|
// Force window update
|
||||||
let _ = unsafe { SetWindowPos(hwnd, None, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER) };
|
let _ = unsafe { SetWindowPos(main, None, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER) };
|
||||||
|
|
||||||
inner
|
native_handle
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn destroy(&self) {
|
pub(super) fn destroy(&self) {
|
||||||
@@ -105,7 +119,7 @@ impl NativeWindowHandle {
|
|||||||
|
|
||||||
// Undo subclassing and destroy the helper window.
|
// Undo subclassing and destroy the helper window.
|
||||||
let _ = unsafe { SetWindowLongPtrW(self.main, GWLP_WNDPROC, self.prev_window_message_handler) };
|
let _ = unsafe { SetWindowLongPtrW(self.main, GWLP_WNDPROC, self.prev_window_message_handler) };
|
||||||
if self.helper.0 != std::ptr::null_mut() {
|
if !self.helper.is_invalid() {
|
||||||
let _ = unsafe { DestroyWindow(self.helper) };
|
let _ = unsafe { DestroyWindow(self.helper) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,16 +278,17 @@ unsafe extern "system" fn helper_window_handle_message(hwnd: HWND, msg: u32, wpa
|
|||||||
unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
|
unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RESIZE_BAND_THICKNESS: i32 = 8;
|
||||||
|
|
||||||
// Position the helper window to match the main window's location and size (plus the resize band size).
|
// Position the helper window to match the main window's location and size (plus the resize band size).
|
||||||
unsafe fn position_helper(main: HWND, helper: HWND) {
|
unsafe fn position_helper(main: HWND, helper: HWND) {
|
||||||
let mut r = RECT::default();
|
let mut r = RECT::default();
|
||||||
let _ = unsafe { GetWindowRect(main, &mut r) };
|
let _ = unsafe { GetWindowRect(main, &mut r) };
|
||||||
|
|
||||||
const RESIZE_BAND_SIZE: i32 = 8;
|
let x = r.left - RESIZE_BAND_THICKNESS;
|
||||||
let x = r.left - RESIZE_BAND_SIZE;
|
let y = r.top - RESIZE_BAND_THICKNESS;
|
||||||
let y = r.top - RESIZE_BAND_SIZE;
|
let w = (r.right - r.left) + RESIZE_BAND_THICKNESS * 2;
|
||||||
let w = (r.right - r.left) + RESIZE_BAND_SIZE * 2;
|
let h = (r.bottom - r.top) + RESIZE_BAND_THICKNESS * 2;
|
||||||
let h = (r.bottom - r.top) + RESIZE_BAND_SIZE * 2;
|
|
||||||
|
|
||||||
let _ = unsafe { SetWindowPos(helper, main, x, y, w, h, SWP_NOACTIVATE) };
|
let _ = unsafe { SetWindowPos(helper, main, x, y, w, h, SWP_NOACTIVATE) };
|
||||||
}
|
}
|
||||||
@@ -285,7 +300,6 @@ unsafe fn calculate_hit(helper: HWND, lparam: LPARAM) -> u32 {
|
|||||||
let mut r = RECT::default();
|
let mut r = RECT::default();
|
||||||
let _ = unsafe { GetWindowRect(helper, &mut r) };
|
let _ = unsafe { GetWindowRect(helper, &mut r) };
|
||||||
|
|
||||||
const RESIZE_BAND_THICKNESS: i32 = 8;
|
|
||||||
let on_top = y < (r.top + RESIZE_BAND_THICKNESS) as u32;
|
let on_top = y < (r.top + RESIZE_BAND_THICKNESS) as u32;
|
||||||
let on_right = x >= (r.right - RESIZE_BAND_THICKNESS) as u32;
|
let on_right = x >= (r.right - RESIZE_BAND_THICKNESS) as u32;
|
||||||
let on_bottom = y >= (r.bottom - RESIZE_BAND_THICKNESS) as u32;
|
let on_bottom = y >= (r.bottom - RESIZE_BAND_THICKNESS) as u32;
|
||||||
|
|||||||
Reference in New Issue
Block a user