Skip to content

Commit

Permalink
Windows tooltip changes (#1982)
Browse files Browse the repository at this point in the history
Fixes positioning of tooltips on windows, and correctly handles DPI scaling (only for tooltip position, so far).
  • Loading branch information
sjoshid authored Oct 5, 2021
1 parent 376c372 commit d650288
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 50 deletions.
64 changes: 39 additions & 25 deletions druid-shell/src/backend/windows/window.rs
Original file line number Diff line number Diff line change
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,9 @@ impl WindowBuilder {
present_strategy: self.present_strategy,
};

let (pos_x, pos_y) = match self.position {
// TODO: pos_x and pos_y are only scaled for windows with parents. But they need to be
// scaled for windows without parents too.
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 @@ -1382,15 +1382,25 @@ impl WindowBuilder {
let mut dwStyle = WS_OVERLAPPEDWINDOW;
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)
if let Some(point_in_window_coord) = self.position {
let screen_point = parent_window_handle.get_position()
+ point_in_window_coord.to_vec2();
let scaled_tooltip_point = WindowBuilder::scale_sub_window_position(
screen_point,
parent_window_handle.get_scale(),
);
pos_x = scaled_tooltip_point.x as i32;
pos_y = scaled_tooltip_point.y as i32;
} else {
warn!("No position provided for tooltip!");
}
}
WindowLevel::DropDown(_) => {
dwStyle = WS_CHILD;
Expand Down Expand Up @@ -1419,8 +1429,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 +1515,21 @@ 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: Point,
parent_window_scale: Result<Scale, crate::Error>,
) -> Point {
match parent_window_scale {
Ok(s) => un_scaled_sub_window_position.to_px(s),
Err(e) => {
warn!("Error with scale: {:?}", e);
Point::new(0., 0.)
}
}
}
}

/// Choose an adapter. Here the heuristic is to choose the adapter with the
Expand Down Expand Up @@ -1849,18 +1872,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 +1898,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
20 changes: 5 additions & 15 deletions druid-shell/src/window.rs
Original file line number Diff line number Diff line change
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 Expand Up @@ -490,10 +478,12 @@ impl WindowBuilder {
self.0.set_transparent(transparent)
}

/// Sets the initial window position in [display points], relative to the origin of the
/// virtual screen.
/// Sets the initial window position in display points.
/// For windows with a parent, the position is relative to the parent.
/// For windows without a parent, it is relative to the origin of the virtual screen.
/// See also [set_level]
///
/// [display points]: crate::Scale
/// [set_level]: crate::WindowBuilder::set_level
pub fn set_position(&mut self, position: Point) {
self.0.set_position(position);
}
Expand Down
19 changes: 10 additions & 9 deletions druid/examples/sub_window.rs
Original file line number Diff line number Diff line change
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,18 @@ 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 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_window_coordinates),
Label::<()>::new(self.tip.clone()),
(),
env.clone(),
Expand All @@ -168,7 +169,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
Original file line number Diff line number Diff line change
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

0 comments on commit d650288

Please sign in to comment.