Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make most window types have a child window #1919

Merged
merged 15 commits into from
Sep 18, 2021
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,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])

### Deprecated

Expand Down Expand Up @@ -775,6 +777,7 @@ Last release without a changelog :(
[#1884]: https://github.com/linebender/druid/pull/1884
[#1885]: https://github.com/linebender/druid/pull/1885
[#1886]: https://github.com/linebender/druid/pull/1886
[#1919]: https://github.com/linebender/druid/pull/1919

[Unreleased]: https://github.com/linebender/druid/compare/v0.7.0...master
[0.7.0]: https://github.com/linebender/druid/compare/v0.6.0...v0.7.0
Expand Down
118 changes: 74 additions & 44 deletions druid-shell/src/backend/gtk/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ 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.
Expand Down Expand Up @@ -180,6 +180,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 @@ -283,7 +294,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.get_window() {
window.set_override_redirect(override_redirect);
}
}

let state = WindowState {
window,
scale: Cell::new(scale),
area: Cell::new(area),
Expand All @@ -301,7 +353,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 @@ -316,12 +371,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 @@ -724,10 +777,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 @@ -894,7 +943,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 @@ -904,7 +961,12 @@ impl WindowHandle {
pub fn get_position(&self) -> Point {
if let Some(state) = self.state.upgrade() {
let (x, y) = state.window.get_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 @@ -934,38 +996,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.get_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
55 changes: 47 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 @@ -104,6 +104,14 @@ pub(crate) struct WindowHandle {
idle_queue: Weak<Mutex<Vec<IdleKind>>>,
}

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 {
WindowHandle {
Expand Down Expand Up @@ -159,6 +167,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 +299,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 +548,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 +1214,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 collin can get this into a state where modal windows follow the parent?
JAicewizard marked this conversation as resolved.
Show resolved Hide resolved
// There is an API to do child windows, (https://developer.apple.com/documentation/appkit/nswindow/1419152-addchildwindow)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure you can move the parent while a modal is presented?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tooltips and dropdowns suffer from the same issue, we expect them to be closed when moving a window but ATM that is upto the user to program. It might still be worth implementing for those? if not Ill remove the comment.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ATM that is upto the user to program.

is it possible? We don't have any windowmove events

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uhh oh. If I am going to be implementing proper parent tracking, I can only do this for GTK and with some research probably X11. Others will have to help me on different platforms.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should give windowmove events and then the user should do whatever they like (move with the window or close the popup)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, lets put that in another MR to not bloat this one.

// 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 +1240,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 +1284,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
4 changes: 0 additions & 4 deletions druid-shell/src/backend/web/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,10 +479,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