From 9a14dc0c723089d4aa44156fad0fb79abb9cf305 Mon Sep 17 00:00:00 2001 From: Sujit Joshi Date: Mon, 20 Sep 2021 16:34:55 -0400 Subject: [PATCH] * Fixing tooltip/drag window in `sub_windows.rs` example. * Implementation differs slightly from #1919. Unlike #1919, this does NOT store parent window handle in `WindowState`. If you want to access parent window handle AFTER creating window use [GetParent](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getparent). * Handles scaling. * Tooltip example in nursery tested with these changes. If this is accepted, I'll make changes in nursery too. --- druid-shell/src/backend/windows/window.rs | 67 ++++++++++++++--------- druid-shell/src/window.rs | 12 ---- druid/examples/sub_window.rs | 23 +++++--- druid/src/app.rs | 2 +- druid/src/contexts.rs | 11 ++++ 5 files changed, 67 insertions(+), 48 deletions(-) diff --git a/druid-shell/src/backend/windows/window.rs b/druid-shell/src/backend/windows/window.rs index 264da3a699..357f55aa83 100644 --- a/druid-shell/src/backend/windows/window.rs +++ b/druid-shell/src/backend/windows/window.rs @@ -237,8 +237,6 @@ struct WindowState { // Is the window focusable ("activatable" in Win32 terminology)? // False for tooltips, to prevent stealing focus from owner window. is_focusable: bool, - - parent: Option, } impl std::fmt::Debug for WindowState { @@ -1335,7 +1333,7 @@ impl WindowBuilder { match level { WindowLevel::AppWindow | WindowLevel::Tooltip(_) => self.level = Some(level), _ => { - warn!("WindowBuilder::set_level({:?}) is currently unimplemented for Windows backend.", level); + warn!("WindowLevel::Modal and WindowLevel::DropDown is currently unimplemented for Windows backend."); } } } @@ -1355,7 +1353,7 @@ impl WindowBuilder { present_strategy: self.present_strategy, }; - let (pos_x, pos_y) = match self.position { + let (mut pos_x, mut pos_y) = match self.position { Some(pos) => (pos.x as i32, pos.y as i32), None => (CW_USEDEFAULT, CW_USEDEFAULT), }; @@ -1379,18 +1377,22 @@ impl WindowBuilder { None => (0 as HMENU, None, false), }; - let mut dwStyle = WS_OVERLAPPEDWINDOW; + let mut dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN; let mut dwExStyle: DWORD = 0; let mut focusable = true; - let mut parent: Option = None; if let Some(level) = self.level { match level { WindowLevel::AppWindow => (), - WindowLevel::Tooltip(p) => { + WindowLevel::Tooltip(parent_window_handle) => { dwStyle = WS_POPUP; dwExStyle = WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW; focusable = false; - parent = Some(p) + let scaled_sub_window_point = WindowBuilder::scale_sub_window_position( + self.position, + parent_window_handle.get_scale(), + ); + pos_x = scaled_sub_window_point.x as i32; + pos_y = scaled_sub_window_point.y as i32; } WindowLevel::DropDown(_) => { dwStyle = WS_CHILD; @@ -1419,8 +1421,6 @@ impl WindowBuilder { handle_titlebar: Cell::new(false), active_text_input: Cell::new(None), is_focusable: focusable, - - parent, }; let win = Rc::new(window); let handle = WindowHandle { @@ -1507,6 +1507,30 @@ impl WindowBuilder { Ok(handle) } } + + /// When creating a sub-window, we need to scale its position with respect to its parent. + /// If there is any error while scaling, log it as a warn and show sub-window in top left corner of screen/window. + fn scale_sub_window_position( + un_scaled_sub_window_position: Option, + parent_window_scale: Result, + ) -> Point { + match (un_scaled_sub_window_position, parent_window_scale) { + (Some(point), Ok(s)) => point.to_px(s), + (None, Ok(_)) => { + warn!("No position"); + Point::new(0., 0.) + } + (Some(_), Err(r)) => { + warn!("Error with scale: {:?}", r); + Point::new(0., 0.) + } + (None, Err(r)) => { + warn!("No position"); + warn!("Error with scale: {:?}", r); + Point::new(0., 0.) + } + } + } } /// Choose an adapter. Here the heuristic is to choose the adapter with the @@ -1849,18 +1873,15 @@ impl WindowHandle { } // Sets the position of the window in virtual screen coordinates - pub fn set_position(&self, mut position: Point) { - //TODO: Make the window follow the parent, mostly for modal windows. - if let Some(state) = self.state.upgrade() { - if let Some(parent_state) = &state.parent { - let pos = (*parent_state).get_position(); - position += (pos.x, pos.y) - } - }; + pub fn set_position(&self, position: Point) { self.defer(DeferredOp::SetWindowState(window::WindowState::Restored)); self.defer(DeferredOp::SetPosition(position)); } + pub fn set_level(&self, _level: WindowLevel) { + warn!("Window level unimplemented for Windows!"); + } + // Gets the position of the window in virtual screen coordinates pub fn get_position(&self) -> Point { if let Some(w) = self.state.upgrade() { @@ -1878,14 +1899,8 @@ impl WindowHandle { Error::Hr(HRESULT_FROM_WIN32(GetLastError())) ); }; - let mut position = Point::new(rect.left as f64, rect.top as f64); - if let Some(state) = self.state.upgrade() { - if let Some(parent_state) = &state.parent { - let pos = (*parent_state).get_position(); - position -= (pos.x, pos.y) - } - }; - return position; + return Point::new(rect.left as f64, rect.top as f64) + .to_dp(self.get_scale().unwrap()); } } Point::new(0.0, 0.0) diff --git a/druid-shell/src/window.rs b/druid-shell/src/window.rs index 73d88f5655..2001d5512f 100644 --- a/druid-shell/src/window.rs +++ b/druid-shell/src/window.rs @@ -15,7 +15,6 @@ //! Platform independent window types. use std::any::Any; -use std::fmt; use std::time::Duration; use crate::application::Application; @@ -161,17 +160,6 @@ pub enum WindowLevel { Modal(WindowHandle), } -impl fmt::Debug for WindowLevel { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - WindowLevel::AppWindow => write!(f, "AppWindow"), - WindowLevel::Tooltip(_) => write!(f, "ToolTip"), - WindowLevel::DropDown(_) => write!(f, "DropDown"), - WindowLevel::Modal(_) => write!(f, "Modal"), - } - } -} - /// Contains the different states a Window can be in. #[derive(Debug, Clone, Copy, PartialEq)] pub enum WindowState { diff --git a/druid/examples/sub_window.rs b/druid/examples/sub_window.rs index 7ed1a8c6e3..79d1874e78 100644 --- a/druid/examples/sub_window.rs +++ b/druid/examples/sub_window.rs @@ -74,7 +74,7 @@ enum TooltipState { last_move: Instant, timer_expire: Instant, token: TimerToken, - window_pos: Point, + position_in_window_coordinates: Point, }, Fresh, } @@ -105,7 +105,7 @@ impl> Controller for TooltipController { last_move: now, timer_expire: now + wait_duration, token: ctx.request_timer(wait_duration), - window_pos: me.window_pos, + position_in_window_coordinates: me.window_pos, }), _ => None, }, @@ -113,7 +113,7 @@ impl> Controller for TooltipController { last_move, timer_expire, token, - window_pos, + position_in_window_coordinates, } => match event { Event::MouseMove(me) if ctx.is_hot() => { let (cur_token, cur_expire) = if *timer_expire - now < resched_dur { @@ -125,7 +125,7 @@ impl> Controller for TooltipController { last_move: now, timer_expire: cur_expire, token: cur_token, - window_pos: me.window_pos, + position_in_window_coordinates: me.window_pos, }) } Event::Timer(tok) if tok == token => { @@ -138,17 +138,22 @@ impl> Controller for TooltipController { last_move: *last_move, timer_expire: deadline, token: ctx.request_timer(wait_for), - window_pos: *window_pos, + position_in_window_coordinates: *position_in_window_coordinates, }) } else { + let tooltip_position_in_window_coordinates = + (position_in_window_coordinates.to_vec2() + cursor_size.to_vec2()) + .to_point(); + let tooltip_position_in_screen_coordinates = ctx + .window_coordinates_to_screen_coordinates( + tooltip_position_in_window_coordinates, + ); let win_id = ctx.new_sub_window( WindowConfig::default() .show_titlebar(false) .window_size_policy(WindowSizePolicy::Content) .set_level(WindowLevel::Tooltip(ctx.window().clone())) - .set_position( - (window_pos.to_vec2() + cursor_size.to_vec2()).to_point(), - ), + .set_position(tooltip_position_in_screen_coordinates), Label::<()>::new(self.tip.clone()), (), env.clone(), @@ -168,7 +173,7 @@ impl> Controller for TooltipController { last_move: now, timer_expire: now + wait_duration, token: ctx.request_timer(wait_duration), - window_pos: me.window_pos, + position_in_window_coordinates: me.window_pos, }) } _ => None, diff --git a/druid/src/app.rs b/druid/src/app.rs index 4f8c9bcb49..7b4f9d7cfb 100644 --- a/druid/src/app.rs +++ b/druid/src/app.rs @@ -53,7 +53,7 @@ pub enum WindowSizePolicy { /// Window configuration that can be applied to a WindowBuilder, or to an existing WindowHandle. /// It does not include anything related to app data. -#[derive(Debug, PartialEq)] +#[derive(PartialEq)] pub struct WindowConfig { pub(crate) size_policy: WindowSizePolicy, pub(crate) size: Option, diff --git a/druid/src/contexts.rs b/druid/src/contexts.rs index f9fad558c5..8f2e0acd95 100644 --- a/druid/src/contexts.rs +++ b/druid/src/contexts.rs @@ -212,6 +212,17 @@ impl_context_method!( content_origin + self.to_window(widget_point).to_vec2() } + /// Convert a point in window coordinate space to the screen's. + /// See the [`Screen`] module + /// + /// [`Screen`]: crate::shell::Screen + pub fn window_coordinates_to_screen_coordinates( + &self, + point_in_window_coordinates: Point, + ) -> Point { + self.window().get_position() + point_in_window_coordinates.to_vec2() + } + /// The "hot" (aka hover) status of a widget. /// /// A widget is "hot" when the mouse is hovered over it. Widgets will