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

//! Event handling: Response type

use super::VoidResponse;
use kas::geom::Rect;

/// Response type from [`Handler::handle`].
///
/// This type wraps [`Handler::Msg`] allowing both custom messages and toolkit
/// messages.
///
/// [`Handler::handle`]: super::Handler::handle
/// [`Handler::Msg`]: super::Handler::Msg
#[derive(Clone, Debug)]
#[must_use]
pub enum Response<M> {
    /// Nothing of external interest
    ///
    /// This implies that the event was consumed, but does not affect parents.
    /// Note that we consider "view changes" (i.e. scrolling) to not be of
    /// external interest.
    None,
    /// Unhandled event
    ///
    /// Indicates that the event was not consumed. An ancestor or the event
    /// manager is thus able to make use of this event.
    Unhandled,
    /// (Keyboard) focus has changed. This region should be made visible.
    Focus(Rect),
    /// Widget wishes to be selected (or have selection status toggled)
    Select,
    /// Notify of update to widget's data
    ///
    /// Widgets which hold editable data should return either this or
    /// [`Response::Msg`] on handling events which update that data.
    /// Note: scrolling/adjusting a view is not considered a data update.
    Update,
    /// Custom message type
    ///
    /// This signals a (possible) update to the widget's data, while passing a
    /// data payload to the parent widget.
    Msg(M),
}

// Unfortunately we cannot write generic `From` / `TryFrom` impls
// due to trait coherence rules, so we impl `from` etc. directly.
impl<M> Response<M> {
    /// Construct `None` or `Msg(msg)`
    #[inline]
    pub fn none_or_msg(opt_msg: Option<M>) -> Self {
        match opt_msg {
            None => Response::None,
            Some(msg) => Response::Msg(msg),
        }
    }

    /// Construct `Update` or `Msg(msg)`
    #[inline]
    pub fn update_or_msg(opt_msg: Option<M>) -> Self {
        match opt_msg {
            None => Response::Update,
            Some(msg) => Response::Msg(msg),
        }
    }

    /// True if variant is `None`
    #[inline]
    pub fn is_none(&self) -> bool {
        match self {
            &Response::None => true,
            _ => false,
        }
    }

    /// True if variant is `Unhandled`
    #[inline]
    pub fn is_unhandled(&self) -> bool {
        match self {
            &Response::Unhandled => true,
            _ => false,
        }
    }

    /// True if variant is `Msg`
    #[inline]
    pub fn is_msg(&self) -> bool {
        match self {
            &Response::Msg(_) => true,
            _ => false,
        }
    }

    /// Map from one `Response` type to another
    ///
    /// Once Rust supports specialisation, this will likely be replaced with a
    /// `From` implementation.
    #[inline]
    pub fn from<N>(r: Response<N>) -> Self
    where
        N: Into<M>,
    {
        r.try_into().unwrap_or_else(|msg| Response::Msg(msg.into()))
    }

    /// Map one `Response` type into another
    ///
    /// Once Rust supports specialisation, this will likely be redundant.
    #[inline]
    pub fn into<N>(self) -> Response<N>
    where
        M: Into<N>,
    {
        Response::from(self)
    }

    /// Try mapping from one `Response` type to another, failing on `Msg`
    /// variant and returning the payload.
    #[inline]
    pub fn try_from<N>(r: Response<N>) -> Result<Self, N> {
        use Response::*;
        match r {
            None => Ok(None),
            Unhandled => Ok(Unhandled),
            Focus(rect) => Ok(Focus(rect)),
            Select => Ok(Select),
            Update => Ok(Update),
            Msg(m) => Err(m),
        }
    }

    /// Try mapping one `Response` type into another, failing on `Msg`
    /// variant and returning the payload.
    #[inline]
    pub fn try_into<N>(self) -> Result<Response<N>, M> {
        Response::try_from(self)
    }
}

impl VoidResponse {
    /// Convert a `Response<VoidMsg>` to another `Response`
    pub fn void_into<M>(self) -> Response<M> {
        self.try_into().unwrap_or(Response::None)
    }
}

impl<M> From<M> for Response<M> {
    fn from(msg: M) -> Self {
        Response::Msg(msg)
    }
}