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
// 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 extension traits

use super::Widget;
use crate::adapter::{MapResponse, Reserve, WithLabel};
use crate::dir::Directional;
use crate::draw::SizeHandle;
use crate::event::{Manager, Response};
use crate::layout::{AxisInfo, SizeRules};
use crate::text::AccelString;
#[allow(unused)]
use kas::Layout;

/// Provides some convenience methods on widgets
pub trait WidgetExt: Widget {
    /// Construct a wrapper widget which maps messages from this widget
    ///
    /// Responses from this widget with a message payload are mapped with `f`.
    fn map_msg<F, M>(self, f: F) -> MapResponse<Self, M>
    where
        F: Fn(&mut Manager, Self::Msg) -> M + 'static,
        Self: Sized,
    {
        MapResponse::new(self, move |mgr, msg| Response::Msg(f(mgr, msg)))
    }

    /// Construct a wrapper widget which discards messages from this widget
    ///
    /// Responses from this widget with a message payload are mapped to
    /// [`Response::None`].
    fn map_msg_discard<M>(self) -> MapResponse<Self, M>
    where
        Self: Sized,
    {
        MapResponse::new(self, |_, _| Response::None)
    }

    /// Construct a wrapper widget which maps message responses from this widget
    ///
    /// Responses from this widget with a message payload are mapped with `f`.
    fn map_response<F, M>(self, f: F) -> MapResponse<Self, M>
    where
        F: Fn(&mut Manager, Self::Msg) -> Response<M> + 'static,
        Self: Sized,
    {
        MapResponse::new(self, f)
    }

    /// Construct a wrapper widget which reserves extra space
    ///
    /// The closure `reserve` should generate `SizeRules` on request, just like
    /// [`Layout::size_rules`]. This can be done by instantiating a temporary
    /// widget, for example:
    ///```
    /// use kas::widget::Label;
    /// use kas::prelude::*;
    ///
    /// let label = Label::new("0").with_reserve(|size_handle, axis| {
    ///     Label::new("00000").size_rules(size_handle, axis)
    /// });
    ///```
    /// Alternatively one may use virtual pixels:
    ///```
    /// use kas::widget::Filler;
    /// use kas::prelude::*;
    ///
    /// let label = Filler::new().with_reserve(|size_handle, axis| {
    ///     let size = size_handle.pixels_from_em(5.0);
    ///     SizeRules::fixed(size.cast_nearest(), (0, 0))
    /// });
    ///```
    /// The resulting `SizeRules` will be the max of those for the inner widget
    /// and the result of the `reserve` closure.
    fn with_reserve<R>(self, r: R) -> Reserve<Self, R>
    where
        R: FnMut(&mut dyn SizeHandle, AxisInfo) -> SizeRules + 'static,
        Self: Sized,
    {
        Reserve::new(self, r)
    }

    /// Construct a wrapper widget adding a label
    fn with_label<D, T>(self, direction: D, label: T) -> WithLabel<Self, D>
    where
        D: Directional,
        T: Into<AccelString>,
        Self: Sized,
    {
        WithLabel::new_with_direction(direction, self, label)
    }
}
impl<W: Widget + ?Sized> WidgetExt for W {}

/// Provides a convenient `.boxed()` method on implementors
//
// Note: this is distinct from WidgetExt to allow this variant on M: Menu + Sized:
// fn boxed(self) -> Box<dyn Menu<Msg = M::Msg>>
pub trait Boxed<T: ?Sized> {
    /// Boxing method
    fn boxed(self) -> Box<T>;
}

impl<W: Widget + Sized> Boxed<dyn Widget<Msg = W::Msg>> for W {
    fn boxed(self) -> Box<dyn Widget<Msg = W::Msg>> {
        Box::new(self)
    }
}