pub trait Widget: WidgetChildren + Layout {
fn pre_configure(&mut self, mgr: &mut ConfigMgr<'_>, id: WidgetId);
fn configure(&mut self, mgr: &mut ConfigMgr<'_>) { ... }
fn navigable(&self) -> bool { ... }
fn translation(&self) -> Offset { ... }
fn nav_next(
&mut self,
mgr: &mut ConfigMgr<'_>,
reverse: bool,
from: Option<usize>
) -> Option<usize> { ... }
fn handle_event(&mut self, mgr: &mut EventMgr<'_>, event: Event) -> Response { ... }
fn steal_event(
&mut self,
mgr: &mut EventMgr<'_>,
id: &WidgetId,
event: &Event
) -> Response { ... }
fn handle_unused(
&mut self,
mgr: &mut EventMgr<'_>,
index: usize,
event: Event
) -> Response { ... }
fn handle_message(&mut self, mgr: &mut EventMgr<'_>, index: usize) { ... }
fn handle_scroll(&mut self, mgr: &mut EventMgr<'_>, scroll: Scroll) { ... }
}
Expand description
The Widget trait
Widgets implement a family of traits, of which this trait is the final member:
WidgetCore
— base functionalityWidgetChildren
— enumerates childrenLayout
— handles sizing and positioning for self and childrenWidget
— configuration, some aspects of layout, event handling
Implementing Widget
To implement a widget, use the macros::widget
macro. This is the
only supported method of implementing Widget
.
The macros::widget
macro only works within macros::impl_scope
.
Other trait implementations can be detected within this scope:
WidgetCore
is always generatedWidgetChildren
is generated if no direct implementation is presentLayout
is generated if thelayout
attribute property is set, and no direct implementation is found. In other cases where a direct implementation of the trait is found, (default) method implementations may be injected where not already present.Widget
is generated if no direct implementation is present, otherwise some (default) method implementations are injected where these methods are not directly implemented.
Some simple examples follow. See also
examples apps
and kas_widgets
code.
use kas::event;
use kas::prelude::*;
use kas::theme::TextClass;
use std::fmt::Debug;
impl_scope! {
/// A text label
#[derive(Clone, Debug)]
#[widget]
pub struct AccelLabel {
core: widget_core!(),
class: TextClass,
label: Text<AccelString>,
}
impl Self {
/// Construct from `label`
pub fn new(label: impl Into<AccelString>) -> Self {
AccelLabel {
core: Default::default(),
class: TextClass::AccelLabel(true),
label: Text::new(label.into()),
}
}
/// Set text class (inline)
pub fn with_class(mut self, class: TextClass) -> Self {
self.class = class;
self
}
/// Get the accelerator keys
pub fn keys(&self) -> &[event::VirtualKeyCode] {
self.label.text().keys()
}
}
impl Layout for Self {
fn size_rules(&mut self, size_mgr: SizeMgr, mut axis: AxisInfo) -> SizeRules {
axis.set_default_align_hv(Align::Default, Align::Center);
size_mgr.text_rules(&mut self.label, self.class, axis)
}
fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect) {
self.core.rect = rect;
mgr.text_set_size(&mut self.label, self.class, rect.size, None);
}
fn draw(&mut self, mut draw: DrawMgr) {
draw.text_effects(self.rect(), &self.label, self.class);
}
}
}
impl_scope! {
/// A push-button with a text label
#[derive(Debug)]
#[widget {
layout = button: self.label;
navigable = true;
hover_highlight = true;
}]
pub struct TextButton<M: Clone + Debug + 'static> {
core: widget_core!(),
#[widget]
label: AccelLabel,
message: M,
}
impl Self {
/// Construct a button with given `label`
pub fn new(label: impl Into<AccelString>, message: M) -> Self {
TextButton {
core: Default::default(),
label: AccelLabel::new(label).with_class(TextClass::Button),
message,
}
}
}
impl Widget for Self {
fn configure(&mut self, mgr: &mut ConfigMgr) {
mgr.add_accel_keys(self.id_ref(), self.label.keys());
}
fn handle_event(&mut self, mgr: &mut EventMgr, event: Event) -> Response {
event.on_activate(mgr, self.id(), |mgr| {
mgr.push_msg(self.message.clone());
Response::Used
})
}
}
}
Required Methods§
sourcefn pre_configure(&mut self, mgr: &mut ConfigMgr<'_>, id: WidgetId)
fn pre_configure(&mut self, mgr: &mut ConfigMgr<'_>, id: WidgetId)
Pre-configuration
This method is called before children are configured to assign a
WidgetId
. Usually it does nothing else, but a custom implementation
may be used to affect child configuration, e.g. via
EventState::new_accel_layer
.
Default impl: assign id
to self
Provided Methods§
sourcefn configure(&mut self, mgr: &mut ConfigMgr<'_>)
fn configure(&mut self, mgr: &mut ConfigMgr<'_>)
Configure widget
Widgets are configured on window creation or dynamically via the
parent calling ConfigMgr::configure
. Parent widgets are responsible
for ensuring that children are configured before calling
Layout::size_rules
or Layout::set_rect
. Configuration may be
repeated and may be used as a mechanism to change a child’s WidgetId
,
but this may be expensive.
This method may be used to configure event handling and to load
resources, including resources affecting Layout::size_rules
.
The window’s scale factor (and thus any sizes available through
ConfigMgr::size_mgr
) may not be correct initially (some platforms
construct all windows using scale factor 1) and/or may change in the
future. Changes to the scale factor result in recalculation of
Layout::size_rules
but not repeated configuration.
Examples found in repository?
87 88 89 90 91 92 93 94 95 96 97 98
pub fn configure(&mut self, id: WidgetId, widget: &mut dyn Widget) {
widget.pre_configure(self, id);
for index in 0..widget.num_children() {
let id = widget.make_child_id(index);
if let Some(widget) = widget.get_child_mut(index) {
self.configure(id, widget);
}
}
widget.configure(self);
}
Is this widget navigable via Tab key?
Defaults to false
.
Examples found in repository?
855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
pub fn next_nav_focus_from(
&mut self,
widget: &mut dyn Widget,
id: WidgetId,
key_focus: bool,
) -> bool {
if id == self.nav_focus {
return true;
} else if !self.config.nav_focus {
return false;
}
self.send_action(TkAction::REDRAW);
if let Some(old_id) = self.nav_focus.take() {
self.pending.push_back(Pending::LostNavFocus(old_id));
}
self.clear_char_focus();
if widget
.find_widget(&id)
.map(|w| w.navigable())
.unwrap_or(false)
{
log::trace!(target: "kas_core::event::manager", "set_nav_focus: {id}");
self.nav_focus = Some(id.clone());
self.pending.push_back(Pending::SetNavFocus(id, key_focus));
true
} else {
self.nav_focus = Some(id);
self.next_nav_focus(widget, false, key_focus)
}
}
More examples
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 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
fn nav(
mgr: &mut ConfigMgr,
widget: &mut dyn Widget,
focus: Option<&WidgetId>,
rev: bool,
) -> Option<WidgetId> {
if mgr.ev_state().is_disabled(widget.id_ref()) {
return None;
}
let mut child = focus.and_then(|id| widget.find_child_index(id));
if !rev {
if let Some(index) = child {
if let Some(id) = widget
.get_child_mut(index)
.and_then(|w| nav(mgr, w, focus, rev))
{
return Some(id);
}
} else if !widget.eq_id(focus) && widget.navigable() {
return Some(widget.id());
}
loop {
if let Some(index) = widget.nav_next(mgr, rev, child) {
if let Some(id) = widget
.get_child_mut(index)
.and_then(|w| nav(mgr, w, focus, rev))
{
return Some(id);
}
child = Some(index);
} else {
return None;
}
}
} else {
if let Some(index) = child {
if let Some(id) = widget
.get_child_mut(index)
.and_then(|w| nav(mgr, w, focus, rev))
{
return Some(id);
}
}
loop {
if let Some(index) = widget.nav_next(mgr, rev, child) {
if let Some(id) = widget
.get_child_mut(index)
.and_then(|w| nav(mgr, w, focus, rev))
{
return Some(id);
}
child = Some(index);
} else {
return if !widget.eq_id(focus) && widget.navigable() {
Some(widget.id())
} else {
None
};
}
}
}
}
// Whether to restart from the beginning on failure
let restart = old_nav_focus.is_some();
let mut opt_id = nav(self, widget, old_nav_focus.as_ref(), reverse);
if restart && opt_id.is_none() {
opt_id = nav(self, widget, None, reverse);
}
log::trace!(
target: "kas_core::event::config_mgr",
"next_nav_focus: nav_focus={opt_id:?}",
);
self.nav_focus = opt_id.clone();
if opt_id == old_nav_focus {
return opt_id.is_some();
}
if let Some(id) = old_nav_focus {
self.pending.push_back(Pending::LostNavFocus(id));
}
if let Some(id) = opt_id {
if id != self.sel_focus {
self.clear_char_focus();
}
self.pending.push_back(Pending::SetNavFocus(id, key_focus));
true
} else {
// Most likely an error occurred
self.clear_char_focus();
false
}
}
/// Advance the keyboard navigation focus
///
/// This is similar to [`Self::next_nav_focus`], but looks for the next
/// widget from `id` which is [`Widget::navigable`].
#[inline]
pub fn next_nav_focus_from(
&mut self,
widget: &mut dyn Widget,
id: WidgetId,
key_focus: bool,
) -> bool {
if id == self.nav_focus {
return true;
} else if !self.config.nav_focus {
return false;
}
self.send_action(TkAction::REDRAW);
if let Some(old_id) = self.nav_focus.take() {
self.pending.push_back(Pending::LostNavFocus(old_id));
}
self.clear_char_focus();
if widget
.find_widget(&id)
.map(|w| w.navigable())
.unwrap_or(false)
{
log::trace!(target: "kas_core::event::manager", "set_nav_focus: {id}");
self.nav_focus = Some(id.clone());
self.pending.push_back(Pending::SetNavFocus(id, key_focus));
true
} else {
self.nav_focus = Some(id);
self.next_nav_focus(widget, false, key_focus)
}
}
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
fn start_key_event(&mut self, widget: &mut dyn Widget, vkey: VirtualKeyCode, scancode: u32) {
log::trace!(
"start_key_event: widget={}, vkey={vkey:?}, scancode={scancode}",
widget.id()
);
use VirtualKeyCode as VK;
let opt_command = self.config.shortcuts(|s| s.get(self.modifiers, vkey));
if let Some(cmd) = opt_command {
let mut targets = vec![];
let mut send = |_self: &mut Self, id: WidgetId, cmd| -> bool {
if !targets.contains(&id) {
let used = _self.send_event(widget, id.clone(), Event::Command(cmd));
if used {
_self.add_key_depress(scancode, id.clone());
}
targets.push(id);
used
} else {
false
}
};
if self.char_focus || cmd.suitable_for_sel_focus() {
if let Some(id) = self.sel_focus.clone() {
if send(self, id, cmd) {
return;
}
}
}
if !self.modifiers.alt() {
if let Some(id) = self.nav_focus.clone() {
if send(self, id, cmd) {
return;
}
}
}
if let Some(id) = self.popups.last().map(|popup| popup.1.parent.clone()) {
if send(self, id, cmd) {
return;
}
}
if let Some(id) = self.nav_fallback.clone() {
if send(self, id, cmd) {
return;
}
}
}
// Next priority goes to accelerator keys when Alt is held or alt_bypass is true
let mut target = None;
let mut n = 0;
for (i, id) in (self.popups.iter().rev())
.map(|(_, popup, _)| popup.parent.clone())
.chain(std::iter::once(widget.id()))
.enumerate()
{
if let Some(layer) = self.accel_layers.get(&id) {
// but only when Alt is held or alt-bypass is enabled:
if self.modifiers == ModifiersState::ALT
|| layer.0 && self.modifiers == ModifiersState::empty()
{
if let Some(id) = layer.1.get(&vkey).cloned() {
target = Some(id);
n = i;
break;
}
}
}
}
// If we found a key binding below the top layer, we should close everything above
if n > 0 {
let len = self.popups.len();
for i in ((len - n)..len).rev() {
let id = self.popups[i].0;
self.close_window(id, false);
}
}
if let Some(id) = target {
if widget
.find_widget(&id)
.map(|w| w.navigable())
.unwrap_or(false)
{
self.set_nav_focus(id.clone(), true);
}
self.add_key_depress(scancode, id.clone());
self.send_event(widget, id, Event::Command(Command::Activate));
} else if self.config.nav_focus && vkey == VK::Tab {
self.clear_char_focus();
let shift = self.modifiers.shift();
self.next_nav_focus(widget, shift, true);
} else if vkey == VK::Escape {
if let Some(id) = self.popups.last().map(|(id, _, _)| *id) {
self.close_window(id, true);
}
}
}
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 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
pub fn handle_winit(&mut self, widget: &mut dyn Widget, event: winit::event::WindowEvent) {
use winit::event::{ElementState, MouseScrollDelta, TouchPhase, WindowEvent::*};
match event {
CloseRequested => self.send_action(TkAction::CLOSE),
/* Not yet supported: see #98
DroppedFile(path) => ,
HoveredFile(path) => ,
HoveredFileCancelled => ,
*/
ReceivedCharacter(c) => {
if let Some(id) = self.char_focus() {
// Filter out control codes (Unicode 5.11). These may be
// generated from combinations such as Ctrl+C by some other
// layer. We use our own shortcut system instead.
if c >= '\x20' && !('\x7f'..='\u{9f}').contains(&c) {
let event = Event::ReceivedCharacter(c);
self.send_event(widget, id, event);
}
}
}
Focused(state) => {
self.window_has_focus = state;
if state {
// Required to restart theme animations
self.send_action(TkAction::REDRAW);
} else {
// Window focus lost: close all popups
while let Some(id) = self.popups.last().map(|(id, _, _)| *id) {
self.close_window(id, true);
}
}
}
KeyboardInput {
input,
is_synthetic,
..
} => {
if input.state == ElementState::Pressed && !is_synthetic {
if let Some(vkey) = input.virtual_keycode {
self.start_key_event(widget, vkey, input.scancode);
}
} else if input.state == ElementState::Released {
self.end_key_event(input.scancode);
}
}
ModifiersChanged(state) => {
if state.alt() != self.modifiers.alt() {
// This controls drawing of accelerator key indicators
self.send_action(TkAction::REDRAW);
}
self.modifiers = state;
}
CursorMoved { position, .. } => {
self.last_click_button = FAKE_MOUSE_BUTTON;
let coord = position.cast_approx();
// Update hovered widget
let cur_id = widget.find_id(coord);
let delta = coord - self.last_mouse_coord;
self.set_hover(cur_id.clone());
if let Some(grab) = self.state.mouse_grab.as_mut() {
if grab.mode == GrabMode::Grab {
grab.cur_id = cur_id;
grab.coord = coord;
grab.delta += delta;
} else if let Some(pan) =
self.state.pan_grab.get_mut(usize::conv(grab.pan_grab.0))
{
pan.coords[usize::conv(grab.pan_grab.1)].1 = coord;
}
} else if let Some(id) = self.popups.last().map(|(_, p, _)| p.parent.clone()) {
let source = PressSource::Mouse(FAKE_MOUSE_BUTTON, 0);
let event = Event::PressMove {
source,
cur_id,
coord,
delta,
};
self.send_event(widget, id, event);
} else {
// We don't forward move events without a grab
}
self.last_mouse_coord = coord;
}
// CursorEntered { .. },
CursorLeft { .. } => {
self.last_click_button = FAKE_MOUSE_BUTTON;
if self.mouse_grab().is_none() {
// If there's a mouse grab, we will continue to receive
// coordinates; if not, set a fake coordinate off the window
self.last_mouse_coord = Coord(-1, -1);
self.set_hover(None);
}
}
MouseWheel { delta, .. } => {
if let Some((id, event)) = self.mouse_grab().and_then(|g| g.flush_move()) {
self.send_event(widget, id, event);
}
self.last_click_button = FAKE_MOUSE_BUTTON;
let event = Event::Scroll(match delta {
MouseScrollDelta::LineDelta(x, y) => ScrollDelta::LineDelta(x, y),
MouseScrollDelta::PixelDelta(pos) => {
// The delta is given as a PhysicalPosition, so we need
// to convert to our vector type (Offset) here.
let coord = Coord::conv_approx(pos);
ScrollDelta::PixelDelta(coord.cast())
}
});
if let Some(id) = self.hover.clone() {
self.send_event(widget, id, event);
}
}
MouseInput { state, button, .. } => {
if let Some((id, event)) = self.mouse_grab().and_then(|g| g.flush_move()) {
self.send_event(widget, id, event);
}
let coord = self.last_mouse_coord;
if state == ElementState::Pressed {
let now = Instant::now();
if button != self.last_click_button || self.last_click_timeout < now {
self.last_click_button = button;
self.last_click_repetitions = 0;
}
self.last_click_repetitions += 1;
self.last_click_timeout = now + DOUBLE_CLICK_TIMEOUT;
}
if let Some(grab) = self.remove_mouse_grab() {
if grab.mode == GrabMode::Grab {
// Mouse grab active: send events there
// Note: any button release may end the grab (intended).
let event = Event::PressEnd {
source: PressSource::Mouse(grab.button, grab.repetitions),
end_id: self.hover.clone(),
coord,
success: state == ElementState::Released,
};
self.send_event(widget, grab.start_id, event);
}
// Pan events do not receive Start/End notifications
}
if state == ElementState::Pressed {
if let Some(start_id) = self.hover.clone() {
// No mouse grab but have a hover target
if self.config.mouse_nav_focus() {
if let Some(w) = widget.find_widget(&start_id) {
if w.navigable() {
self.set_nav_focus(w.id(), false);
}
}
}
}
let source = PressSource::Mouse(button, self.last_click_repetitions);
let event = Event::PressStart {
source,
start_id: self.hover.clone(),
coord,
};
self.send_popup_first(widget, self.hover.clone(), event);
}
}
// TouchpadPressure { pressure: f32, stage: i64, },
// AxisMotion { axis: AxisId, value: f64, },
Touch(touch) => {
let source = PressSource::Touch(touch.id);
let coord = touch.location.cast_approx();
match touch.phase {
TouchPhase::Started => {
let start_id = widget.find_id(coord);
if let Some(id) = start_id.as_ref() {
if self.config.touch_nav_focus() {
if let Some(w) = widget.find_widget(id) {
if w.navigable() {
self.set_nav_focus(w.id(), false);
}
}
}
let event = Event::PressStart {
source,
start_id: start_id.clone(),
coord,
};
self.send_popup_first(widget, start_id, event);
}
}
TouchPhase::Moved => {
let cur_id = widget.find_id(coord);
let mut redraw = false;
let mut pan_grab = None;
if let Some(grab) = self.get_touch(touch.id) {
if grab.mode == GrabMode::Grab {
// Only when 'depressed' status changes:
redraw = grab.cur_id != cur_id
&& (grab.start_id == grab.cur_id || grab.start_id == cur_id);
grab.cur_id = cur_id;
grab.coord = coord;
} else {
pan_grab = Some(grab.pan_grab);
}
}
if redraw {
self.send_action(TkAction::REDRAW);
} else if let Some(pan_grab) = pan_grab {
if usize::conv(pan_grab.1) < MAX_PAN_GRABS {
if let Some(pan) = self.pan_grab.get_mut(usize::conv(pan_grab.0)) {
pan.coords[usize::conv(pan_grab.1)].1 = coord;
}
}
}
}
ev @ (TouchPhase::Ended | TouchPhase::Cancelled) => {
if let Some(mut grab) = self.remove_touch(touch.id) {
if let Some((id, event)) = grab.flush_move() {
self.send_event(widget, id, event);
}
if grab.mode == GrabMode::Grab {
let event = Event::PressEnd {
source,
end_id: grab.cur_id.clone(),
coord,
success: ev == TouchPhase::Ended,
};
self.send_event(widget, grab.start_id, event);
}
}
}
}
}
_ => (),
}
}
sourcefn translation(&self) -> Offset
fn translation(&self) -> Offset
Get translation of children relative to this widget
Usually this is zero; only widgets with scrollable or offset content
and child widgets need to implement this.
Such widgets must also implement Widget::handle_scroll
.
Affects event handling via Layout::find_id
and affects the positioning
of pop-up menus. Layout::draw
must be implemented directly using
DrawMgr::with_clip_region
to offset contents.
Examples found in repository?
117 118 119 120 121 122 123 124 125 126 127 128 129
fn find_rect(widget: &dyn Widget, id: WidgetId) -> Option<Rect> {
match widget.find_child_index(&id) {
Some(i) => {
if let Some(w) = widget.get_child(i) {
find_rect(w, id).map(|rect| rect - widget.translation())
} else {
None
}
}
None if widget.eq_id(&id) => Some(widget.rect()),
_ => None,
}
}
More examples
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
fn send_recurse(
&mut self,
widget: &mut dyn Widget,
id: WidgetId,
disabled: bool,
event: Event,
) -> Response {
let mut response = Response::Unused;
if widget.steal_event(self, &id, &event) == Response::Used {
response = Response::Used;
} else if let Some(index) = widget.find_child_index(&id) {
let translation = widget.translation();
if let Some(w) = widget.get_child_mut(index) {
response = self.send_recurse(w, id, disabled, event.clone() + translation);
if self.scroll != Scroll::None {
widget.handle_scroll(self, self.scroll);
}
} else {
log::warn!(
"send_recurse: {} found index {index} for {id} but not child",
widget.identify()
);
}
if matches!(response, Response::Unused) {
response = widget.handle_unused(self, index, event);
} else if self.has_msg() {
widget.handle_message(self, index);
}
} else if disabled {
// event is unused
} else if id == widget.id_ref() {
if event == Event::NavFocus(true) {
self.set_scroll(Scroll::Rect(widget.rect()));
response = Response::Used;
}
response |= widget.pre_handle_event(self, event)
} else {
log::warn!(
"send_recurse: Widget {} cannot find path to {id}",
widget.identify()
);
}
response
}
// Traverse widget tree by recursive call, broadcasting
fn send_all(&mut self, widget: &mut dyn Widget, event: Event) -> usize {
let child_event = event.clone() + widget.translation();
widget.pre_handle_event(self, event);
let mut count = 1;
for index in 0..widget.num_children() {
if let Some(w) = widget.get_child_mut(index) {
count += self.send_all(w, child_event.clone());
}
}
count
}
Navigation in spatial order
Controls Tab navigation order of children. This method should:
- Return
None
if there is no next child - Determine the next child after
from
(if provided) or the whole range, optionally inreverse
order - Ensure that the selected widget is addressable through
WidgetChildren::get_child
Both from
and the return value use the widget index, as used by
WidgetChildren::get_child
.
Default implementation:
- Generated from
#[widget]
’s layout property, if used - Otherwise, iterate through children in order of definition
Examples found in repository?
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 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
fn nav(
mgr: &mut ConfigMgr,
widget: &mut dyn Widget,
focus: Option<&WidgetId>,
rev: bool,
) -> Option<WidgetId> {
if mgr.ev_state().is_disabled(widget.id_ref()) {
return None;
}
let mut child = focus.and_then(|id| widget.find_child_index(id));
if !rev {
if let Some(index) = child {
if let Some(id) = widget
.get_child_mut(index)
.and_then(|w| nav(mgr, w, focus, rev))
{
return Some(id);
}
} else if !widget.eq_id(focus) && widget.navigable() {
return Some(widget.id());
}
loop {
if let Some(index) = widget.nav_next(mgr, rev, child) {
if let Some(id) = widget
.get_child_mut(index)
.and_then(|w| nav(mgr, w, focus, rev))
{
return Some(id);
}
child = Some(index);
} else {
return None;
}
}
} else {
if let Some(index) = child {
if let Some(id) = widget
.get_child_mut(index)
.and_then(|w| nav(mgr, w, focus, rev))
{
return Some(id);
}
}
loop {
if let Some(index) = widget.nav_next(mgr, rev, child) {
if let Some(id) = widget
.get_child_mut(index)
.and_then(|w| nav(mgr, w, focus, rev))
{
return Some(id);
}
child = Some(index);
} else {
return if !widget.eq_id(focus) && widget.navigable() {
Some(widget.id())
} else {
None
};
}
}
}
}
sourcefn handle_event(&mut self, mgr: &mut EventMgr<'_>, event: Event) -> Response
fn handle_event(&mut self, mgr: &mut EventMgr<'_>, event: Event) -> Response
Handle an event sent to this widget
An Event
is some form of user input, timer or notification.
This is the primary event handler for a widget. Secondary handlers are:
- If this method returns
Response::Unused
, thenWidget::handle_unused
is called on each parent until the event is used (or the root widget is reached) - If a message is left on the stack by
EventMgr::push_msg
, thenWidget::handle_message
is called on each parent until the stack is empty (failing to empty the stack results in a warning in the log). - If any scroll state is set by
EventMgr::set_scroll
, thenWidget::handle_scroll
is called for each parent
Default implementation: do nothing; return Response::Unused
.
Calling handle_event
It is not recommended to call handle_event
directly except on self
.
Doing so would miss related event handling code such as cursor-hover
effects and calling other event-handling methods on parents.
Instead, one should call EventMgr::send
with the target’s id
.
sourcefn steal_event(
&mut self,
mgr: &mut EventMgr<'_>,
id: &WidgetId,
event: &Event
) -> Response
fn steal_event(
&mut self,
mgr: &mut EventMgr<'_>,
id: &WidgetId,
event: &Event
) -> Response
Potentially steal an event before it reaches a child
This is called on each widget while sending an event, including when the
target is self.
If this returns Response::Used
, the event is not sent further.
Default implementation: return Response::Unused
.
Examples found in repository?
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
fn send_recurse(
&mut self,
widget: &mut dyn Widget,
id: WidgetId,
disabled: bool,
event: Event,
) -> Response {
let mut response = Response::Unused;
if widget.steal_event(self, &id, &event) == Response::Used {
response = Response::Used;
} else if let Some(index) = widget.find_child_index(&id) {
let translation = widget.translation();
if let Some(w) = widget.get_child_mut(index) {
response = self.send_recurse(w, id, disabled, event.clone() + translation);
if self.scroll != Scroll::None {
widget.handle_scroll(self, self.scroll);
}
} else {
log::warn!(
"send_recurse: {} found index {index} for {id} but not child",
widget.identify()
);
}
if matches!(response, Response::Unused) {
response = widget.handle_unused(self, index, event);
} else if self.has_msg() {
widget.handle_message(self, index);
}
} else if disabled {
// event is unused
} else if id == widget.id_ref() {
if event == Event::NavFocus(true) {
self.set_scroll(Scroll::Rect(widget.rect()));
response = Response::Used;
}
response |= widget.pre_handle_event(self, event)
} else {
log::warn!(
"send_recurse: Widget {} cannot find path to {id}",
widget.identify()
);
}
response
}
sourcefn handle_unused(
&mut self,
mgr: &mut EventMgr<'_>,
index: usize,
event: Event
) -> Response
fn handle_unused(
&mut self,
mgr: &mut EventMgr<'_>,
index: usize,
event: Event
) -> Response
Handle an event sent to child index
but left unhandled
Default implementation: call Self::handle_event
with event
.
Examples found in repository?
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
fn send_recurse(
&mut self,
widget: &mut dyn Widget,
id: WidgetId,
disabled: bool,
event: Event,
) -> Response {
let mut response = Response::Unused;
if widget.steal_event(self, &id, &event) == Response::Used {
response = Response::Used;
} else if let Some(index) = widget.find_child_index(&id) {
let translation = widget.translation();
if let Some(w) = widget.get_child_mut(index) {
response = self.send_recurse(w, id, disabled, event.clone() + translation);
if self.scroll != Scroll::None {
widget.handle_scroll(self, self.scroll);
}
} else {
log::warn!(
"send_recurse: {} found index {index} for {id} but not child",
widget.identify()
);
}
if matches!(response, Response::Unused) {
response = widget.handle_unused(self, index, event);
} else if self.has_msg() {
widget.handle_message(self, index);
}
} else if disabled {
// event is unused
} else if id == widget.id_ref() {
if event == Event::NavFocus(true) {
self.set_scroll(Scroll::Rect(widget.rect()));
response = Response::Used;
}
response |= widget.pre_handle_event(self, event)
} else {
log::warn!(
"send_recurse: Widget {} cannot find path to {id}",
widget.identify()
);
}
response
}
sourcefn handle_message(&mut self, mgr: &mut EventMgr<'_>, index: usize)
fn handle_message(&mut self, mgr: &mut EventMgr<'_>, index: usize)
Handler for messages from children/descendants
This method is called when a child leaves a message on the stack. Some parent or ancestor widget should read this message.
The default implementation does nothing.
Examples found in repository?
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
fn send_recurse(
&mut self,
widget: &mut dyn Widget,
id: WidgetId,
disabled: bool,
event: Event,
) -> Response {
let mut response = Response::Unused;
if widget.steal_event(self, &id, &event) == Response::Used {
response = Response::Used;
} else if let Some(index) = widget.find_child_index(&id) {
let translation = widget.translation();
if let Some(w) = widget.get_child_mut(index) {
response = self.send_recurse(w, id, disabled, event.clone() + translation);
if self.scroll != Scroll::None {
widget.handle_scroll(self, self.scroll);
}
} else {
log::warn!(
"send_recurse: {} found index {index} for {id} but not child",
widget.identify()
);
}
if matches!(response, Response::Unused) {
response = widget.handle_unused(self, index, event);
} else if self.has_msg() {
widget.handle_message(self, index);
}
} else if disabled {
// event is unused
} else if id == widget.id_ref() {
if event == Event::NavFocus(true) {
self.set_scroll(Scroll::Rect(widget.rect()));
response = Response::Used;
}
response |= widget.pre_handle_event(self, event)
} else {
log::warn!(
"send_recurse: Widget {} cannot find path to {id}",
widget.identify()
);
}
response
}
sourcefn handle_scroll(&mut self, mgr: &mut EventMgr<'_>, scroll: Scroll)
fn handle_scroll(&mut self, mgr: &mut EventMgr<'_>, scroll: Scroll)
Handler for scrolling
When a child calls EventMgr::set_scroll
with a value other than
Scroll::None
, this method is called. (This method is not called
after Self::handle_event
or other handlers called on self.)
Note that Scroll::Rect
values are in the child’s coordinate space,
and must be translated to the widget’s own coordinate space by this
method (this is not done by the default implementation since any widget
with non-zero translation very likely wants to implement this method
anyway).
If the child is in an independent coordinate space, then this method
should call mgr.set_scroll(Scroll::None)
to avoid any reactions to
child’s scroll requests.
The default implementation does nothing.
Examples found in repository?
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
fn send_recurse(
&mut self,
widget: &mut dyn Widget,
id: WidgetId,
disabled: bool,
event: Event,
) -> Response {
let mut response = Response::Unused;
if widget.steal_event(self, &id, &event) == Response::Used {
response = Response::Used;
} else if let Some(index) = widget.find_child_index(&id) {
let translation = widget.translation();
if let Some(w) = widget.get_child_mut(index) {
response = self.send_recurse(w, id, disabled, event.clone() + translation);
if self.scroll != Scroll::None {
widget.handle_scroll(self, self.scroll);
}
} else {
log::warn!(
"send_recurse: {} found index {index} for {id} but not child",
widget.identify()
);
}
if matches!(response, Response::Unused) {
response = widget.handle_unused(self, index, event);
} else if self.has_msg() {
widget.handle_message(self, index);
}
} else if disabled {
// event is unused
} else if id == widget.id_ref() {
if event == Event::NavFocus(true) {
self.set_scroll(Scroll::Rect(widget.rect()));
response = Response::Used;
}
response |= widget.pre_handle_event(self, event)
} else {
log::warn!(
"send_recurse: Widget {} cannot find path to {id}",
widget.identify()
);
}
response
}