Skip to content

Commit

Permalink
make most window types have a child window (#1919)
Browse files Browse the repository at this point in the history
Also adds equality to WindowHandle
  • Loading branch information
JAicewizard authored Sep 18, 2021
1 parent f2e78ab commit 53321e2
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 97 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ You can find its changes [documented below](#070---2021-01-01).
- Move macOS only function to Mac extension trait ([#1863] by [@Maan2003])
- x11: Only query atoms once instead of per window ([#1865] by [@psychon])
- remove prefix from platform extension traits ([#1873] by [@Maan2003])
- Remove `set_level` on windows ([#1919] by [@JAicewizard])
- Add parent windows to non-main windows. (Coordinate space is now from their origin) ([#1919] by [@JAicewizard])
- `ListIter` implementations for `Arc<Vec<T>>`, `(S, Arc<Vec<T>>)`, `Arc<VecDequeue<T>>` and `(S, Arc<VecDequeue<T>>)` ([#1967] by [@xarvic])

### Deprecated
Expand Down Expand Up @@ -788,6 +790,7 @@ Last release without a changelog :(
[#1885]: https://github.com/linebender/druid/pull/1885
[#1886]: https://github.com/linebender/druid/pull/1886
[#1907]: https://github.com/linebender/druid/pull/1907
[#1919]: https://github.com/linebender/druid/pull/1919
[#1929]: https://github.com/linebender/druid/pull/1929
[#1947]: https://github.com/linebender/druid/pull/1947
[#1967]: https://github.com/linebender/druid/pull/1967
Expand Down
129 changes: 85 additions & 44 deletions druid-shell/src/backend/gtk/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,24 @@ macro_rules! clone {
);
}

#[derive(Clone, Default)]
#[derive(Clone, Default, Debug)]
pub struct WindowHandle {
pub(crate) state: Weak<WindowState>,
// Ensure that we don't implement Send, because it isn't actually safe to send the WindowState.
marker: std::marker::PhantomData<*const ()>,
}

impl PartialEq for WindowHandle {
fn eq(&self, other: &Self) -> bool {
match (self.state.upgrade(), other.state.upgrade()) {
(None, None) => true,
(Some(s), Some(o)) => std::sync::Arc::ptr_eq(&s, &o),
(_, _) => false,
}
}
}
impl Eq for WindowHandle {}

#[cfg(feature = "raw-win-handle")]
unsafe impl HasRawWindowHandle for WindowHandle {
fn raw_window_handle(&self) -> RawWindowHandle {
Expand Down Expand Up @@ -191,6 +202,17 @@ pub(crate) struct WindowState {

request_animation: Cell<bool>,
in_draw: Cell<bool>,

parent: Option<crate::WindowHandle>,
}

impl std::fmt::Debug for WindowState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.write_str("WindowState{")?;
self.window.fmt(f)?;
f.write_str("}")?;
Ok(())
}
}

#[derive(Clone, PartialEq)]
Expand Down Expand Up @@ -293,7 +315,48 @@ impl WindowBuilder {
window.add(&vbox);
let drawing_area = gtk::DrawingArea::new();

let win_state = Arc::new(WindowState {
// Set the parent widget and handle level specific code
let mut parent: Option<crate::WindowHandle> = None;
if let Some(level) = &self.level {
let hint = match level {
WindowLevel::AppWindow => WindowTypeHint::Normal,
WindowLevel::Tooltip(_) => WindowTypeHint::Tooltip,
WindowLevel::DropDown(_) => WindowTypeHint::DropdownMenu,
WindowLevel::Modal(_) => WindowTypeHint::Dialog,
};

window.set_type_hint(hint);

match &level {
WindowLevel::Tooltip(p) => {
parent = Some(p.clone());
}
WindowLevel::DropDown(p) => {
parent = Some(p.clone());
}
WindowLevel::Modal(p) => {
parent = Some(p.clone());
window.set_urgency_hint(true);
window.set_modal(true);
}
_ => (),
};
if let Some(parent) = &parent {
if let Some(parent_state) = parent.0.state.upgrade() {
window.set_transient_for(Some(&parent_state.window));
}
}

let override_redirect = match level {
WindowLevel::AppWindow => false,
WindowLevel::Tooltip(_) | WindowLevel::DropDown(_) | WindowLevel::Modal(_) => true,
};
if let Some(window) = window.window() {
window.set_override_redirect(override_redirect);
}
}

let state = WindowState {
window,
scale: Cell::new(scale),
area: Cell::new(area),
Expand All @@ -311,7 +374,10 @@ impl WindowBuilder {
deferred_queue: RefCell::new(Vec::new()),
request_animation: Cell::new(false),
in_draw: Cell::new(false),
});
parent,
};

let win_state = Arc::new(state);

self.app
.gtk_app()
Expand All @@ -326,12 +392,10 @@ impl WindowBuilder {
state: Arc::downgrade(&win_state),
marker: std::marker::PhantomData,
};
if let Some(level) = self.level {
handle.set_level(level);
}
if let Some(pos) = self.position {
handle.set_position(pos);
}

if let Some(state) = self.state {
handle.set_window_state(state)
}
Expand Down Expand Up @@ -735,10 +799,6 @@ impl WindowBuilder {
.expect("realize didn't create window")
.set_event_compression(false);

if let Some(level) = self.level {
handle.set_override_redirect(level);
}

let size = self.size;
win_state.with_handler(|h| {
h.connect(&handle.clone().into());
Expand Down Expand Up @@ -905,7 +965,15 @@ impl WindowHandle {
}
}

pub fn set_position(&self, position: Point) {
pub fn set_position(&self, mut position: Point) {
// TODO: Make the window follow the parent.
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)
}
};

if let Some(state) = self.state.upgrade() {
let px = position.to_px(state.scale.get());
state.window.move_(px.x as i32, px.y as i32)
Expand All @@ -915,7 +983,12 @@ impl WindowHandle {
pub fn get_position(&self) -> Point {
if let Some(state) = self.state.upgrade() {
let (x, y) = state.window.position();
Point::new(x as f64, y as f64).to_dp(state.scale.get())
let mut position = Point::new(x as f64, y as f64);
if let Some(parent_state) = &state.parent {
let pos = (*parent_state).get_position();
position -= (pos.x, pos.y)
}
position.to_dp(state.scale.get())
} else {
Point::new(0.0, 0.0)
}
Expand Down Expand Up @@ -945,38 +1018,6 @@ impl WindowHandle {
}
}

pub fn set_level(&self, level: WindowLevel) {
if let Some(state) = self.state.upgrade() {
let hint = match level {
WindowLevel::AppWindow => WindowTypeHint::Normal,
WindowLevel::Tooltip => WindowTypeHint::Tooltip,
WindowLevel::DropDown => WindowTypeHint::DropdownMenu,
WindowLevel::Modal => WindowTypeHint::Dialog,
};

state.window.set_type_hint(hint);
}

self.set_override_redirect(level);
}

/// The override-redirect flag tells the window manager not to mess with the window; it should
/// be set for things like tooltips, dropdowns, etc.
///
/// Note that this is exposed on the GDK window, so we can't set it until the GTK window is
/// realized.
fn set_override_redirect(&self, level: WindowLevel) {
let override_redirect = match level {
WindowLevel::AppWindow => false,
WindowLevel::Tooltip | WindowLevel::DropDown | WindowLevel::Modal => true,
};
if let Some(state) = self.state.upgrade() {
if let Some(window) = state.window.window() {
window.set_override_redirect(override_redirect);
}
}
}

pub fn set_size(&self, size: Size) {
if let Some(state) = self.state.upgrade() {
let px = size.to_px(state.scale.get());
Expand Down
65 changes: 57 additions & 8 deletions druid-shell/src/backend/mac/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ mod levels {
use WindowLevel::*;
match window_level {
AppWindow => NSNormalWindowLevel,
Tooltip => NSFloatingWindowLevel,
DropDown => NSFloatingWindowLevel,
Modal => NSModalPanelWindowLevel,
Tooltip(_) => NSFloatingWindowLevel,
DropDown(_) => NSFloatingWindowLevel,
Modal(_) => NSModalPanelWindowLevel,
}
}
}
Expand All @@ -103,6 +103,24 @@ pub(crate) struct WindowHandle {
nsview: WeakPtr,
idle_queue: Weak<Mutex<Vec<IdleKind>>>,
}
impl PartialEq for WindowHandle {
fn eq(&self, other: &Self) -> bool {
match (self.idle_queue.upgrade(), other.idle_queue.upgrade()) {
(None, None) => true,
(Some(s), Some(o)) => std::sync::Arc::ptr_eq(&s, &o),
(_, _) => false,
}
}
}
impl Eq for WindowHandle {}

impl std::fmt::Debug for WindowHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.write_str("WindowHandle{\n")?;
f.write_str("}")?;
Ok(())
}
}

impl Default for WindowHandle {
fn default() -> Self {
Expand Down Expand Up @@ -159,6 +177,7 @@ struct ViewState {
keyboard_state: KeyboardState,
text: PietText,
active_text_input: Option<TextFieldToken>,
parent: Option<crate::WindowHandle>,
}

#[derive(Clone, PartialEq)]
Expand Down Expand Up @@ -290,7 +309,13 @@ impl WindowBuilder {
}

if let Some(level) = self.level {
handle.set_level(level)
match &level {
WindowLevel::Tooltip(parent) => (*view_state).parent = Some(parent.clone()),
WindowLevel::DropDown(parent) => (*view_state).parent = Some(parent.clone()),
WindowLevel::Modal(parent) => (*view_state).parent = Some(parent.clone()),
_ => {}
}
handle.set_level(level);
}

// set_window_state above could have invalidated the frame size
Expand Down Expand Up @@ -533,6 +558,7 @@ fn make_view(handler: Box<dyn WinHandler>) -> (id, Weak<Mutex<Vec<IdleKind>>>) {
keyboard_state,
text: PietText::new_with_unique_state(),
active_text_input: None,
parent: None,
};
let state_ptr = Box::into_raw(Box::new(state));
(*view).set_ivar("viewState", state_ptr as *mut c_void);
Expand Down Expand Up @@ -1198,7 +1224,21 @@ impl WindowHandle {
pub fn show_titlebar(&self, _show_titlebar: bool) {}

// Need to translate mac y coords, as they start from bottom left
pub fn set_position(&self, position: Point) {
pub fn set_position(&self, mut position: Point) {
// TODO: Maybe @cmyr can get this into a state where modal windows follow the parent?
// There is an API to do child windows, (https://developer.apple.com/documentation/appkit/nswindow/1419152-addchildwindow)
// but I have no good way of testing and making sure this works.
unsafe {
if let Some(view) = self.nsview.load().as_ref() {
let state: *mut c_void = *view.get_ivar("viewState");
let state = &mut (*(state as *mut ViewState));
if let Some(parent_state) = &state.parent {
let pos = (*parent_state).get_position();
position += (pos.x, pos.y)
}
}
}

self.defer(DeferredOp::SetPosition(position))
}

Expand All @@ -1210,10 +1250,19 @@ impl WindowHandle {
let window: id = msg_send![*self.nsview.load(), window];
let current_frame: NSRect = msg_send![window, frame];

Point::new(
let mut position = Point::new(
current_frame.origin.x,
screen_height - current_frame.origin.y - current_frame.size.height,
)
);
if let Some(view) = self.nsview.load().as_ref() {
let state: *mut c_void = *view.get_ivar("viewState");
let state = &mut (*(state as *mut ViewState));
if let Some(parent_state) = &state.parent {
let pos = (*parent_state).get_position();
position -= (pos.x, pos.y)
}
}
position
}
}

Expand Down Expand Up @@ -1245,7 +1294,7 @@ impl WindowHandle {
}
}

pub fn set_level(&self, level: WindowLevel) {
fn set_level(&self, level: WindowLevel) {
unsafe {
let level = levels::as_raw_window_level(level);
let window: id = msg_send![*self.nsview.load(), window];
Expand Down
14 changes: 10 additions & 4 deletions druid-shell/src/backend/web/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ pub(crate) struct WindowBuilder {

#[derive(Clone, Default)]
pub struct WindowHandle(Weak<WindowState>);
impl PartialEq for WindowHandle {
fn eq(&self, other: &Self) -> bool {
match (self.0.upgrade(), other.0.upgrade()) {
(None, None) => true,
(Some(s), Some(o)) => std::rc::Rc::ptr_eq(&s, &o),
(_, _) => false,
}
}
}
impl Eq for WindowHandle {}

#[cfg(feature = "raw-win-handle")]
unsafe impl HasRawWindowHandle for WindowHandle {
Expand Down Expand Up @@ -479,10 +489,6 @@ impl WindowHandle {
warn!("WindowHandle::set_position unimplemented for web");
}

pub fn set_level(&self, _level: WindowLevel) {
warn!("WindowHandle::set_level is currently unimplemented for web.");
}

pub fn get_position(&self) -> Point {
warn!("WindowHandle::get_position unimplemented for web.");
Point::new(0.0, 0.0)
Expand Down
Loading

0 comments on commit 53321e2

Please sign in to comment.