pub struct SizeMgr<'a>(_);Expand description
Size and scale interface
This interface is provided to widgets in crate::Layout::size_rules.
It may also be accessed through crate::event::EventMgr::size_mgr,
DrawMgr::size_mgr.
Most methods get or calculate the size of some feature. These same features
may be drawn through DrawMgr.
Implementations§
source§impl<'a> SizeMgr<'a>
impl<'a> SizeMgr<'a>
sourcepub fn re<'b>(&'b self) -> SizeMgr<'b>where
'a: 'b,
pub fn re<'b>(&'b self) -> SizeMgr<'b>where
'a: 'b,
Reborrow with a new lifetime
Rust allows references like &T or &mut T to be “reborrowed” through
coercion: essentially, the pointer is copied under a new, shorter, lifetime.
Until rfcs#1403 lands, reborrows on user types require a method call.
Calling this method is zero-cost.
Examples found in repository?
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
pub fn solve_size_rules<W: Widget + ?Sized>(
widget: &mut W,
size_mgr: SizeMgr,
x_size: Option<i32>,
y_size: Option<i32>,
h_align: Option<Align>,
v_align: Option<Align>,
) {
widget.size_rules(size_mgr.re(), AxisInfo::new(false, y_size, h_align));
widget.size_rules(size_mgr.re(), AxisInfo::new(true, x_size, v_align));
}
/// Size solver
///
/// This struct is used to solve widget layout, read size constraints and
/// cache the results until the next solver run.
///
/// [`SolveCache::find_constraints`] constructs an instance of this struct,
/// solving for size constraints.
///
/// [`SolveCache::apply_rect`] accepts a [`Rect`], updates constraints as
/// necessary and sets widget positions within this `rect`.
pub struct SolveCache {
// Technically we don't need to store min and ideal here, but it simplifies
// the API for very little real cost.
min: Size,
ideal: Size,
margins: Margins,
refresh_rules: bool,
last_width: i32,
}
impl SolveCache {
/// Get the minimum size
///
/// If `inner_margin` is true, margins are included in the result.
pub fn min(&self, inner_margin: bool) -> Size {
if inner_margin {
self.margins.pad(self.min)
} else {
self.min
}
}
/// Get the ideal size
///
/// If `inner_margin` is true, margins are included in the result.
pub fn ideal(&self, inner_margin: bool) -> Size {
if inner_margin {
self.margins.pad(self.ideal)
} else {
self.ideal
}
}
/// Get the margins
pub fn margins(&self) -> Margins {
self.margins
}
/// Calculate required size of widget
///
/// Assumes no explicit alignment.
pub fn find_constraints(widget: &mut dyn Widget, size_mgr: SizeMgr) -> Self {
let start = std::time::Instant::now();
let w = widget.size_rules(size_mgr.re(), AxisInfo::new(false, None, None));
let h = widget.size_rules(
size_mgr.re(),
AxisInfo::new(true, Some(w.ideal_size()), None),
);
let min = Size(w.min_size(), h.min_size());
let ideal = Size(w.ideal_size(), h.ideal_size());
let margins = Margins::hv(w.margins(), h.margins());
log::trace!(
target: "kas_perf::layout", "find_constraints: {}μs",
start.elapsed().as_micros(),
);
log::debug!("find_constraints: min={min:?}, ideal={ideal:?}, margins={margins:?}");
let refresh_rules = false;
let last_width = ideal.0;
SolveCache {
min,
ideal,
margins,
refresh_rules,
last_width,
}
}More examples
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
fn size_rules_(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
match &mut self.layout {
LayoutType::None => SizeRules::EMPTY,
LayoutType::Component(component) => component.size_rules(mgr, axis),
LayoutType::BoxComponent(component) => component.size_rules(mgr, axis),
LayoutType::Single(child) => child.size_rules(mgr, axis),
LayoutType::AlignSingle(child, hints) => {
child.size_rules(mgr, axis.with_align_hints(*hints))
}
LayoutType::Align(layout, hints) => {
layout.size_rules_(mgr, axis.with_align_hints(*hints))
}
LayoutType::Pack(layout, stor, hints) => {
let rules = layout.size_rules_(mgr, stor.apply_align(axis, *hints));
stor.size.set_component(axis, rules.ideal_size());
rules
}
LayoutType::Margins(child, dirs, margins) => {
let mut child_rules = child.size_rules_(mgr.re(), axis);
if dirs.intersects(Directions::from(axis)) {
let mut rule_margins = child_rules.margins();
let margins = mgr.margins(*margins).extract(axis);
if dirs.intersects(Directions::LEFT | Directions::UP) {
rule_margins.0 = margins.0;
}
if dirs.intersects(Directions::RIGHT | Directions::DOWN) {
rule_margins.1 = margins.1;
}
child_rules.set_margins(rule_margins);
}
child_rules
}
LayoutType::Frame(child, storage, style) => {
let child_rules = child.size_rules_(mgr.re(), storage.child_axis(axis));
storage.size_rules(mgr, axis, child_rules, *style)
}
LayoutType::Button(child, storage, _) => {
let child_rules = child.size_rules_(mgr.re(), storage.child_axis_centered(axis));
storage.size_rules(mgr, axis, child_rules, FrameStyle::Button)
}
}
}
/// Apply a given `rect` to self
#[inline]
pub fn set_rect(mut self, mgr: &mut ConfigMgr, rect: Rect) {
self.set_rect_(mgr, rect);
}
fn set_rect_(&mut self, mgr: &mut ConfigMgr, rect: Rect) {
match &mut self.layout {
LayoutType::None => (),
LayoutType::Component(component) => component.set_rect(mgr, rect),
LayoutType::BoxComponent(layout) => layout.set_rect(mgr, rect),
LayoutType::Single(child) => child.set_rect(mgr, rect),
LayoutType::Align(layout, _) => layout.set_rect_(mgr, rect),
LayoutType::AlignSingle(child, _) => child.set_rect(mgr, rect),
LayoutType::Pack(layout, stor, _) => layout.set_rect_(mgr, stor.aligned_rect(rect)),
LayoutType::Margins(child, _, _) => child.set_rect_(mgr, rect),
LayoutType::Frame(child, storage, _) | LayoutType::Button(child, storage, _) => {
storage.rect = rect;
let child_rect = Rect {
pos: rect.pos + storage.offset,
size: rect.size - storage.size,
};
child.set_rect_(mgr, child_rect);
}
}
}
/// Find a widget by coordinate
///
/// Does not return the widget's own identifier. See example usage in
/// [`Visitor::find_id`].
#[inline]
pub fn find_id(mut self, coord: Coord) -> Option<WidgetId> {
self.find_id_(coord)
}
fn find_id_(&mut self, coord: Coord) -> Option<WidgetId> {
match &mut self.layout {
LayoutType::None => None,
LayoutType::Component(component) => component.find_id(coord),
LayoutType::BoxComponent(layout) => layout.find_id(coord),
LayoutType::Single(child) | LayoutType::AlignSingle(child, _) => child.find_id(coord),
LayoutType::Align(layout, _) => layout.find_id_(coord),
LayoutType::Pack(layout, _, _) => layout.find_id_(coord),
LayoutType::Margins(layout, _, _) => layout.find_id_(coord),
LayoutType::Frame(child, _, _) => child.find_id_(coord),
// Buttons steal clicks, hence Button never returns ID of content
LayoutType::Button(_, _, _) => None,
}
}
/// Draw a widget's children
#[inline]
pub fn draw(mut self, draw: DrawMgr) {
self.draw_(draw);
}
fn draw_(&mut self, mut draw: DrawMgr) {
match &mut self.layout {
LayoutType::None => (),
LayoutType::Component(component) => component.draw(draw),
LayoutType::BoxComponent(layout) => layout.draw(draw),
LayoutType::Single(child) | LayoutType::AlignSingle(child, _) => draw.recurse(*child),
LayoutType::Align(layout, _) => layout.draw_(draw),
LayoutType::Pack(layout, _, _) => layout.draw_(draw),
LayoutType::Margins(layout, _, _) => layout.draw_(draw),
LayoutType::Frame(child, storage, style) => {
draw.frame(storage.rect, *style, Background::Default);
child.draw_(draw);
}
LayoutType::Button(child, storage, color) => {
let bg = match color {
Some(rgb) => Background::Rgb(*rgb),
None => Background::Default,
};
draw.frame(storage.rect, FrameStyle::Button, bg);
child.draw_(draw);
}
}
}
}
/// Implement row/column layout for children
struct List<'a, S, D, I> {
data: &'a mut S,
direction: D,
children: I,
}
impl<'a, S: RowStorage, D: Directional, I> Layout for List<'a, S, D, I>
where
I: ExactSizeIterator<Item = Visitor<'a>>,
{
fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
let dim = (self.direction, self.children.len());
let mut solver = RowSolver::new(axis, dim, self.data);
for (n, child) in (&mut self.children).enumerate() {
solver.for_child(self.data, n, |axis| child.size_rules(mgr.re(), axis));
}
solver.finish(self.data)
}
fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect) {
let dim = (self.direction, self.children.len());
let mut setter = RowSetter::<D, Vec<i32>, _>::new(rect, dim, self.data);
for (n, child) in (&mut self.children).enumerate() {
child.set_rect(mgr, setter.child_rect(self.data, n));
}
}
fn find_id(&mut self, coord: Coord) -> Option<WidgetId> {
// TODO(opt): more efficient search strategy?
self.children.find_map(|child| child.find_id(coord))
}
fn draw(&mut self, mut draw: DrawMgr) {
for child in &mut self.children {
child.draw(draw.re_clone());
}
}
}
/// Float layout
struct Float<'a, I>
where
I: DoubleEndedIterator<Item = Visitor<'a>>,
{
children: I,
}
impl<'a, I> Layout for Float<'a, I>
where
I: DoubleEndedIterator<Item = Visitor<'a>>,
{
fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
let mut rules = SizeRules::EMPTY;
for child in &mut self.children {
rules = rules.max(child.size_rules(mgr.re(), axis));
}
rules
}
fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect) {
for child in &mut self.children {
child.set_rect(mgr, rect);
}
}
fn find_id(&mut self, coord: Coord) -> Option<WidgetId> {
self.children.find_map(|child| child.find_id(coord))
}
fn draw(&mut self, mut draw: DrawMgr) {
let mut iter = (&mut self.children).rev();
if let Some(first) = iter.next() {
first.draw(draw.re_clone());
}
for child in iter {
draw.with_pass(|draw| child.draw(draw));
}
}
}
/// A row/column over a slice
struct Slice<'a, W: Widget, D: Directional> {
data: &'a mut DynRowStorage,
direction: D,
children: &'a mut [W],
}
impl<'a, W: Widget, D: Directional> Layout for Slice<'a, W, D> {
fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
let dim = (self.direction, self.children.len());
let mut solver = RowSolver::new(axis, dim, self.data);
for (n, child) in self.children.iter_mut().enumerate() {
solver.for_child(self.data, n, |axis| child.size_rules(mgr.re(), axis));
}
solver.finish(self.data)
}
fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect) {
let dim = (self.direction, self.children.len());
let mut setter = RowSetter::<D, Vec<i32>, _>::new(rect, dim, self.data);
for (n, child) in self.children.iter_mut().enumerate() {
child.set_rect(mgr, setter.child_rect(self.data, n));
}
}
fn find_id(&mut self, coord: Coord) -> Option<WidgetId> {
let solver = RowPositionSolver::new(self.direction);
solver
.find_child_mut(self.children, coord)
.and_then(|child| child.find_id(coord))
}
fn draw(&mut self, mut draw: DrawMgr) {
let solver = RowPositionSolver::new(self.direction);
solver.for_children(self.children, draw.get_clip_rect(), |w| draw.recurse(w));
}
}
/// Implement grid layout for children
struct Grid<'a, S, I> {
data: &'a mut S,
dim: GridDimensions,
children: I,
}
impl<'a, S: GridStorage, I> Layout for Grid<'a, S, I>
where
I: Iterator<Item = (GridChildInfo, Visitor<'a>)>,
{
fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
let mut solver = GridSolver::<Vec<_>, Vec<_>, _>::new(axis, self.dim, self.data);
for (info, child) in &mut self.children {
solver.for_child(self.data, info, |axis| child.size_rules(mgr.re(), axis));
}
solver.finish(self.data)
}sourcepub fn scale_factor(&self) -> f32
pub fn scale_factor(&self) -> f32
Get the scale factor
“Traditional” PC screens have a scale factor of 1; high-DPI screens may have a factor of 2 or higher. This may be fractional and may be adjusted to suit the device type (e.g. a phone or desktop monitor) as well as the user’s preference.
One could use this value to calculate physical size, but be warned that
the result may be quite inaccurate on anything other than a desktop
monitor: 25.4 mm = 1 inch = (96 * scale_factor) pixels
To calculate screen pixel sizes from virtual pixel sizes:
use kas_core::cast::*;
let size: i32 = (100.0 * scale_factor).cast_ceil();This value may change during a program’s execution (e.g. when a window
is moved to a different monitor); in this case all widgets will be
resized via crate::Layout::size_rules.
Examples found in repository?
253 254 255 256 257 258 259 260 261 262 263 264 265 266
pub fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
let margins = mgr.margins(self.margins).extract(axis);
let scale_factor = mgr.scale_factor();
let min = self
.size
.to_physical(scale_factor * self.min_factor)
.extract(axis);
let ideal = self
.size
.to_physical(scale_factor * self.ideal_factor)
.extract(axis);
self.align.set_component(axis, axis.align_or_center());
SizeRules::new(min, ideal, margins, self.stretch)
}More examples
253 254 255 256 257 258 259 260 261 262 263 264 265 266
pub fn update_widgets(&mut self, widget: &mut dyn Widget, id: UpdateId, payload: u64) {
if id == self.state.config.config.id() {
let (sf, dpem) = self.size_mgr(|size| (size.scale_factor(), size.dpem()));
self.state.config.update(sf, dpem);
}
let start = Instant::now();
let count = self.send_all(widget, Event::Update { id, payload });
log::debug!(
target: "kas_core::event::manager",
"update_widgets: sent Event::Update ({id:?}) to {count} widgets in {}μs",
start.elapsed().as_micros()
);
}sourcepub fn dpem(&self) -> f32
pub fn dpem(&self) -> f32
The Em size of the standard font in pixels
The Em is a unit of typography (variously defined as the point-size of
the font, the height of the font or the width of an upper-case M).
The method Self::line_height returns a related but distinct value.
This method returns the size of 1 Em in physical pixels, derived from the font size in use by the theme and the screen’s scale factor.
Examples found in repository?
253 254 255 256 257 258 259 260 261 262 263 264 265 266
pub fn update_widgets(&mut self, widget: &mut dyn Widget, id: UpdateId, payload: u64) {
if id == self.state.config.config.id() {
let (sf, dpem) = self.size_mgr(|size| (size.scale_factor(), size.dpem()));
self.state.config.update(sf, dpem);
}
let start = Instant::now();
let count = self.send_all(widget, Event::Update { id, payload });
log::debug!(
target: "kas_core::event::manager",
"update_widgets: sent Event::Update ({id:?}) to {count} widgets in {}μs",
start.elapsed().as_micros()
);
}sourcepub fn min_scroll_size(&self, axis: impl Directional) -> i32
pub fn min_scroll_size(&self, axis: impl Directional) -> i32
The minimum size of a scrollable area
sourcepub fn handle_len(&self) -> i32
pub fn handle_len(&self) -> i32
The length of a dragable handle for a scroll bar or slider
This is the length in line with the control. The size on the opposite
axis is assumed to be equal to the feature size as reported by
Self::feature.
sourcepub fn scroll_bar_width(&self) -> i32
pub fn scroll_bar_width(&self) -> i32
The width of a vertical scroll bar
This value is also available through Self::feature.
sourcepub fn margins(&self, style: MarginStyle) -> Margins
pub fn margins(&self, style: MarginStyle) -> Margins
Get margin size
Examples found in repository?
253 254 255 256 257 258 259 260 261 262 263 264 265 266
pub fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
let margins = mgr.margins(self.margins).extract(axis);
let scale_factor = mgr.scale_factor();
let min = self
.size
.to_physical(scale_factor * self.min_factor)
.extract(axis);
let ideal = self
.size
.to_physical(scale_factor * self.ideal_factor)
.extract(axis);
self.align.set_component(axis, axis.align_or_center());
SizeRules::new(min, ideal, margins, self.stretch)
}More examples
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
fn size_rules_(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
match &mut self.layout {
LayoutType::None => SizeRules::EMPTY,
LayoutType::Component(component) => component.size_rules(mgr, axis),
LayoutType::BoxComponent(component) => component.size_rules(mgr, axis),
LayoutType::Single(child) => child.size_rules(mgr, axis),
LayoutType::AlignSingle(child, hints) => {
child.size_rules(mgr, axis.with_align_hints(*hints))
}
LayoutType::Align(layout, hints) => {
layout.size_rules_(mgr, axis.with_align_hints(*hints))
}
LayoutType::Pack(layout, stor, hints) => {
let rules = layout.size_rules_(mgr, stor.apply_align(axis, *hints));
stor.size.set_component(axis, rules.ideal_size());
rules
}
LayoutType::Margins(child, dirs, margins) => {
let mut child_rules = child.size_rules_(mgr.re(), axis);
if dirs.intersects(Directions::from(axis)) {
let mut rule_margins = child_rules.margins();
let margins = mgr.margins(*margins).extract(axis);
if dirs.intersects(Directions::LEFT | Directions::UP) {
rule_margins.0 = margins.0;
}
if dirs.intersects(Directions::RIGHT | Directions::DOWN) {
rule_margins.1 = margins.1;
}
child_rules.set_margins(rule_margins);
}
child_rules
}
LayoutType::Frame(child, storage, style) => {
let child_rules = child.size_rules_(mgr.re(), storage.child_axis(axis));
storage.size_rules(mgr, axis, child_rules, *style)
}
LayoutType::Button(child, storage, _) => {
let child_rules = child.size_rules_(mgr.re(), storage.child_axis_centered(axis));
storage.size_rules(mgr, axis, child_rules, FrameStyle::Button)
}
}
}sourcepub fn inner_margins(&self) -> Margins
pub fn inner_margins(&self) -> Margins
Get margins for MarginStyle::Inner
sourcepub fn tiny_margins(&self) -> Margins
pub fn tiny_margins(&self) -> Margins
Get margins for MarginStyle::Tiny
sourcepub fn small_margins(&self) -> Margins
pub fn small_margins(&self) -> Margins
Get margins for MarginStyle::Small
sourcepub fn large_margins(&self) -> Margins
pub fn large_margins(&self) -> Margins
Get margins for MarginStyle::Large
sourcepub fn text_margins(&self) -> Margins
pub fn text_margins(&self) -> Margins
Get margins for MarginStyle::Text
sourcepub fn feature(&self, feature: Feature, axis: impl Directional) -> SizeRules
pub fn feature(&self, feature: Feature, axis: impl Directional) -> SizeRules
Size rules for a feature
sourcepub fn frame(&self, style: FrameStyle, axis: impl Directional) -> FrameRules
pub fn frame(&self, style: FrameStyle, axis: impl Directional) -> FrameRules
Size of a frame around another element
Examples found in repository?
536 537 538 539 540 541 542 543 544 545 546 547 548
pub fn size_rules(
&mut self,
mgr: SizeMgr,
axis: AxisInfo,
child_rules: SizeRules,
style: FrameStyle,
) -> SizeRules {
let frame_rules = mgr.frame(style, axis);
let (rules, offset, size) = frame_rules.surround(child_rules);
self.offset.set_component(axis, offset);
self.size.set_component(axis, size);
rules
}sourcepub fn line_height(&self, class: TextClass) -> i32
pub fn line_height(&self, class: TextClass) -> i32
The height of a line of text using the corresponding font
This method looks up the font face corresponding to the given class,
scales this according to Self::dpem, then measures the line height.
The result is typically 100% - 150% of the value returned by
Self::dpem, depending on the font face.
sourcepub fn text_rules(
&self,
text: &mut dyn TextApi,
class: TextClass,
axis: AxisInfo
) -> SizeRules
pub fn text_rules(
&self,
text: &mut dyn TextApi,
class: TextClass,
axis: AxisInfo
) -> SizeRules
Get SizeRules for a text element
The TextClass is used to select a font and controls whether line
wrapping is enabled.
Alignment is set from AxisInfo::align_or_default. If other alignment
is desired, modify axis before calling this method.
Horizontal size without wrapping is simply the size the text.
Horizontal size with wrapping is bounded to some width dependant on the
theme, and may have non-zero Stretch depending on the size.
Vertical size is the size of the text with or without wrapping, but with the minimum at least the height of one line of text.
Widgets with editable text contents or internal scrolling enabled may wish to adjust the result.
Note: this method partially prepares the text object. It is not
required to call this method but it is required to call
ConfigMgr::text_set_size before text display for correct results.