diff --git a/CHANGELOG.md b/CHANGELOG.md index de57a14c7b..a607a41df5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,10 +42,12 @@ You can find its changes [documented below](#070---2021-01-01). - Text input handles Delete key ([#1746] by [@bjorn]) - `lens` macro can access nested fields ([#1764] by [@Maan2003]) - X11 backend now supports custom cursors ([#1801] by [@psychon]) +- X11: Add support for transparent windows ([#1803] by [@psychon]) +- `has_focus` method on `WidgetPod` ([#1825] by [@ForLoveOfCats]) ### Changed -- Warn on unhandled Commands ([#1533] by [@Maan2003]) +- ~~Warn on unhandled Commands ([#1533] by [@Maan2003])~~ (Reverted in #1813) - `WindowDesc::new` takes the root widget directly instead of a closure ([#1559] by [@lassipulkkinen]) - Switch to trace-based logging ([#1562] by [@PoignardAzur]) - Spacers in `Flex` are now implemented by calculating the space in `Flex` instead of creating a widget for it ([#1584] by [@JAicewizard]) @@ -68,6 +70,7 @@ You can find its changes [documented below](#070---2021-01-01). - `Notification`s will not be delivered to the widget that sends them ([#1640] by [@cmyr]) - `TextBox` can handle standard keyboard shortcuts without needing menus ([#1660] by [@cmyr]) - GTK Shell: Prevent mangling of newline characters in clipboard ([#1695] by [@ForLoveOfCats]) +- GTK: Replaced call to `std::str::from_utf8_unchecked` with `from_utf8` ([#1820] by [@psychon]) - Use correct fill rule when rendering SVG paths ([#1606] by [@SecondFlight]) - Correctly capture and use stroke properties when rendering SVG paths ([#1647] by [@SecondFlight]) - focus-chain now only includes non hidden (`should_propagate_to_hidden()` on `Event` and `Lifecylce`) widgets ([#1724] by [@xarvic]) @@ -728,6 +731,9 @@ Last release without a changelog :( [#1787]: https://github.com/linebender/druid/pull/1787 [#1801]: https://github.com/linebender/druid/pull/1800 [#1802]: https://github.com/linebender/druid/pull/1802 +[#1803]: https://github.com/linebender/druid/pull/1803 +[#1820]: https://github.com/linebender/druid/pull/1820 +[#1825]: https://github.com/linebender/druid/pull/1825 [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 diff --git a/druid-derive/src/attr.rs b/druid-derive/src/attr.rs index 59f7458ed6..5c0b392eeb 100644 --- a/druid-derive/src/attr.rs +++ b/druid-derive/src/attr.rs @@ -52,7 +52,7 @@ pub enum FieldIdent { impl FieldIdent { pub fn unwrap_named(&self) -> syn::Ident { if let FieldIdent::Named(s) = self { - syn::Ident::new(&s, Span::call_site()) + syn::Ident::new(s, Span::call_site()) } else { panic!("Unwrap named called on unnamed FieldIdent"); } @@ -272,7 +272,7 @@ impl Field { impl Field { pub fn ident_tokens(&self) -> TokenTree { match self.ident { - FieldIdent::Named(ref s) => Ident::new(&s, Span::call_site()).into(), + FieldIdent::Named(ref s) => Ident::new(s, Span::call_site()).into(), FieldIdent::Unnamed(num) => Literal::usize_unsuffixed(num).into(), } } diff --git a/druid-derive/src/data.rs b/druid-derive/src/data.rs index d414799d9d..5cc9a41dc2 100644 --- a/druid-derive/src/data.rs +++ b/druid-derive/src/data.rs @@ -87,7 +87,7 @@ fn derive_enum( let impl_generics = generics_bounds(&input.generics); let (_, ty_generics, where_clause) = &input.generics.split_for_impl(); - if is_c_style_enum(&s) { + if is_c_style_enum(s) { let res = quote! { impl<#impl_generics> ::druid::Data for #ident #ty_generics #where_clause { fn same(&self, other: &Self) -> bool { self == other } diff --git a/druid-derive/src/lens.rs b/druid-derive/src/lens.rs index 75b2bbfeaa..4a4e508130 100644 --- a/druid-derive/src/lens.rs +++ b/druid-derive/src/lens.rs @@ -154,7 +154,7 @@ fn derive_struct(input: &syn::DeriveInput) -> Result) -> glib::source::Continue { util::assert_main_thread(); let result = state.with_handler(|handler| { - let queue: Vec<_> = std::mem::replace(&mut state.idle_queue.lock().unwrap(), Vec::new()); + let queue: Vec<_> = std::mem::take(&mut state.idle_queue.lock().unwrap()); for item in queue { match item { diff --git a/druid-shell/src/platform/x11/application.rs b/druid-shell/src/platform/x11/application.rs index b8fbbac8d6..aa380aaa00 100644 --- a/druid-shell/src/platform/x11/application.rs +++ b/druid-shell/src/platform/x11/application.rs @@ -26,7 +26,9 @@ use x11rb::connection::{Connection, RequestConnection}; use x11rb::protocol::present::ConnectionExt as _; use x11rb::protocol::render::{self, ConnectionExt as _, Pictformat}; use x11rb::protocol::xfixes::ConnectionExt as _; -use x11rb::protocol::xproto::{self, ConnectionExt, CreateWindowAux, EventMask, WindowClass}; +use x11rb::protocol::xproto::{ + self, ConnectionExt, CreateWindowAux, EventMask, Visualtype, WindowClass, +}; use x11rb::protocol::Event; use x11rb::resource_manager::Database as ResourceDb; use x11rb::xcb_ffi::XCBConnection; @@ -52,6 +54,11 @@ pub(crate) struct Application { /// `druid_shell::WindowHandle` to be `!Send` and `!Sync`. marker: std::marker::PhantomData<*mut XCBConnection>, + /// The type of visual used by the root window + root_visual_type: Visualtype, + /// The visual for windows with transparent backgrounds, if supported + argb_visual_type: Option, + /// The X11 resource database used to query dpi. pub(crate) rdb: Rc, pub(crate) cursors: Cursors, @@ -189,6 +196,15 @@ impl Application { col_resize: load_cursor("col-resize"), }; + let screen = connection + .setup() + .roots + .get(screen_num as usize) + .ok_or_else(|| anyhow!("Invalid screen num: {}", screen_num))?; + let root_visual_type = util::get_visual_from_screen(&screen) + .ok_or_else(|| anyhow!("Couldn't get visual from screen"))?; + let argb_visual_type = util::get_argb_visual_type(&*connection, &screen)?; + Ok(Application { connection, rdb, @@ -199,6 +215,8 @@ impl Application { cursors, idle_write, present_opcode, + root_visual_type, + argb_visual_type, marker: std::marker::PhantomData, render_argb32_pictformat_cursor, }) @@ -326,6 +344,33 @@ impl Application { self.screen_num } + #[inline] + pub(crate) fn argb_visual_type(&self) -> Option { + // Check if a composite manager is running + let atom_name = format!("_NET_WM_CM_S{}", self.screen_num); + let owner = self + .connection + .intern_atom(false, atom_name.as_bytes()) + .ok() + .and_then(|cookie| cookie.reply().ok()) + .map(|reply| reply.atom) + .and_then(|atom| self.connection.get_selection_owner(atom).ok()) + .and_then(|cookie| cookie.reply().ok()) + .map(|reply| reply.owner); + + if Some(x11rb::NONE) == owner { + tracing::debug!("_NET_WM_CM_Sn selection is unowned, not providing ARGB visual"); + None + } else { + self.argb_visual_type + } + } + + #[inline] + pub(crate) fn root_visual_type(&self) -> Visualtype { + self.root_visual_type + } + /// Returns `Ok(true)` if we want to exit the main loop. fn handle_event(&self, ev: &Event) -> Result { match ev { diff --git a/druid-shell/src/platform/x11/util.rs b/druid-shell/src/platform/x11/util.rs index 2e57d2e640..fb85368a95 100644 --- a/druid-shell/src/platform/x11/util.rs +++ b/druid-shell/src/platform/x11/util.rs @@ -19,8 +19,11 @@ use std::rc::Rc; use std::time::Instant; use anyhow::{anyhow, Error}; +use x11rb::connection::RequestConnection; +use x11rb::errors::ReplyError; use x11rb::protocol::randr::{ConnectionExt, ModeFlag}; -use x11rb::protocol::xproto::{Screen, Visualtype, Window}; +use x11rb::protocol::render::{self, ConnectionExt as _}; +use x11rb::protocol::xproto::{Screen, Visualid, Visualtype, Window}; use x11rb::xcb_ffi::XCBConnection; use crate::window::TimerToken; @@ -69,10 +72,10 @@ pub fn refresh_rate(conn: &Rc, window_id: Window) -> Option } // Apparently you have to get the visualtype this way :| -pub fn get_visual_from_screen(screen: &Screen) -> Option { +fn find_visual_from_screen(screen: &Screen, visual_id: u32) -> Option { for depth in &screen.allowed_depths { for visual in &depth.visuals { - if visual.visual_id == screen.root_visual { + if visual.visual_id == visual_id { return Some(*visual); } } @@ -80,6 +83,65 @@ pub fn get_visual_from_screen(screen: &Screen) -> Option { None } +pub fn get_visual_from_screen(screen: &Screen) -> Option { + find_visual_from_screen(screen, screen.root_visual) +} + +pub fn get_argb_visual_type( + conn: &XCBConnection, + screen: &Screen, +) -> Result, ReplyError> { + fn find_visual_for_format( + reply: &render::QueryPictFormatsReply, + id: render::Pictformat, + ) -> Option { + let find_in_depth = |depth: &render::Pictdepth| { + depth + .visuals + .iter() + .find(|visual| visual.format == id) + .map(|visual| visual.visual) + }; + let find_in_screen = + |screen: &render::Pictscreen| screen.depths.iter().find_map(find_in_depth); + reply.screens.iter().find_map(find_in_screen) + } + + // Getting a visual is already funny, but finding the ARGB32 visual is even more fun. + // RENDER has picture formats. Each format corresponds to a visual. Thus, we first find the + // right picture format, then find the corresponding visual id, then the Visualtype. + if conn + .extension_information(render::X11_EXTENSION_NAME)? + .is_none() + { + // RENDER not supported + Ok(None) + } else { + let pict_formats = conn.render_query_pict_formats()?.reply()?; + // Find the ARGB32 standard format + let res = pict_formats + .formats + .iter() + .find(|format| { + format.type_ == render::PictType::DIRECT + && format.depth == 32 + && format.direct.red_shift == 16 + && format.direct.red_mask == 0xff + && format.direct.green_shift == 8 + && format.direct.green_mask == 0xff + && format.direct.blue_shift == 0 + && format.direct.blue_mask == 0xff + && format.direct.alpha_shift == 24 + && format.direct.alpha_mask == 0xff + }) + // Now find the corresponding visual ID + .and_then(|format| find_visual_for_format(&pict_formats, format.id)) + // And finally, we can find the visual + .and_then(|visual_id| find_visual_from_screen(screen, visual_id)); + Ok(res) + } +} + macro_rules! log_x11 { ($val:expr) => { if let Err(e) = $val { diff --git a/druid-shell/src/platform/x11/window.rs b/druid-shell/src/platform/x11/window.rs index 1d22a305a6..8da8753625 100644 --- a/druid-shell/src/platform/x11/window.rs +++ b/druid-shell/src/platform/x11/window.rs @@ -16,7 +16,7 @@ use std::cell::{Cell, RefCell}; use std::collections::BinaryHeap; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use std::os::unix::io::RawFd; use std::panic::Location; use std::rc::{Rc, Weak}; @@ -30,13 +30,14 @@ use tracing::{error, info, warn}; use x11rb::atom_manager; use x11rb::connection::Connection; use x11rb::errors::ReplyOrIdError; +use x11rb::properties::{WmHints, WmHintsState, WmSizeHints}; use x11rb::protocol::present::{CompleteNotifyEvent, ConnectionExt as _, IdleNotifyEvent}; use x11rb::protocol::render::{ConnectionExt as _, Pictformat}; use x11rb::protocol::xfixes::{ConnectionExt as _, Region as XRegion}; use x11rb::protocol::xproto::{ - self, AtomEnum, ChangeWindowAttributesAux, ConfigureNotifyEvent, ConnectionExt, CreateGCAux, - EventMask, Gcontext, ImageFormat, ImageOrder as X11ImageOrder, Pixmap, PropMode, Rectangle, - Visualtype, WindowClass, + self, AtomEnum, ChangeWindowAttributesAux, ColormapAlloc, ConfigureNotifyEvent, + ConfigureWindowAux, ConnectionExt, CreateGCAux, EventMask, Gcontext, ImageFormat, + ImageOrder as X11ImageOrder, Pixmap, PropMode, Rectangle, Visualtype, WindowClass, }; use x11rb::wrapper::ConnectionExt as _; use x11rb::xcb_ffi::XCBConnection; @@ -62,7 +63,7 @@ use crate::{window, ScaledArea}; use super::application::Application; use super::keycodes; use super::menu::Menu; -use super::util::{self, Timer}; +use super::util::Timer; /// A version of XCB's `xcb_visualtype_t` struct. This was copied from the [example] in x11rb; it /// is used to interoperate with cairo. @@ -99,15 +100,28 @@ impl From for xcb_visualtype_t { } } +fn size_hints(resizable: bool, size: Size, min_size: Size) -> WmSizeHints { + let mut size_hints = WmSizeHints::new(); + if resizable { + size_hints.min_size = Some((min_size.width as i32, min_size.height as i32)); + } else { + size_hints.min_size = Some((size.width as i32, size.height as i32)); + size_hints.max_size = Some((size.width as i32, size.height as i32)); + } + size_hints +} + pub(crate) struct WindowBuilder { app: Application, handler: Option>, title: String, + transparent: bool, + position: Option, size: Size, - - // TODO: implement min_size for X11 - #[allow(dead_code)] min_size: Size, + resizable: bool, + level: WindowLevel, + state: Option, } impl WindowBuilder { @@ -116,8 +130,13 @@ impl WindowBuilder { app, handler: None, title: String::new(), + transparent: false, + position: None, size: Size::new(500.0, 400.0), min_size: Size::new(0.0, 0.0), + resizable: true, + level: WindowLevel::AppWindow, + state: None, } } @@ -126,36 +145,41 @@ impl WindowBuilder { } pub fn set_size(&mut self, size: Size) { - self.size = size; + // zero sized window results in server error + self.size = if size.width == 0. || size.height == 0. { + Size::new(1., 1.) + } else { + size + }; } pub fn set_min_size(&mut self, min_size: Size) { - warn!("WindowBuilder::set_min_size is implemented, but the setting is currently unused for X11 platforms."); self.min_size = min_size; } - pub fn resizable(&mut self, _resizable: bool) { - warn!("WindowBuilder::resizable is currently unimplemented for X11 platforms."); + pub fn resizable(&mut self, resizable: bool) { + self.resizable = resizable; } pub fn show_titlebar(&mut self, _show_titlebar: bool) { + // not sure how to do this, maybe _MOTIF_WM_HINTS? warn!("WindowBuilder::show_titlebar is currently unimplemented for X11 platforms."); } - pub fn set_transparent(&mut self, _transparent: bool) { - // Ignored + pub fn set_transparent(&mut self, transparent: bool) { + self.transparent = transparent; } - pub fn set_position(&mut self, _position: Point) { - warn!("WindowBuilder::set_position is currently unimplemented for X11 platforms."); + pub fn set_position(&mut self, position: Point) { + self.position = Some(position); } - pub fn set_level(&mut self, _level: window::WindowLevel) { - warn!("WindowBuilder::set_level is currently unimplemented for X11 platforms."); + pub fn set_level(&mut self, level: window::WindowLevel) { + self.level = level; } - pub fn set_window_state(&self, _state: window::WindowState) { - warn!("WindowBuilder::set_window_state is currently unimplemented for X11 platforms."); + pub fn set_window_state(&mut self, state: window::WindowState) { + self.state = Some(state); } pub fn set_title>(&mut self, title: S) { @@ -249,11 +273,20 @@ impl WindowBuilder { .roots .get(screen_num as usize) .ok_or_else(|| anyhow!("Invalid screen num: {}", screen_num))?; - let visual_type = util::get_visual_from_screen(&screen) - .ok_or_else(|| anyhow!("Couldn't get visual from screen"))?; - let visual_id = visual_type.visual_id; + let visual_type = if self.transparent { + self.app.argb_visual_type() + } else { + None + }; + let (transparent, visual_type) = match visual_type { + Some(visual) => (true, visual), + None => (false, self.app.root_visual_type()), + }; + if transparent != self.transparent { + warn!("Windows with transparent backgrounds do not work"); + } - let cw_values = xproto::CreateWindowAux::new().event_mask( + let mut cw_values = xproto::CreateWindowAux::new().event_mask( EventMask::EXPOSURE | EventMask::STRUCTURE_NOTIFY | EventMask::KEY_PRESS @@ -262,21 +295,36 @@ impl WindowBuilder { | EventMask::BUTTON_RELEASE | EventMask::POINTER_MOTION, ); + if transparent { + let colormap = conn.generate_id()?; + conn.create_colormap( + ColormapAlloc::NONE, + colormap, + screen.root, + visual_type.visual_id, + )?; + cw_values = cw_values + .border_pixel(screen.white_pixel) + .colormap(colormap); + }; + + let pos = self.position.unwrap_or_default().to_px(scale); // Create the actual window let (width_px, height_px) = (size_px.width as u16, size_px.height as u16); + let depth = if transparent { 32 } else { screen.root_depth }; conn.create_window( // Window depth - x11rb::COPY_FROM_PARENT.try_into().unwrap(), + depth, // The new window's ID id, // Parent window of this new window // TODO(#468): either `screen.root()` (no parent window) or pass parent here to attach screen.root, // X-coordinate of the new window - 0, + pos.x as _, // Y-coordinate of the new window - 0, + pos.y as _, // Width of the new window width_px, // Height of the new window @@ -286,13 +334,17 @@ impl WindowBuilder { // Window class type WindowClass::INPUT_OUTPUT, // Visual ID - visual_id, + visual_type.visual_id, // Window properties mask &cw_values, )? .check() .context("create window")?; + if let Some(colormap) = cw_values.colormap { + conn.free_colormap(colormap)?; + } + // Allocate a graphics context (currently used only for copying pixels when present is // unavailable). let gc = conn.generate_id()?; @@ -316,12 +368,7 @@ impl WindowBuilder { // other one). Otherwise, we only need one. let buf_count = if present_data.is_some() { 2 } else { 1 }; let buffers = RefCell::new(Buffers::new( - conn, - id, - buf_count, - width_px, - height_px, - screen.root_depth, + conn, id, buf_count, width_px, height_px, depth, )?); // Initialize some properties @@ -338,6 +385,50 @@ impl WindowBuilder { .context("set _NET_WM_PID")?; } + let min_size = self.min_size.to_px(scale); + log_x11!(size_hints(self.resizable, size_px, min_size) + .set_normal_hints(conn.as_ref(), id) + .context("set wm normal hints")); + + // TODO: set _NET_WM_STATE + let mut hints = WmHints::new(); + if let Some(state) = self.state { + hints.initial_state = Some(match state { + window::WindowState::Maximized => WmHintsState::Normal, + window::WindowState::Minimized => WmHintsState::Iconic, + window::WindowState::Restored => WmHintsState::Normal, + }); + } + log_x11!(hints.set(conn.as_ref(), id).context("set wm hints")); + + // set level + { + let window_type = match self.level { + WindowLevel::AppWindow => atoms._NET_WM_WINDOW_TYPE_NORMAL, + WindowLevel::Tooltip => atoms._NET_WM_WINDOW_TYPE_TOOLTIP, + WindowLevel::Modal => atoms._NET_WM_WINDOW_TYPE_DIALOG, + WindowLevel::DropDown => atoms._NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + }; + + let conn = self.app.connection(); + log_x11!(conn.change_property32( + xproto::PropMode::REPLACE, + id, + atoms._NET_WM_WINDOW_TYPE, + AtomEnum::ATOM, + &[window_type], + )); + if matches!( + self.level, + WindowLevel::DropDown | WindowLevel::Modal | WindowLevel::Tooltip + ) { + log_x11!(conn.change_window_attributes( + id, + &ChangeWindowAttributesAux::new().override_redirect(1), + )); + } + } + let window = Rc::new(Window { id, gc, @@ -347,6 +438,7 @@ impl WindowBuilder { atoms, area: Cell::new(ScaledArea::from_px(size_px, scale)), scale: Cell::new(scale), + min_size, invalid: RefCell::new(Region::EMPTY), destroyed: Cell::new(false), timer_queue: Mutex::new(BinaryHeap::new()), @@ -356,7 +448,11 @@ impl WindowBuilder { buffers, active_text_field: Cell::new(None), }); + window.set_title(&self.title); + if let Some(pos) = self.position { + window.set_position(pos); + } let handle = WindowHandle::new(id, Rc::downgrade(&window)); window.connect(handle.clone())?; @@ -426,6 +522,8 @@ pub(crate) struct Window { atoms: WindowAtoms, area: Cell, scale: Cell, + // min size in px + min_size: Size, /// We've told X11 to destroy this window, so don't so any more X requests with this window id. destroyed: Cell, /// The region that was invalidated since the last time we rendered. @@ -512,6 +610,11 @@ atom_manager! { _NET_WM_PID, _NET_WM_NAME, UTF8_STRING, + _NET_WM_WINDOW_TYPE, + _NET_WM_WINDOW_TYPE_NORMAL, + _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + _NET_WM_WINDOW_TYPE_TOOLTIP, + _NET_WM_WINDOW_TYPE_DIALOG, } } @@ -629,7 +732,7 @@ impl Window { } // note: size is in px - fn set_size(&self, size: Size) -> Result<(), Error> { + fn size_changed(&self, size: Size) -> Result<(), Error> { let scale = self.scale.get(); let new_size = { if size != self.area.get().size_px() { @@ -765,8 +868,11 @@ impl Window { } /// Set whether the window should be resizable - fn resizable(&self, _resizable: bool) { - warn!("Window::resizeable is currently unimplemented for X11 platforms."); + fn resizable(&self, resizable: bool) { + let conn = self.app.connection().as_ref(); + log_x11!(size_hints(resizable, self.size().size_px(), self.min_size) + .set_normal_hints(conn, self.id) + .context("set normal hints")); } /// Set whether the window should show titlebar @@ -774,6 +880,43 @@ impl Window { warn!("Window::show_titlebar is currently unimplemented for X11 platforms."); } + fn get_position(&self) -> Point { + fn _get_position(window: &Window) -> Result { + let conn = window.app.connection(); + let scale = window.scale.get(); + let geom = conn.get_geometry(window.id)?.reply()?; + let cord = conn + .translate_coordinates(window.id, geom.root, 0, 0)? + .reply()?; + Ok(Point::new(cord.dst_x as _, cord.dst_y as _).to_dp(scale)) + } + let pos = _get_position(self); + log_x11!(&pos); + pos.unwrap_or_default() + } + + fn set_position(&self, pos: Point) { + let conn = self.app.connection(); + let scale = self.scale.get(); + let pos = pos.to_px(scale).expand(); + log_x11!(conn.configure_window( + self.id, + &ConfigureWindowAux::new().x(pos.x as i32).y(pos.y as i32), + )); + } + + fn set_size(&self, size: Size) { + let conn = self.app.connection(); + let scale = self.scale.get(); + let size = size.to_px(scale).expand(); + log_x11!(conn.configure_window( + self.id, + &ConfigureWindowAux::new() + .width(size.width as u32) + .height(size.height as u32), + )); + } + /// Bring this window to the front of the window stack and give it focus. fn bring_to_front_and_focus(&self) { if self.destroyed() { @@ -1056,7 +1199,7 @@ impl Window { } pub fn handle_configure_notify(&self, event: &ConfigureNotifyEvent) -> Result<(), Error> { - self.set_size(Size::new(event.width as f64, event.height as f64)) + self.size_changed(Size::new(event.width as f64, event.height as f64)) } pub fn handle_complete_notify(&self, event: &CompleteNotifyEvent) -> Result<(), Error> { @@ -1483,13 +1626,21 @@ impl WindowHandle { } } - pub fn set_position(&self, _position: Point) { - warn!("WindowHandle::set_position is currently unimplemented for X11 platforms."); + pub fn set_position(&self, position: Point) { + if let Some(w) = self.window.upgrade() { + w.set_position(position); + } else { + error!("Window {} has already been dropped", self.id); + } } pub fn get_position(&self) -> Point { - warn!("WindowHandle::get_position is currently unimplemented for X11 platforms."); - Point::new(0.0, 0.0) + if let Some(w) = self.window.upgrade() { + w.get_position() + } else { + error!("Window {} has already been dropped", self.id); + Point::new(0.0, 0.0) + } } pub fn content_insets(&self) -> Insets { @@ -1498,16 +1649,24 @@ impl WindowHandle { } pub fn set_level(&self, _level: WindowLevel) { - warn!("WindowHandle::set_level is currently unimplemented for X11 platforms."); + warn!("WindowHandle::set_level unimplemented for X11 platforms."); } - pub fn set_size(&self, _size: Size) { - warn!("WindowHandle::set_size is currently unimplemented for X11 platforms."); + pub fn set_size(&self, size: Size) { + if let Some(w) = self.window.upgrade() { + w.set_size(size); + } else { + error!("Window {} has already been dropped", self.id); + } } pub fn get_size(&self) -> Size { - warn!("WindowHandle::get_size is currently unimplemented for X11 platforms."); - Size::new(0.0, 0.0) + if let Some(w) = self.window.upgrade() { + w.size().size_dp() + } else { + error!("Window {} has already been dropped", self.id); + Size::ZERO + } } pub fn set_window_state(&self, _state: window::WindowState) { diff --git a/druid/examples/event_viewer.rs b/druid/examples/event_viewer.rs index 44ead7e7e3..1b4dd7958f 100644 --- a/druid/examples/event_viewer.rs +++ b/druid/examples/event_viewer.rs @@ -42,7 +42,7 @@ const PROPERTIES: &[(&str, f64)] = &[ ("Location", 60.0), ]; -#[allow(clippy::clippy::rc_buffer)] +#[allow(clippy::rc_buffer)] #[derive(Clone, Data, Lens)] struct AppState { /// The text in the text field diff --git a/druid/examples/game_of_life.rs b/druid/examples/game_of_life.rs index d302ce5837..56080c56bb 100644 --- a/druid/examples/game_of_life.rs +++ b/druid/examples/game_of_life.rs @@ -37,7 +37,7 @@ static COLOURS: ColorScheme = &[ Color::rgb8(0xE0, 0xAF, 0xAF), //Color::rgb(224,175,175) ]; -#[allow(clippy::clippy::rc_buffer)] +#[allow(clippy::rc_buffer)] #[derive(Clone, Data, PartialEq)] struct Grid { storage: Arc>, diff --git a/druid/src/command.rs b/druid/src/command.rs index 305e25ad48..feadb24cc6 100644 --- a/druid/src/command.rs +++ b/druid/src/command.rs @@ -25,18 +25,7 @@ use crate::{WidgetId, WindowId}; /// The identity of a [`Selector`]. /// /// [`Selector`]: struct.Selector.html -#[derive(Copy, Clone, PartialEq, Eq)] -pub(crate) struct SelectorSymbol { - str: &'static str, - must_use: bool, -} - -impl std::fmt::Debug for SelectorSymbol { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let must_use = if self.must_use { " (must_use)" } else { "" }; - write!(f, "{}{}", self.str, must_use) - } -} +pub(crate) type SelectorSymbol = &'static str; /// An identifier for a particular command. /// @@ -353,25 +342,8 @@ impl Selector<()> { impl Selector { /// Create a new `Selector` with the given string. - pub const fn new(str: &'static str) -> Selector { - Selector( - SelectorSymbol { - str, - must_use: false, - }, - PhantomData, - ) - } - - /// Create a `Selector` that must be used. - pub const fn must_use(str: &'static str) -> Selector { - Selector( - SelectorSymbol { - str, - must_use: true, - }, - PhantomData, - ) + pub const fn new(s: &'static str) -> Selector { + Selector(s, PhantomData) } /// Returns the `SelectorSymbol` identifying this `Selector`. @@ -417,11 +389,6 @@ impl Command { .default_to(Target::Global) } - /// Checks if this command must be used. - pub fn must_be_used(&self) -> bool { - self.symbol.must_use - } - /// A helper method for creating a `Notification` from a `Command`. /// /// This is slightly icky; it lets us do `SOME_SELECTOR.with(SOME_PAYLOAD)` @@ -446,14 +413,6 @@ impl Command { self } - /// Make the `Command` must use. - /// - /// this will log warning if this `Command` is not handled. - pub fn must_use(mut self, must_use: bool) -> Self { - self.symbol.must_use = must_use; - self - } - /// Set the correct default target when target is `Auto`. pub(crate) fn default_to(mut self, target: Target) -> Self { self.target.default(target); @@ -492,7 +451,7 @@ impl Command { if self.symbol == selector.symbol() { Some(self.payload.downcast_ref().unwrap_or_else(|| { panic!( - "The selector {:?} exists twice with different types. See druid::Command::get for more information", + "The selector \"{}\" exists twice with different types. See druid::Command::get for more information", selector.symbol() ); })) @@ -518,7 +477,7 @@ impl Command { pub fn get_unchecked(&self, selector: Selector) -> &T { self.get(selector).unwrap_or_else(|| { panic!( - "Expected selector {:?} but the command was {:?}.", + "Expected selector \"{}\" but the command was \"{}\".", selector.symbol(), self.symbol ) @@ -545,7 +504,7 @@ impl Notification { if self.symbol == selector.symbol() { Some(self.payload.downcast_ref().unwrap_or_else(|| { panic!( - "The selector {:?} exists twice with different types. \ + "The selector \"{}\" exists twice with different types. \ See druid::Command::get for more information", selector.symbol() ); @@ -587,7 +546,7 @@ impl From for Command { impl std::fmt::Display for Selector { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Selector({:?}, {})", self.0, any::type_name::()) + write!(f, "Selector(\"{}\", {})", self.0, any::type_name::()) } } @@ -637,7 +596,7 @@ impl std::fmt::Debug for Notification { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, - "Notification: Selector {:?} from {:?}", + "Notification: Selector {} from {:?}", self.symbol, self.source ) } diff --git a/druid/src/contexts.rs b/druid/src/contexts.rs index 98f2cb4a19..dfc94ff143 100644 --- a/druid/src/contexts.rs +++ b/druid/src/contexts.rs @@ -154,7 +154,7 @@ impl_context_method!( /// Returns a reference to the current `WindowHandle`. pub fn window(&self) -> &WindowHandle { - &self.state.window + self.state.window } /// Get the `WindowId` of the current window. diff --git a/druid/src/core.rs b/druid/src/core.rs index 054d156eb7..c7fcaba065 100644 --- a/druid/src/core.rs +++ b/druid/src/core.rs @@ -73,7 +73,7 @@ pub struct WidgetPod { /// [`paint`]: trait.Widget.html#tymethod.paint /// [`WidgetPod`]: struct.WidgetPod.html #[derive(Clone)] -pub(crate) struct WidgetState { +pub struct WidgetState { pub(crate) id: WidgetId, /// The size of the child; this is the value returned by the child's layout /// method. @@ -220,6 +220,11 @@ impl> WidgetPod { self.old_data.is_some() } + /// Returns `true` if widget or any descendent is focused + pub fn has_focus(&self) -> bool { + self.state.has_focus + } + /// Query the "active" state of the widget. pub fn is_active(&self) -> bool { self.state.is_active @@ -840,7 +845,7 @@ impl> WidgetPod { ctx.is_handled = true } _ => { - self.inner.event(&mut inner_ctx, &inner_event, data, env); + self.inner.event(&mut inner_ctx, inner_event, data, env); inner_ctx.widget_state.has_active |= inner_ctx.widget_state.is_active; ctx.is_handled |= inner_ctx.is_handled; @@ -975,7 +980,6 @@ impl> WidgetPod { self.state.needs_window_origin = false; true } - #[cfg(test)] InternalLifeCycle::DebugRequestState { widget, state_cell } => { if *widget == self.id() { state_cell.set(self.state.clone()); @@ -983,10 +987,9 @@ impl> WidgetPod { } else { // Recurse when the target widget could be our descendant. // The bloom filter we're checking can return false positives. - self.state.children.may_contain(&widget) + self.state.children.may_contain(widget) } } - #[cfg(test)] InternalLifeCycle::DebugInspectState(f) => { f.call(&self.state); true diff --git a/druid/src/event.rs b/druid/src/event.rs index 816e305986..1510972f46 100644 --- a/druid/src/event.rs +++ b/druid/src/event.rs @@ -333,18 +333,19 @@ pub enum InternalLifeCycle { RouteDisabledChanged, /// The parents widget origin in window coordinate space has changed. ParentWindowOrigin, - /// Testing only: request the `WidgetState` of a specific widget. + /// For testing: request the `WidgetState` of a specific widget. /// /// During testing, you may wish to verify that the state of a widget /// somewhere in the tree is as expected. In that case you can dispatch /// this event, specifying the widget in question, and that widget will /// set its state in the provided `Cell`, if it exists. - #[cfg(test)] DebugRequestState { + /// the widget whose state is requested widget: WidgetId, + /// a cell used to store the a widget's state state_cell: StateCell, }, - #[cfg(test)] + /// For testing: apply the given function on every widget. DebugInspectState(StateCheckFn), } @@ -446,17 +447,14 @@ impl InternalLifeCycle { | InternalLifeCycle::RouteFocusChanged { .. } | InternalLifeCycle::RouteDisabledChanged => true, InternalLifeCycle::ParentWindowOrigin => false, - #[cfg(test)] InternalLifeCycle::DebugRequestState { .. } | InternalLifeCycle::DebugInspectState(_) => true, } } } -#[cfg(test)] pub(crate) use state_cell::{StateCell, StateCheckFn}; -#[cfg(test)] mod state_cell { use crate::core::WidgetState; use crate::WidgetId; @@ -503,7 +501,7 @@ mod state_cell { pub(crate) fn call(&self, state: &WidgetState) { let mut panic_reporter = WidgetDrop(true, state.id); - (self.0)(&state); + (self.0)(state); panic_reporter.0 = false; } } diff --git a/druid/src/lib.rs b/druid/src/lib.rs index c1b9aa01ff..0c8625ea46 100644 --- a/druid/src/lib.rs +++ b/druid/src/lib.rs @@ -166,8 +166,7 @@ mod mouse; pub mod scroll_component; mod sub_window; #[cfg(not(target_arch = "wasm32"))] -#[cfg(test)] -mod tests; +pub mod tests; pub mod text; pub mod theme; pub mod widget; @@ -210,7 +209,6 @@ pub use win_handler::DruidHandler; pub use window::{Window, WindowId}; #[cfg(not(target_arch = "wasm32"))] -#[cfg(test)] pub(crate) use event::{StateCell, StateCheckFn}; #[deprecated(since = "0.8.0", note = "import from druid::text module instead")] diff --git a/druid/src/localization.rs b/druid/src/localization.rs index fd50ffbb4b..142a5149fa 100644 --- a/druid/src/localization.rs +++ b/druid/src/localization.rs @@ -156,7 +156,7 @@ impl ResourceManager { for locale in &resolved_locales { let mut bundle = FluentBundle::new(resolved_locales.clone()); for res_id in resource_ids { - let res = self.get_resource(&res_id, &locale.to_string()); + let res = self.get_resource(res_id, &locale.to_string()); bundle.add_resource(res).unwrap(); } stack.push(bundle); diff --git a/druid/src/scroll_component.rs b/druid/src/scroll_component.rs index 5943fbacf0..c38119821b 100644 --- a/druid/src/scroll_component.rs +++ b/druid/src/scroll_component.rs @@ -510,7 +510,7 @@ impl ScrollComponent { pub fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, env: &Env) { if let LifeCycle::Size(_) = event { // Show the scrollbars any time our size changes - self.reset_scrollbar_fade(|d| ctx.request_timer(d), &env); + self.reset_scrollbar_fade(|d| ctx.request_timer(d), env); } } } diff --git a/druid/src/tests/harness.rs b/druid/src/tests/harness.rs index 95974f420c..8a54c42e74 100644 --- a/druid/src/tests/harness.rs +++ b/druid/src/tests/harness.rs @@ -84,6 +84,7 @@ impl<'a> TargetGuard<'a> { } } +#[allow(missing_docs)] impl Harness<'_, T> { /// Create a new `Harness` with the given data and a root widget, /// and provide that harness to the passed in function. @@ -189,7 +190,7 @@ impl Harness<'_, T> { } /// Retrieve a copy of this widget's `WidgetState`, or die trying. - pub(crate) fn get_state(&mut self, widget: WidgetId) -> WidgetState { + pub fn get_state(&mut self, widget: WidgetId) -> WidgetState { match self.try_get_state(widget) { Some(thing) => thing, None => panic!("get_state failed for widget {:?}", widget), @@ -197,7 +198,7 @@ impl Harness<'_, T> { } /// Attempt to retrieve a copy of this widget's `WidgetState`. - pub(crate) fn try_get_state(&mut self, widget: WidgetId) -> Option { + pub fn try_get_state(&mut self, widget: WidgetId) -> Option { let cell = StateCell::default(); let state_cell = cell.clone(); self.lifecycle(LifeCycle::Internal(InternalLifeCycle::DebugRequestState { @@ -210,7 +211,7 @@ impl Harness<'_, T> { /// Inspect the `WidgetState` of each widget in the tree. /// /// The provided closure will be called on each widget. - pub(crate) fn inspect_state(&mut self, f: impl Fn(&WidgetState) + 'static) { + pub fn inspect_state(&mut self, f: impl Fn(&WidgetState) + 'static) { let checkfn = StateCheckFn::new(f); self.lifecycle(LifeCycle::Internal(InternalLifeCycle::DebugInspectState( checkfn, @@ -309,7 +310,7 @@ impl Inner { #[allow(dead_code)] fn paint_region(&mut self, piet: &mut Piet, invalid: &Region) { self.window - .do_paint(piet, &invalid, &mut self.cmds, &self.data, &self.env); + .do_paint(piet, invalid, &mut self.cmds, &self.data, &self.env); } } diff --git a/druid/src/tests/helpers.rs b/druid/src/tests/helpers.rs index 400aa88b80..b18abb9fe4 100644 --- a/druid/src/tests/helpers.rs +++ b/druid/src/tests/helpers.rs @@ -15,6 +15,11 @@ //! Helper types for test writing. //! //! This includes tools for making throwaway widgets more easily. +//! +//! Note: Some of these types are undocumented. They're meant to help maintainers of Druid and +//! people trying to build a framework on top of Druid (like crochet), not to be user-facing. + +#![allow(missing_docs)] use std::cell::RefCell; use std::collections::VecDeque; @@ -53,12 +58,16 @@ pub struct ReplaceChild { /// Make one like this: /// /// ``` +/// # use druid::widget::Label; +/// # use druid::{WidgetExt, LifeCycle}; +/// use druid::tests::helpers::{Recording, Record, TestWidgetExt}; +/// use druid::tests::harness::Harness; /// let recording = Recording::default(); -/// let widget = Label::new().padding(4.0).record(&recording); +/// let widget = Label::new("Hello").padding(4.0).record(&recording); /// -/// Harness::create((), widget, |harness| { -/// widget.send_initial_events(); -/// assert_matches!(recording.next(), Record::L(LifeCycle::WidgetAdded)); +/// Harness::create_simple((), widget, |harness| { +/// harness.send_initial_events(); +/// assert!(matches!(recording.next(), Record::L(LifeCycle::WidgetAdded))); /// }) /// ``` pub struct Recorder { @@ -303,42 +312,12 @@ impl> Widget for Recorder { } } -// easily make a bunch of WidgetIds -pub fn widget_id2() -> (WidgetId, WidgetId) { - (WidgetId::next(), WidgetId::next()) -} - -pub fn widget_id3() -> (WidgetId, WidgetId, WidgetId) { - (WidgetId::next(), WidgetId::next(), WidgetId::next()) -} - -pub fn widget_id4() -> (WidgetId, WidgetId, WidgetId, WidgetId) { - ( - WidgetId::next(), - WidgetId::next(), - WidgetId::next(), - WidgetId::next(), - ) -} +pub fn widget_ids() -> [WidgetId; N] { + let mut ids = [WidgetId::reserved(0); N]; -#[allow(dead_code)] -pub fn widget_id5() -> (WidgetId, WidgetId, WidgetId, WidgetId, WidgetId) { - ( - WidgetId::next(), - WidgetId::next(), - WidgetId::next(), - WidgetId::next(), - WidgetId::next(), - ) -} + for id in &mut ids { + *id = WidgetId::next() + } -pub fn widget_id6() -> (WidgetId, WidgetId, WidgetId, WidgetId, WidgetId, WidgetId) { - ( - WidgetId::next(), - WidgetId::next(), - WidgetId::next(), - WidgetId::next(), - WidgetId::next(), - WidgetId::next(), - ) + ids } diff --git a/druid/src/tests/layout_tests.rs b/druid/src/tests/layout_tests.rs index 16d2665eca..7034d40368 100644 --- a/druid/src/tests/layout_tests.rs +++ b/druid/src/tests/layout_tests.rs @@ -46,7 +46,7 @@ fn simple_layout() { #[test] fn row_column() { - let (id1, id2, id3, id4, id5, id6) = widget_id6(); + let [id1, id2, id3, id4, id5, id6] = widget_ids(); let widget = Flex::row() .must_fill_main_axis(true) .with_flex_child( @@ -81,7 +81,7 @@ fn row_column() { #[test] fn simple_paint_rect() { - let (id1, id2) = widget_id2(); + let [id1, id2] = widget_ids(); let widget = ModularWidget::<(), ()>::new(()) .layout_fn(|_, ctx, bc, _, _| { diff --git a/druid/src/tests/mod.rs b/druid/src/tests/mod.rs index 86c791c4c8..4cb8fefbd8 100644 --- a/druid/src/tests/mod.rs +++ b/druid/src/tests/mod.rs @@ -14,9 +14,14 @@ //! Additional unit tests that cross file or module boundaries. -pub(crate) mod harness; -mod helpers; +#![allow(unused_imports)] + +pub mod harness; +pub mod helpers; + +#[cfg(test)] mod invalidation_tests; +#[cfg(test)] mod layout_tests; use std::cell::Cell; @@ -31,7 +36,8 @@ use harness::*; use helpers::*; use kurbo::Vec2; -fn move_mouse(p: impl Into) -> MouseEvent { +/// Helper function to construct a "move to this position" mouse event. +pub fn move_mouse(p: impl Into) -> MouseEvent { let pos = p.into(); MouseEvent { pos, @@ -45,7 +51,8 @@ fn move_mouse(p: impl Into) -> MouseEvent { } } -fn scroll_mouse(p: impl Into, delta: impl Into) -> MouseEvent { +/// Helper function to construct a "scroll by n ticks" mouse event. +pub fn scroll_mouse(p: impl Into, delta: impl Into) -> MouseEvent { let pos = p.into(); MouseEvent { pos, @@ -66,6 +73,7 @@ fn scroll_mouse(p: impl Into, delta: impl Into) -> MouseEvent { /// directory will be cleaned up at the end of the PathBufs lifetime. This /// uses the `tempfile` crate. #[allow(dead_code)] +#[cfg(test)] pub fn temp_dir_for_test() -> std::path::PathBuf { let current_exe_path = env::current_exe().unwrap(); let mut exe_dir = current_exe_path.parent().unwrap(); @@ -84,7 +92,7 @@ pub fn temp_dir_for_test() -> std::path::PathBuf { /// test that the first widget to request focus during an event gets it. #[test] fn propagate_hot() { - let (button, pad, root, empty) = widget_id4(); + let [button, pad, root, empty] = widget_ids(); let root_rec = Recording::default(); let padding_rec = Recording::default(); @@ -202,7 +210,7 @@ fn take_focus() { }) } - let (id_1, id_2, _id_3) = widget_id3(); + let [id_1, id_2, _id_3] = widget_ids(); // we use these so that we can check the widget's internal state let left_focus: Rc>> = Default::default(); @@ -286,7 +294,7 @@ fn focus_changed() { let b_rec = Recording::default(); let c_rec = Recording::default(); - let (id_a, id_b, id_c) = widget_id3(); + let [id_a, id_b, id_c] = widget_ids(); // a contains b which contains c let c = make_focus_container(vec![]).record(&c_rec).with_id(id_c); @@ -799,7 +807,7 @@ fn adding_child_lifecycle() { #[test] fn participate_in_autofocus() { - let (id_1, id_2, id_3, id_4, id_5, id_6) = widget_id6(); + let [id_1, id_2, id_3, id_4, id_5, id_6] = widget_ids(); // this widget starts with a single child, and will replace them with a split // when we send it a command. @@ -840,7 +848,7 @@ fn participate_in_autofocus() { #[test] fn child_tracking() { - let (id_1, id_2, id_3, id_4) = widget_id4(); + let [id_1, id_2, id_3, id_4] = widget_ids(); let widget = Split::columns( SizedBox::empty().with_id(id_1), @@ -868,8 +876,7 @@ fn child_tracking() { #[test] /// Test that all children are registered correctly after a child is replaced. fn register_after_adding_child() { - let (id_1, id_2, id_3, id_4, id_5, id_6) = widget_id6(); - let id_7 = WidgetId::next(); + let [id_1, id_2, id_3, id_4, id_5, id_6, id_7] = widget_ids(); let replacer = ReplaceChild::new(Slider::new().with_id(id_1), move || { Split::columns(Slider::new().with_id(id_2), Slider::new().with_id(id_3)).with_id(id_7) diff --git a/druid/src/text/editable_text.rs b/druid/src/text/editable_text.rs index 3551ff4003..7d05d73db8 100644 --- a/druid/src/text/editable_text.rs +++ b/druid/src/text/editable_text.rs @@ -75,7 +75,7 @@ pub trait EditableText: Sized { impl EditableText for String { fn cursor<'a>(&self, position: usize) -> Option { let new_cursor = StringCursor { - text: &self, + text: self, position, }; diff --git a/druid/src/widget/aspect_ratio_box.rs b/druid/src/widget/aspect_ratio_box.rs index 0b78ce698b..711baebb7b 100644 --- a/druid/src/widget/aspect_ratio_box.rs +++ b/druid/src/widget/aspect_ratio_box.rs @@ -139,13 +139,13 @@ impl Widget for AspectRatioBox { if bc.max() == bc.min() { warn!("Box constraints are tight. Aspect ratio box will not be able to preserve aspect ratio."); - return self.inner.layout(ctx, &bc, data, env); + return self.inner.layout(ctx, bc, data, env); } if bc.max().width == f64::INFINITY && bc.max().height == f64::INFINITY { warn!("Box constraints are INFINITE. Aspect ratio box won't be able to choose a size because the constraints given by the parent widget are INFINITE."); - return self.inner.layout(ctx, &bc, data, env); + return self.inner.layout(ctx, bc, data, env); } let bc = self.generate_constraints(bc); diff --git a/druid/src/widget/checkbox.rs b/druid/src/widget/checkbox.rs index 8299c3b5d5..c9aeefeff6 100644 --- a/druid/src/widget/checkbox.rs +++ b/druid/src/widget/checkbox.rs @@ -92,7 +92,7 @@ impl Widget for Checkbox { bc.debug_check("Checkbox"); let x_padding = env.get(theme::WIDGET_CONTROL_COMPONENT_PADDING); let check_size = env.get(theme::BASIC_WIDGET_HEIGHT); - let label_size = self.child_label.layout(ctx, &bc, data, env); + let label_size = self.child_label.layout(ctx, bc, data, env); let desired_size = Size::new( check_size + x_padding + label_size.width, diff --git a/druid/src/widget/env_scope.rs b/druid/src/widget/env_scope.rs index a0277dd4a9..33094903cc 100644 --- a/druid/src/widget/env_scope.rs +++ b/druid/src/widget/env_scope.rs @@ -61,7 +61,7 @@ impl> Widget for EnvScope { #[instrument(name = "EnvScope", level = "trace", skip(self, ctx, event, data, env))] fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) { let mut new_env = env.clone(); - (self.f)(&mut new_env, &data); + (self.f)(&mut new_env, data); self.child.event(ctx, event, data, &new_env) } @@ -69,7 +69,7 @@ impl> Widget for EnvScope { #[instrument(name = "EnvScope", level = "trace", skip(self, ctx, event, data, env))] fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) { let mut new_env = env.clone(); - (self.f)(&mut new_env, &data); + (self.f)(&mut new_env, data); self.child.lifecycle(ctx, event, data, &new_env) } @@ -80,7 +80,7 @@ impl> Widget for EnvScope { )] fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &T, data: &T, env: &Env) { let mut new_env = env.clone(); - (self.f)(&mut new_env, &data); + (self.f)(&mut new_env, data); self.child.update(ctx, data, &new_env); } @@ -90,9 +90,9 @@ impl> Widget for EnvScope { bc.debug_check("EnvScope"); let mut new_env = env.clone(); - (self.f)(&mut new_env, &data); + (self.f)(&mut new_env, data); - let size = self.child.layout(ctx, &bc, data, &new_env); + let size = self.child.layout(ctx, bc, data, &new_env); self.child.set_origin(ctx, data, env, Point::ORIGIN); size } @@ -100,7 +100,7 @@ impl> Widget for EnvScope { #[instrument(name = "EnvScope", level = "trace", skip(self, ctx, data, env))] fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) { let mut new_env = env.clone(); - (self.f)(&mut new_env, &data); + (self.f)(&mut new_env, data); self.child.paint(ctx, data, &new_env); } diff --git a/druid/src/widget/radio.rs b/druid/src/widget/radio.rs index 04216fb490..a3436d8fe6 100644 --- a/druid/src/widget/radio.rs +++ b/druid/src/widget/radio.rs @@ -106,7 +106,7 @@ impl Widget for Radio { fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size { bc.debug_check("Radio"); - let label_size = self.child_label.layout(ctx, &bc, data, env); + let label_size = self.child_label.layout(ctx, bc, data, env); let radio_diam = env.get(theme::BASIC_WIDGET_HEIGHT); let x_padding = env.get(theme::WIDGET_CONTROL_COMPONENT_PADDING); diff --git a/druid/src/widget/scope.rs b/druid/src/widget/scope.rs index 929d11071b..4f29f80d00 100644 --- a/druid/src/widget/scope.rs +++ b/druid/src/widget/scope.rs @@ -115,7 +115,7 @@ impl, In: Data, State: Data> ScopeTransfer for LensScopeTrans fn read_input(&self, state: &mut State, data: &In) { self.lens.with_mut(state, |inner| { - if !inner.same(&data) { + if !inner.same(data) { *inner = data.clone() } }); @@ -123,7 +123,7 @@ impl, In: Data, State: Data> ScopeTransfer for LensScopeTrans fn write_back_input(&self, state: &State, data: &mut In) { self.lens.with(state, |inner| { - if !inner.same(&data) { + if !inner.same(data) { *data = inner.clone(); } }); diff --git a/druid/src/widget/scroll.rs b/druid/src/widget/scroll.rs index 11eda98909..d2e7f012aa 100644 --- a/druid/src/widget/scroll.rs +++ b/druid/src/widget/scroll.rs @@ -204,7 +204,7 @@ impl> Widget for Scroll { bc.debug_check("Scroll"); let old_size = self.clip.viewport().view_size; - let child_size = self.clip.layout(ctx, &bc, data, env); + let child_size = self.clip.layout(ctx, bc, data, env); log_size_warnings(child_size); let self_size = bc.constrain(child_size); diff --git a/druid/src/widget/split.rs b/druid/src/widget/split.rs index 92ae222bb8..8e81c85384 100644 --- a/druid/src/widget/split.rs +++ b/druid/src/widget/split.rs @@ -390,8 +390,8 @@ impl Widget for Split { #[instrument(name = "Split", level = "trace", skip(self, ctx, _old_data, data, env))] fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &T, data: &T, env: &Env) { - self.child1.update(ctx, &data, env); - self.child2.update(ctx, &data, env); + self.child1.update(ctx, data, env); + self.child2.update(ctx, data, env); } #[instrument(name = "Split", level = "trace", skip(self, ctx, bc, data, env))] @@ -464,8 +464,8 @@ impl Widget for Split { ) } }; - let child1_size = self.child1.layout(ctx, &child1_bc, &data, env); - let child2_size = self.child2.layout(ctx, &child2_bc, &data, env); + let child1_size = self.child1.layout(ctx, &child1_bc, data, env); + let child2_size = self.child2.layout(ctx, &child2_bc, data, env); // Top-left align for both children, out of laziness. // Reduce our unsplit direction to the larger of the two widgets @@ -498,7 +498,7 @@ impl Widget for Split { } else { self.paint_stroked_bar(ctx, env); } - self.child1.paint(ctx, &data, env); - self.child2.paint(ctx, &data, env); + self.child1.paint(ctx, data, env); + self.child2.paint(ctx, data, env); } } diff --git a/druid/src/widget/tabs.rs b/druid/src/widget/tabs.rs index 3b0e85f21a..13057b94ce 100644 --- a/druid/src/widget/tabs.rs +++ b/druid/src/widget/tabs.rs @@ -524,7 +524,7 @@ fn ensure_for_tabs( existing_idx.push(contents.len()); child } else { - f(&policy, key.clone()) + f(policy, key.clone()) }; contents.push((key.clone(), next)) } diff --git a/druid/src/win_handler.rs b/druid/src/win_handler.rs index c803b49541..236be2a68e 100644 --- a/druid/src/win_handler.rs +++ b/druid/src/win_handler.rs @@ -688,10 +688,7 @@ impl AppState { tracing::warn!("SHOW_OPEN_PANEL command must target a window.") } _ => { - let handled = self.inner.borrow_mut().dispatch_cmd(cmd.clone()); - if !handled.is_handled() && cmd.must_be_used() { - tracing::warn!("{:?} was not handled.", cmd); - } + self.inner.borrow_mut().dispatch_cmd(cmd); } } } diff --git a/druid/src/window.rs b/druid/src/window.rs index f1618b8556..c0fac55ecc 100644 --- a/druid/src/window.rs +++ b/druid/src/window.rs @@ -300,7 +300,7 @@ impl Window { } if let Some(cursor) = &widget_state.cursor { - self.handle.set_cursor(&cursor); + self.handle.set_cursor(cursor); } else if matches!( event, Event::MouseMove(..) | Event::Internal(InternalEvent::MouseLeave) @@ -384,13 +384,11 @@ impl Window { self.invalid.clear(); } - #[cfg(test)] #[allow(dead_code)] pub(crate) fn invalid(&self) -> &Region { &self.invalid } - #[cfg(test)] #[allow(dead_code)] pub(crate) fn invalid_mut(&mut self) -> &mut Region { &mut self.invalid @@ -479,7 +477,6 @@ impl Window { /// only expose `layout` for testing; normally it is called as part of `do_paint` #[cfg(not(target_arch = "wasm32"))] - #[cfg(test)] pub(crate) fn just_layout(&mut self, queue: &mut CommandQueue, data: &T, env: &Env) { self.layout(queue, data, env) }