1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License in the LICENSE-APACHE file or at: // https://www.apache.org/licenses/LICENSE-2.0 //! Widget traits use std::any::Any; use std::fmt; use std::ops::DerefMut; use crate::draw::SizeHandle; use crate::event::{self, Manager}; use crate::{layout, Direction, WidgetId, WindowId}; mod impls; mod utils; mod widget; pub use utils::*; pub use widget::*; /// Trait to describe the type needed by the layout implementation. /// /// The (non-trivial) [`layout`] engines require a storage field within their /// widget. For manual [`Layout`] implementations this may be specified /// directly, but to allow the `derive(Widget)` macro to specify the appropriate /// data type, a widget should include a field of the following form: /// ```none /// #[layout_data] layout_data: <Self as kas::LayoutData>::Data, /// ``` /// /// Ideally we would use an inherent associated type on the struct in question, /// but until rust-lang#8995 is implemented that is not possible. We also cannot /// place this associated type on the [`Widget`] trait itself, since then uses /// of the trait would require parameterisation. Thus, this trait. pub trait LayoutData { type Data: Clone + fmt::Debug + Default; type Solver: layout::RulesSolver; type Setter: layout::RulesSetter; } /// A widget which escapes its parent's rect /// /// A pop-up is a special widget drawn either as a layer over the existing /// window or in a new borderless window. It should be precisely positioned /// *next to* it's `parent`'s `rect`, in the specified `direction` (or, if not /// possible, in the opposite direction). /// /// A pop-up is in some ways an ordinary child widget and in some ways not. /// The pop-up widget should be a permanent child of its parent, but is not /// visible until [`Manager::add_popup`] is called. /// /// A pop-up widget's rect is not contained by its parent, therefore the parent /// must not call any [`Layout`] methods on the pop-up (whether or not it is /// visible). The window is responsible for calling these methods. /// /// Other methods on the pop-up, including event handlers, should be called /// normally, with one exception: after calling an event handler on the pop-up, /// the parent should invoke [`Manager::pop_action`] and handle the action /// itself, where possible (using [`Manager::close_window`] to close it). /// Remaining actions should be added back to the [`Manager`]. // // NOTE: it's tempting to include a pointer to the widget here. There are two // options: (a) an unsafe aliased pointer or (b) Rc<RefCell<dyn WidgetConfig>>. // Option (a) should work but is an unnecessary performance hack; (b) could in // theory work but requires adjusting WidgetChildren::get, find etc. to take a // closure instead of returning a reference, causing *significant* complication. #[derive(Clone, Debug)] pub struct Popup { pub id: WidgetId, pub parent: WidgetId, pub direction: Direction, } /// Functionality required by a window pub trait Window: Widget<Msg = event::VoidMsg> { /// Get the window title fn title(&self) -> &str; /// Whether to limit the maximum size of a window /// /// All widgets' size rules allow calculation of two sizes: the minimum /// size and the ideal size. Windows are initially sized to the ideal size. /// This option controls whether the window size is restricted by the /// calculated minimum size and by the ideal size. /// /// Return value is `(restrict_min, restrict_max)`. Suggested is to use /// `(true, true)` for simple dialog boxes and `(true, false)` for complex /// windows. fn restrict_dimensions(&self) -> (bool, bool); /// Add a pop-up as a layer in the current window /// /// Each [`Popup`] is assigned a [`WindowId`]; both are passed. fn add_popup( &mut self, size_handle: &mut dyn SizeHandle, mgr: &mut Manager, id: WindowId, popup: Popup, ); /// Resize popups /// /// This is called immediately after [`Layout::set_rect`] to resize /// existing pop-ups. fn resize_popups(&mut self, size_handle: &mut dyn SizeHandle); /// Trigger closure of a pop-up /// /// If the given `id` refers to a pop-up, it should be closed. fn remove_popup(&mut self, mgr: &mut Manager, id: WindowId); /// Handle closure of self /// /// This allows for actions on destruction, but doesn't need to do anything. fn handle_closure(&mut self, _mgr: &mut Manager) {} } /// Return value of [`ThemeApi`] functions /// /// This type is used to notify the toolkit of required updates. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] pub enum ThemeAction { /// No action needed #[cfg_attr(not(feature = "internal_doc"), doc(hidden))] None, /// All windows require redrawing #[cfg_attr(not(feature = "internal_doc"), doc(hidden))] RedrawAll, /// Theme sizes have changed /// /// This implies that per-window theme data must be updated /// (via [`kas-theme::Theme::update_window`]) and all widgets resized. #[cfg_attr(not(feature = "internal_doc"), doc(hidden))] ThemeResize, } /// Interface through which a theme can be adjusted at run-time /// /// All methods return a [`ThemeAction`] to enable correct action when a theme /// is updated via [`Manager::adjust_theme`]. When adjusting a theme before /// the UI is started, this return value can be safely ignored. pub trait ThemeApi { /// Set font size. Default is 18. Units are unknown. fn set_font_size(&mut self, size: f32) -> ThemeAction; /// Change the colour scheme /// /// If no scheme by this name is found, the scheme is unchanged. // TODO: revise scheme identification and error handling? fn set_colours(&mut self, _scheme: &str) -> ThemeAction; /// Switch the theme /// /// Most themes do not react to this method; `kas_theme::MultiTheme` uses /// it to switch themes. fn set_theme(&mut self, _theme: &str) -> ThemeAction { ThemeAction::None } } impl<T: ThemeApi> ThemeApi for Box<T> { fn set_font_size(&mut self, size: f32) -> ThemeAction { self.deref_mut().set_font_size(size) } fn set_colours(&mut self, scheme: &str) -> ThemeAction { self.deref_mut().set_colours(scheme) } fn set_theme(&mut self, theme: &str) -> ThemeAction { self.deref_mut().set_theme(theme) } }