Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ You can find its changes [documented below](#070---2021-01-01).
- RichTextBuilder ([#1520] by [@Maan2003])
- `get_external_handle` on `DelegateCtx` ([#1526] by [@Maan2003])
- `AppLauncher::localization_resources` to use custom l10n resources. ([#1528] by [@edwin0cheng])
- Shell: get_content_insets and mac implementation ([#XXXX] by [@rjwittams])
- Contexts: to_window and to_screen (useful for relatively positioning sub windows) ([#XXXX] by [@rjwittams])
- WindowSizePolicy: allow windows to be sized by their content ([#XXXX] by [@rjwittams])
Copy link
Member

Choose a reason for hiding this comment

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

@rjwittams might want to pr something to fix these up?


### Changed

Expand Down
7 changes: 6 additions & 1 deletion druid-shell/src/platform/gtk/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use gio::ApplicationExt;
use gtk::prelude::*;
use gtk::{AccelGroup, ApplicationWindow, DrawingArea, SettingsExt};

use crate::kurbo::{Point, Rect, Size, Vec2};
use crate::kurbo::{Insets, Point, Rect, Size, Vec2};
use crate::piet::{Piet, PietText, RenderContext};

use crate::common_util::{ClickCounter, IdleCallback};
Expand Down Expand Up @@ -818,6 +818,11 @@ impl WindowHandle {
}
}

pub fn get_content_insets(&self) -> Insets {
log::warn!("WindowHandle::get_content_insets unimplemented for GTK platforms.");
Insets::ZERO
}

pub fn set_level(&self, level: WindowLevel) {
if let Some(state) = self.state.upgrade() {
let hint = match level {
Expand Down
156 changes: 104 additions & 52 deletions druid-shell/src/platform/mac/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use objc::rc::WeakPtr;
use objc::runtime::{Class, Object, Sel};
use objc::{class, msg_send, sel, sel_impl};

use crate::kurbo::{Point, Rect, Size, Vec2};
use crate::kurbo::{Insets, Point, Rect, Size, Vec2};
use crate::piet::{Piet, PietText, RenderContext};

use super::appkit::{
Expand Down Expand Up @@ -127,10 +127,17 @@ pub(crate) struct IdleHandle {
idle_queue: Weak<Mutex<Vec<IdleKind>>>,
}

#[derive(Debug)]
enum DeferredOp {
SetSize(Size),
SetPosition(Point),
}

/// This represents different Idle Callback Mechanism
enum IdleKind {
Callback(Box<dyn IdleCallback>),
Token(IdleToken),
DeferredOp(DeferredOp),
}

/// This is the state associated with our custom NSView.
Expand All @@ -156,7 +163,7 @@ impl WindowBuilder {
handler: None,
title: String::new(),
menu: None,
size: Size::new(500.0, 400.0),
size: Size::new(0., 0.),
Copy link
Contributor

Choose a reason for hiding this comment

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

See issue #1543: because of this change, windows don't appear at all on Mac if their size are not explicitly set.

min_size: None,
position: None,
level: None,
Expand Down Expand Up @@ -220,10 +227,11 @@ impl WindowBuilder {
style_mask |= NSWindowStyleMask::NSResizableWindowMask;
}

let rect = NSRect::new(
NSPoint::new(0., 0.),
NSSize::new(self.size.width, self.size.height),
);
let screen_height = crate::Screen::get_display_rect().height();
let position = self.position.unwrap_or_else(|| Point::new(20., 20.));
let origin = NSPoint::new(position.x, screen_height - position.y - self.size.height); // Flip back

let rect = NSRect::new(origin, NSSize::new(self.size.width, self.size.height));

let window: id = msg_send![WINDOW_CLASS.0, alloc];
let window = window.initWithContentRect_styleMask_backing_defer_(
Expand All @@ -238,7 +246,6 @@ impl WindowBuilder {
window.setContentMinSize_(size);
}

window.cascadeTopLeftFromPoint_(NSPoint::new(20.0, 20.0));
window.setTitle_(make_nsstring(&self.title));

let (view, idle_queue) = make_view(self.handler.expect("view"));
Expand All @@ -260,9 +267,6 @@ impl WindowBuilder {
idle_queue,
};

if let Some(pos) = self.position {
handle.set_position(pos);
}
if let Some(window_state) = self.window_state {
handle.set_window_state(window_state);
}
Expand Down Expand Up @@ -758,6 +762,43 @@ extern "C" fn draw_rect(this: &mut Object, _: Sel, dirtyRect: NSRect) {
}
}

fn run_deferred(this: &mut Object, view_state: &mut ViewState, op: DeferredOp) {
match op {
DeferredOp::SetSize(size) => set_size_deferred(this, view_state, size),
DeferredOp::SetPosition(pos) => set_position_deferred(this, view_state, pos),
}
}

fn set_size_deferred(this: &mut Object, _view_state: &mut ViewState, size: Size) {
unsafe {
let window: id = msg_send![this, window];
let current_frame: NSRect = msg_send![window, frame];
let mut new_frame = current_frame;

// TODO: maintain druid origin (as mac origin is bottom left)
new_frame.origin.y -= size.height - current_frame.size.height;
new_frame.size.width = size.width;
new_frame.size.height = size.height;
let () = msg_send![window, setFrame: new_frame display: YES];
}
}

fn set_position_deferred(this: &mut Object, _view_state: &mut ViewState, position: Point) {
unsafe {
// TODO this should be the max y in orig mac coords

let window: id = msg_send![this, window];
let frame: NSRect = msg_send![window, frame];

let mut new_frame = frame;
new_frame.origin.x = position.x;
let screen_height = crate::Screen::get_display_rect().height();
new_frame.origin.y = screen_height - position.y - frame.size.height; // Flip back
let () = msg_send![window, setFrame: new_frame display: YES];
log::debug!("set_position_deferred {:?}", position);
}
}

extern "C" fn run_idle(this: &mut Object, _: Sel) {
let view_state = unsafe {
let view_state: *mut c_void = *this.get_ivar("viewState");
Expand All @@ -773,6 +814,7 @@ extern "C" fn run_idle(this: &mut Object, _: Sel) {
IdleKind::Token(it) => {
view_state.handler.as_mut().idle(it);
}
IdleKind::DeferredOp(op) => run_deferred(this, view_state, op),
}
}
}
Expand Down Expand Up @@ -1005,17 +1047,7 @@ impl WindowHandle {

// Need to translate mac y coords, as they start from bottom left
pub fn set_position(&self, position: Point) {
unsafe {
// TODO this should be the max y in orig mac coords
let screen_height = crate::Screen::get_display_rect().height();
let window: id = msg_send![*self.nsview.load(), window];
let frame: NSRect = msg_send![window, frame];

let mut new_frame = frame;
new_frame.origin.x = position.x;
new_frame.origin.y = screen_height - position.y - frame.size.height; // Flip back
let () = msg_send![window, setFrame: new_frame display: YES];
}
self.defer(DeferredOp::SetPosition(position))
}

pub fn get_position(&self) -> Point {
Expand All @@ -1033,6 +1065,34 @@ impl WindowHandle {
}
}

pub fn get_content_insets(&self) -> Insets {
unsafe {
let screen_height = crate::Screen::get_display_rect().height();

let window: id = msg_send![*self.nsview.load(), window];
let clr: NSRect = msg_send![window, contentLayoutRect];

let window_frame_r: NSRect = NSWindow::frame(window);
let content_frame_r: NSRect = NSWindow::convertRectToScreen_(window, clr);

let window_frame_rk = Rect::from_origin_size(
(
window_frame_r.origin.x,
screen_height - window_frame_r.origin.y - window_frame_r.size.height,
),
(window_frame_r.size.width, window_frame_r.size.height),
);
let content_frame_rk = Rect::from_origin_size(
(
content_frame_r.origin.x,
screen_height - content_frame_r.origin.y - content_frame_r.size.height,
),
(content_frame_r.size.width, content_frame_r.size.height),
);
window_frame_rk - content_frame_rk
}
}

pub fn set_level(&self, level: WindowLevel) {
unsafe {
let level = levels::as_raw_window_level(level);
Expand All @@ -1042,14 +1102,7 @@ impl WindowHandle {
}

pub fn set_size(&self, size: Size) {
unsafe {
let window: id = msg_send![*self.nsview.load(), window];
let current_frame: NSRect = msg_send![window, frame];
let mut new_frame = current_frame;
new_frame.size.width = size.width;
new_frame.size.height = size.height;
let () = msg_send![window, setFrame: new_frame display: YES];
}
self.defer(DeferredOp::SetSize(size));
}

pub fn get_size(&self) -> Size {
Expand Down Expand Up @@ -1132,6 +1185,12 @@ impl WindowHandle {
}
}

fn defer(&self, op: DeferredOp) {
if let Some(i) = self.get_idle_handle() {
i.add_idle(IdleKind::DeferredOp(op))
}
}

/// Get a handle that can be used to schedule an idle task.
pub fn get_idle_handle(&self) -> Option<IdleHandle> {
if self.nsview.load().is_null() {
Expand All @@ -1154,16 +1213,7 @@ impl WindowHandle {
unsafe impl Send for IdleHandle {}

impl IdleHandle {
/// Add an idle handler, which is called (once) when the message loop
/// is empty. The idle handler will be run from the main UI thread, and
/// won't be scheduled if the associated view has been dropped.
///
/// Note: the name "idle" suggests that it will be scheduled with a lower
/// priority than other UI events, but that's not necessarily the case.
pub fn add_idle_callback<F>(&self, callback: F)
where
F: FnOnce(&dyn Any) + Send + 'static,
{
fn add_idle(&self, idle: IdleKind) {
if let Some(queue) = self.idle_queue.upgrade() {
let mut queue = queue.lock().expect("queue lock");
if queue.is_empty() {
Expand All @@ -1174,23 +1224,25 @@ impl IdleHandle {
withObject: nil waitUntilDone: NO);
}
}
queue.push(IdleKind::Callback(Box::new(callback)));
queue.push(idle);
}
}

/// Add an idle handler, which is called (once) when the message loop
/// is empty. The idle handler will be run from the main UI thread, and
/// won't be scheduled if the associated view has been dropped.
///
/// Note: the name "idle" suggests that it will be scheduled with a lower
/// priority than other UI events, but that's not necessarily the case.
pub fn add_idle_callback<F>(&self, callback: F)
where
F: FnOnce(&dyn Any) + Send + 'static,
{
self.add_idle(IdleKind::Callback(Box::new(callback)));
}

pub fn add_idle_token(&self, token: IdleToken) {
if let Some(queue) = self.idle_queue.upgrade() {
let mut queue = queue.lock().expect("queue lock");
if queue.is_empty() {
unsafe {
let nsview = self.nsview.load();
// Note: the nsview might be nil here if the window has been dropped, but that's ok.
let () = msg_send!(*nsview, performSelectorOnMainThread: sel!(runIdle)
withObject: nil waitUntilDone: NO);
}
}
queue.push(IdleKind::Token(token));
}
self.add_idle(IdleKind::Token(token));
}
}

Expand Down
7 changes: 6 additions & 1 deletion druid-shell/src/platform/web/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use instant::Instant;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;

use crate::kurbo::{Point, Rect, Size, Vec2};
use crate::kurbo::{Insets, Point, Rect, Size, Vec2};

use crate::piet::{PietText, RenderContext};

Expand Down Expand Up @@ -477,6 +477,11 @@ impl WindowHandle {
Size::new(0.0, 0.0)
}

pub fn get_content_insets(&self) -> Insets {
log::warn!("WindowHandle::get_content_insets unimplemented for web.");
Insets::ZERO
}

pub fn set_window_state(&self, _state: window::WindowState) {
log::warn!("WindowHandle::set_window_state unimplemented for web.");
}
Expand Down
7 changes: 6 additions & 1 deletion druid-shell/src/platform/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use winapi::um::winuser::*;
use piet_common::d2d::{D2DFactory, DeviceContext};
use piet_common::dwrite::DwriteFactory;

use crate::kurbo::{Point, Rect, Size, Vec2};
use crate::kurbo::{Insets, Point, Rect, Size, Vec2};
use crate::piet::{Piet, PietText, RenderContext};

use super::accels::register_accel;
Expand Down Expand Up @@ -1657,6 +1657,11 @@ impl WindowHandle {
Point::new(0.0, 0.0)
}

pub fn get_content_insets(&self) -> Insets {
log::warn!("WindowHandle::get_content_insets unimplemented for windows.");
Insets::ZERO
}

// Sets the size of the window in DP
pub fn set_size(&self, size: Size) {
self.defer(DeferredOp::SetSize(size));
Expand Down
7 changes: 6 additions & 1 deletion druid-shell/src/platform/x11/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ use crate::common_util::IdleCallback;
use crate::dialog::FileDialogOptions;
use crate::error::Error as ShellError;
use crate::keyboard::{KeyEvent, KeyState, Modifiers};
use crate::kurbo::{Point, Rect, Size, Vec2};
use crate::kurbo::{Insets, Point, Rect, Size, Vec2};
use crate::mouse::{Cursor, CursorDesc, MouseButton, MouseButtons, MouseEvent};
use crate::piet::{Piet, PietText, RenderContext};
use crate::region::Region;
Expand Down Expand Up @@ -1404,6 +1404,11 @@ impl WindowHandle {
Point::new(0.0, 0.0)
}

pub fn get_content_insets(&self) -> Insets {
log::warn!("WindowHandle::get_content_insets unimplemented for X11 platforms.");
Insets::ZERO
}

pub fn set_level(&self, _level: WindowLevel) {
log::warn!("WindowHandle::set_level is currently unimplemented for X11 platforms.");
}
Expand Down
10 changes: 8 additions & 2 deletions druid-shell/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use crate::common_util::Counter;
use crate::dialog::{FileDialogOptions, FileInfo};
use crate::error::Error;
use crate::keyboard::KeyEvent;
use crate::kurbo::{Point, Rect, Size};
use crate::kurbo::{Insets, Point, Rect, Size};
use crate::menu::Menu;
use crate::mouse::{Cursor, CursorDesc, MouseEvent};
use crate::platform::window as platform;
Expand Down Expand Up @@ -194,12 +194,18 @@ impl WindowHandle {
self.0.set_position(position.into())
}

/// Returns the position of the window in [pixels](crate::Scale), relative to the origin of the
/// Returns the position of the top left corner of the window in [pixels](crate::Scale), relative to the origin of the
/// virtual screen.
pub fn get_position(&self) -> Point {
self.0.get_position()
}

/// Returns the insets of the window content from its position and size in [pixels](crate::Scale).
/// This is to account for any window system provided chrome, eg. title bars.
pub fn get_content_insets(&self) -> Insets {
self.0.get_content_insets()
}

/// Set the window's size in [display points](crate::Scale).
///
/// The actual window size in pixels will depend on the platform DPI settings.
Expand Down
1 change: 1 addition & 0 deletions druid/examples/hello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub fn main() {

// start the application
AppLauncher::with_window(main_window)
.use_simple_logger()
.launch(initial_state)
.expect("Failed to launch application");
}
Expand Down
Loading