Skip to content

Commit

Permalink
* Fixing tooltip/drag window in sub_windows.rs example.
Browse files Browse the repository at this point in the history
* Implementation differs slightly from linebender#1919. Unlike linebender#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.
  • Loading branch information
sjoshid committed Sep 21, 2021
1 parent 82ec0be commit 9a14dc0
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 48 deletions.
67 changes: 41 additions & 26 deletions druid-shell/src/backend/windows/window.rs
Expand Up @@ -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<crate::WindowHandle>,
}

impl std::fmt::Debug for WindowState {
Expand Down Expand Up @@ -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.");
}
}
}
Expand All @@ -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),
};
Expand All @@ -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<crate::WindowHandle> = 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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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<Point>,
parent_window_scale: Result<Scale, crate::Error>,
) -> 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
Expand Down Expand Up @@ -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() {
Expand All @@ -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)
Expand Down
12 changes: 0 additions & 12 deletions druid-shell/src/window.rs
Expand Up @@ -15,7 +15,6 @@
//! Platform independent window types.

use std::any::Any;
use std::fmt;
use std::time::Duration;

use crate::application::Application;
Expand Down Expand Up @@ -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 {
Expand Down
23 changes: 14 additions & 9 deletions druid/examples/sub_window.rs
Expand Up @@ -74,7 +74,7 @@ enum TooltipState {
last_move: Instant,
timer_expire: Instant,
token: TimerToken,
window_pos: Point,
position_in_window_coordinates: Point,
},
Fresh,
}
Expand Down Expand Up @@ -105,15 +105,15 @@ impl<T, W: Widget<T>> Controller<T, W> 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,
},
TooltipState::Waiting {
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 {
Expand All @@ -125,7 +125,7 @@ impl<T, W: Widget<T>> Controller<T, W> 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 => {
Expand All @@ -138,17 +138,22 @@ impl<T, W: Widget<T>> Controller<T, W> 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(),
Expand All @@ -168,7 +173,7 @@ impl<T, W: Widget<T>> Controller<T, W> 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,
Expand Down
2 changes: 1 addition & 1 deletion druid/src/app.rs
Expand Up @@ -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<Size>,
Expand Down
11 changes: 11 additions & 0 deletions druid/src/contexts.rs
Expand Up @@ -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
Expand Down

0 comments on commit 9a14dc0

Please sign in to comment.