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
use super::super::*;

use binding::*;
use std::sync::*;

///
/// Controller that provides standard behaviour for popups.
/// Supply this as a controller for a control that needs a popup.
/// This will supply the popup container and suppress the popup
/// UI while the popup is not visible.
/// 
pub struct PopupController<ContentController: Controller> {
    /// Controller that provides the main content for the popup
    content_controller: ContentController,

    /// Binding that provides the size of the popup
    popup_size: BindRef<(u32, u32)>,

    /// The direction the popup will appear in
    popup_direction: BindRef<PopupDirection>,

    /// The offset of the popup from the parent control
    offset: BindRef<u32>,

    /// Binding that specifies whether or not the popup is open
    /// (This will set to false if the popup is dismissed)
    open: Binding<bool>,

    /// User interface for this controller
    ui: BindRef<Control>
}

impl<ContentController: Controller> PopupController<ContentController> {
    ///
    /// Creates a new popup controller.
    /// 
    /// Default settings are a size of 100,100, popup center, offset 8
    /// 
    pub fn new(controller: ContentController, is_open: &Binding<bool>) -> PopupController<ContentController> {
        // Create the initial set of bindings
        let content_controller  = controller;
        let open                = is_open.clone();
        let popup_size          = BindRef::from(&(100, 100));
        let popup_direction     = BindRef::from(&PopupDirection::WindowCentered);
        let offset              = BindRef::from(&8);
        let content             = content_controller.ui();

        // Derive the basic UI binding
        let ui                  = Self::create_ui(&content, &open, &popup_size, &popup_direction, &offset);

        // Generate the popup controller
        PopupController {
            content_controller: content_controller,
            open:               open,
            popup_size:         popup_size,
            popup_direction:    popup_direction,
            offset:             offset,
            ui:                 ui
        }
    }

    ///
    /// Returns a modified controller with a different size
    /// 
    pub fn with_size<T: Into<BindRef<(u32, u32)>>>(mut self, size: T) -> PopupController<ContentController> {
        self.popup_size = size.into();
        self.regenerate_ui()
    }

    ///
    /// Returns a modified controller with a different direction
    /// 
    pub fn with_direction<T: Into<BindRef<PopupDirection>>>(mut self, direction: T) -> PopupController<ContentController> {
        self.popup_direction = direction.into();
        self.regenerate_ui()
    }

    ///
    /// Returns a modified controller with a different offset
    /// 
    pub fn with_offset<T: Into<BindRef<u32>>>(mut self, offset: T) -> PopupController<ContentController> {
        self.offset = offset.into();
        self.regenerate_ui()
    }

    ///
    /// Regenerates the UI field from the current bindings
    /// 
    fn regenerate_ui(mut self) -> PopupController<ContentController> {
        self.ui = Self::create_ui(&self.content_controller.ui(), &self.open, &self.popup_size, &self.popup_direction, &self.offset);
        self
    }

    ///
    /// Creates the UI binding for this controller
    /// 
    fn create_ui(content: &BindRef<Control>, open: &Binding<bool>, size: &BindRef<(u32, u32)>, direction: &BindRef<PopupDirection>, offset: &BindRef<u32>) -> BindRef<Control> {
        // Clone the model bits
        let content     = content.clone();
        let open        = open.clone();
        let size        = size.clone();
        let direction   = direction.clone();
        let offset      = offset.clone();

        // Compute the UI
        BindRef::from(computed(move || {
            // Get the standard popup properties
            let open            = open.get();
            let direction       = direction.get();
            let (width, height) = size.get();
            let offset          = offset.get();

            if !open {
                // If not open, then generate an empty popup
                // Not binding the UI here so if the controller updates, nothing happens
                Control::popup()
                    .with(Popup::IsOpen(Property::Bool(false)))
                    .with(Popup::Direction(direction))
                    .with(Popup::Size(width, height))
                    .with(Popup::Offset(offset))
                    .with(ControlAttribute::Padding((8,8), (8,8)))
                    .with(ControlAttribute::ZIndex(1000))
            } else {
                // Bind the UI only when the controller is open
                Control::popup()
                    .with(Popup::IsOpen(Property::Bool(true)))
                    .with(Popup::Direction(direction))
                    .with(Popup::Size(width, height))
                    .with(Popup::Offset(offset))
                    .with(ControlAttribute::Padding((8,8), (8,8)))
                    .with(ControlAttribute::ZIndex(1000))
                    .with((ActionTrigger::Dismiss, "DismissPopup"))
                    .with(vec![
                        content.get()
                    ])
            }
        }))
    }
}

impl<ContentController: Controller> Controller for PopupController<ContentController> {
    fn ui(&self) -> BindRef<Control> {
        self.ui.clone()
    }

    fn get_viewmodel(&self) -> Option<Arc<ViewModel>> {
        self.content_controller.get_viewmodel()
    }

    fn get_subcontroller(&self, id: &str) -> Option<Arc<Controller>> { 
        self.content_controller.get_subcontroller(id)
    }

    fn action(&self, action_id: &str, action_data: &ActionParameter) {
        // Hide the popup if our DismissPopup action is fired
        if action_id == "DismissPopup" {
            self.open.clone().set(false);
        }

        // Pass the action on to the main controller
        self.content_controller.action(action_id, action_data);
    }

    fn get_image_resources(&self) -> Option<Arc<ResourceManager<Image>>> { 
        self.content_controller.get_image_resources()
    }

    fn get_canvas_resources(&self) -> Option<Arc<ResourceManager<BindingCanvas>>> {
        self.content_controller.get_canvas_resources()
    }
}