Skip to main content

ravenrs_app/
frame.rs

1//! Where a view lives: its controllers, its actions and its records.
2
3use std::any::Any;
4use std::cell::{Cell, RefCell};
5use std::collections::VecDeque;
6use std::fmt;
7use std::rc::Rc;
8
9use ravenrs_reactive::State;
10
11use crate::action::{
12    ActionContext, ActionError, ActionInput, ActionKind, ActionResult, REASON_CONTROLLER, fill_url,
13};
14use crate::controller::{ViewController, WindowController};
15use crate::{Action, ModelError, ModelNode, Object, ObjectSpace};
16
17/// What a view shows.
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19#[cfg_attr(feature = "live", ravenrs_live::expose(part = "variants"))]
20pub enum ViewKind {
21    /// Many records.
22    List,
23    /// One record.
24    Detail,
25    /// Other views together.
26    Dashboard,
27}
28
29/// A view, as the model describes it.
30#[derive(Clone, Debug, PartialEq, Eq)]
31#[non_exhaustive]
32#[cfg_attr(feature = "live", ravenrs_live::expose(part = "fields"))]
33pub struct ViewInfo {
34    /// Its id in the model.
35    pub id: String,
36    /// What it shows.
37    pub kind: ViewKind,
38    /// The entity it shows; a dashboard has none.
39    pub entity: Option<String>,
40}
41
42#[cfg_attr(feature = "live", ravenrs_live::expose)]
43impl ViewInfo {
44    /// A view with this id.
45    pub fn new(id: impl Into<String>, kind: ViewKind, entity: Option<&str>) -> Self {
46        Self { id: id.into(), kind, entity: entity.map(str::to_owned) }
47    }
48
49    /// Reads a view from the model.
50    pub fn from_model(root: &ModelNode, id: &str) -> Result<Self, ModelError> {
51        let node =
52            root.at(&["Views", id]).ok_or_else(|| ModelError(format!("the model has no view {id}")))?;
53        let kind = match node.text("Kind") {
54            Some("ListView") => ViewKind::List,
55            Some("DetailView") => ViewKind::Detail,
56            Some("DashboardView") => ViewKind::Dashboard,
57            other => return Err(ModelError(format!("the view {id} is of an unknown kind: {other:?}"))),
58        };
59        Ok(Self::new(id, kind, node.text("Entity")))
60    }
61}
62
63/// Where a frame is, which decides its template and what targets it.
64#[derive(Clone, Copy, Debug, PartialEq, Eq)]
65#[cfg_attr(feature = "live", ravenrs_live::expose(part = "variants"))]
66pub enum FrameContext {
67    /// The main window.
68    Main,
69    /// Inside another view.
70    Nested,
71    /// A window over the others.
72    Popup,
73    /// A window to pick a record.
74    Lookup,
75}
76
77/// What a frame goes through, in order, for whoever shows it.
78#[derive(Clone, Debug, PartialEq, Eq)]
79#[non_exhaustive]
80#[cfg_attr(feature = "live", ravenrs_live::expose(part = "variants"))]
81pub enum FrameEvent {
82    /// The view is about to change, to this one.
83    ViewChanging(Option<String>),
84    /// The view changed, and the controllers that fit it are on.
85    ViewChanged(Option<String>),
86}
87
88type Validator = Rc<dyn Fn(&[String], &ActionContext) -> Result<(), String>>;
89type FrameWatcher = Rc<dyn Fn(&FrameEvent)>;
90
91/// A view waiting for its turn, with the unit of work to take on, if a new one.
92type PendingView = (Option<ViewInfo>, Option<Rc<RefCell<dyn ObjectSpace>>>);
93
94struct ViewSlot {
95    controller: RefCell<Box<dyn ViewController>>,
96    id: String,
97    actions: Vec<Action>,
98    /// Reasons given from outside, and whether it is on now.
99    reasons: RefCell<crate::action::Reasons>,
100    on: Cell<bool>,
101}
102
103struct WindowSlot {
104    controller: RefCell<Box<dyn WindowController>>,
105    actions: Vec<Action>,
106}
107
108type Messenger = Rc<dyn Fn(crate::Message)>;
109
110struct FrameInner {
111    context: FrameContext,
112    space: RefCell<Rc<RefCell<dyn ObjectSpace>>>,
113    model: Option<Rc<ModelNode>>,
114    extensions: RefCell<Vec<Rc<dyn Any>>>,
115    entities: RefCell<Rc<Vec<crate::EntityInfo>>>,
116    view: State<Option<ViewInfo>>,
117    selected: State<Vec<Object>>,
118    popup: State<Option<(String, String)>>,
119    views: Vec<ViewSlot>,
120    windows: Vec<WindowSlot>,
121    watchers: RefCell<Vec<FrameWatcher>>,
122    validator: RefCell<Option<Validator>>,
123    messenger: RefCell<Option<Messenger>>,
124    security: RefCell<Option<Rc<crate::SecurityContext>>>,
125    /// Views asked for while one was being changed to, which wait for it to end.
126    pending: RefCell<VecDeque<PendingView>>,
127    changing: Cell<bool>,
128    /// Background work that started here and did not end yet.
129    jobs: RefCell<Vec<crate::Job>>,
130    job_waker: RefCell<Option<crate::JobWaker>>,
131}
132
133/// Where a view lives: it holds the view, the controllers that fit it, their actions and a
134/// unit of work. A handle, so controllers and actions can keep one.
135///
136/// Changing the view tells whoever watches, turns off the controllers that were on, changes the
137/// view, turns on those that fit the new one, and tells again.
138#[derive(Clone)]
139#[cfg_attr(feature = "live", ravenrs_live::expose(part = "fields"))]
140pub struct Frame(Rc<FrameInner>);
141
142impl fmt::Debug for Frame {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        f.debug_struct("Frame").field("context", &self.0.context).finish_non_exhaustive()
145    }
146}
147
148#[cfg_attr(feature = "live", ravenrs_live::expose)]
149impl Frame {
150    /// A frame with no view, with these controllers. Window controllers turn on now, if
151    /// the context fits them, and stay on.
152    pub fn new(
153        context: FrameContext,
154        space: Rc<RefCell<dyn ObjectSpace>>,
155        model: Option<Rc<ModelNode>>,
156        view_controllers: Vec<Box<dyn ViewController>>,
157        window_controllers: Vec<Box<dyn WindowController>>,
158    ) -> Self {
159        let views = view_controllers
160            .into_iter()
161            .map(|controller| ViewSlot {
162                id: controller.id().to_owned(),
163                actions: controller.actions(),
164                controller: RefCell::new(controller),
165                reasons: RefCell::default(),
166                on: Cell::new(false),
167            })
168            .collect();
169        let windows: Vec<WindowSlot> = window_controllers
170            .into_iter()
171            .map(|controller| WindowSlot {
172                actions: controller.actions(),
173                controller: RefCell::new(controller),
174            })
175            .collect();
176        let frame = Self(Rc::new(FrameInner {
177            context,
178            space: RefCell::new(space),
179            model,
180            extensions: RefCell::default(),
181            entities: RefCell::default(),
182            view: State::new(None),
183            selected: State::new(Vec::new()),
184            popup: State::new(None),
185            views,
186            windows,
187            watchers: RefCell::default(),
188            validator: RefCell::default(),
189            messenger: RefCell::default(),
190            security: RefCell::default(),
191            pending: RefCell::default(),
192            changing: Cell::new(false),
193            jobs: RefCell::default(),
194            job_waker: RefCell::default(),
195        }));
196        for slot in &frame.0.windows {
197            let fits = slot.controller.borrow().contexts().contains(&context);
198            for action in &slot.actions {
199                action.set_active(REASON_CONTROLLER, fits);
200            }
201            if fits {
202                slot.controller.borrow_mut().activated(&frame);
203            }
204        }
205        frame.refresh_controllers();
206        frame.refresh_actions();
207        frame
208    }
209
210    /// Gives messages to this, which shows them.
211    pub fn set_messenger(&self, messenger: impl Fn(crate::Message) + 'static) {
212        *self.0.messenger.borrow_mut() = Some(Rc::new(messenger));
213    }
214
215    /// Tells people something, as long as someone showing the application listens.
216    pub fn show_message(&self, message: crate::Message) {
217        let messenger = self.0.messenger.borrow().clone();
218        if let Some(messenger) = messenger {
219            messenger(message);
220        }
221    }
222
223    /// Where it is.
224    pub fn context(&self) -> FrameContext {
225        self.0.context
226    }
227
228    /// The unit of work of its view.
229    pub fn space(&self) -> Rc<RefCell<dyn ObjectSpace>> {
230        self.0.space.borrow().clone()
231    }
232
233    /// The entities whose appearance rules turn actions on and off.
234    pub fn set_entities(&self, entities: Rc<Vec<crate::EntityInfo>>) {
235        *self.0.entities.borrow_mut() = entities;
236        self.refresh_actions();
237    }
238
239    /// Turns actions off by what this person can do. What protects records is the secured space;
240    /// this is what keeps people from reaching for what would be refused.
241    pub fn set_security(&self, security: Rc<crate::SecurityContext>) {
242        *self.0.security.borrow_mut() = Some(security);
243        self.refresh_actions();
244    }
245
246    /// What this person can do, when the application has permissions.
247    pub fn security(&self) -> Option<Rc<crate::SecurityContext>> {
248        self.0.security.borrow().clone()
249    }
250
251    /// Keeps something for whoever holds the frame to find by its type, as what shows it
252    /// does for its controllers and actions.
253    pub fn set_extension<T: 'static>(&self, value: Rc<T>) {
254        self.0.extensions.borrow_mut().retain(|kept| !kept.is::<T>());
255        self.0.extensions.borrow_mut().push(value);
256    }
257
258    pub(crate) fn keep_extension(&self, value: Rc<dyn Any>) {
259        self.0.extensions.borrow_mut().push(value);
260    }
261
262    /// What was kept of this type.
263    pub fn extension<T: 'static>(&self) -> Option<Rc<T>> {
264        let kept = self.0.extensions.borrow().iter().find(|kept| kept.is::<T>()).cloned();
265        kept.and_then(|kept| kept.downcast().ok())
266    }
267
268    /// The application model, when it was given one.
269    pub fn model(&self) -> Option<Rc<ModelNode>> {
270        self.0.model.clone()
271    }
272
273    /// The view it holds.
274    pub fn view(&self) -> Option<ViewInfo> {
275        self.0.view.get()
276    }
277
278    /// The records selected in the view.
279    pub fn selected(&self) -> Vec<Object> {
280        self.0.selected.get()
281    }
282
283    /// The popup open, as its action's id and its view's id.
284    pub fn open_popup(&self) -> Option<(String, String)> {
285        self.0.popup.get()
286    }
287
288    /// Calls this as the view changes.
289    pub fn on_event(&self, watcher: impl Fn(&FrameEvent) + 'static) {
290        self.0.watchers.borrow_mut().push(Rc::new(watcher));
291    }
292
293    /// What passes actions' validation contexts before they run.
294    pub fn set_validator(
295        &self,
296        validator: impl Fn(&[String], &ActionContext) -> Result<(), String> + 'static,
297    ) {
298        *self.0.validator.borrow_mut() = Some(Rc::new(validator));
299    }
300
301    fn tell(&self, event: &FrameEvent) {
302        let watchers = self.0.watchers.borrow().clone();
303        for watcher in watchers {
304            watcher(event);
305        }
306    }
307
308    /// Shows the view with this id in the model.
309    pub fn show_view(&self, id: &str) -> Result<(), ModelError> {
310        let model = self.0.model.as_ref().ok_or_else(|| ModelError("this frame has no model".into()))?;
311        self.set_view(Some(ViewInfo::from_model(model, id)?));
312        Ok(())
313    }
314
315    /// Changes the view. Asked while a change is under way, as from a controller turning on,
316    /// it happens after that one ends.
317    pub fn set_view(&self, view: Option<ViewInfo>) {
318        self.queue_view(view, None);
319    }
320
321    /// Changes the view and takes on a new unit of work with it, as each view has its own.
322    /// The controllers that turn on already see it.
323    pub fn set_view_in(&self, view: Option<ViewInfo>, space: Rc<RefCell<dyn ObjectSpace>>) {
324        self.queue_view(view, Some(space));
325    }
326
327    fn queue_view(&self, view: Option<ViewInfo>, space: Option<Rc<RefCell<dyn ObjectSpace>>>) {
328        self.0.pending.borrow_mut().push_back((view, space));
329        if self.0.changing.replace(true) {
330            return;
331        }
332        loop {
333            let next = self.0.pending.borrow_mut().pop_front();
334            let Some((next, space)) = next else { break };
335            self.change_view(next, space);
336        }
337        self.0.changing.set(false);
338    }
339
340    fn change_view(&self, view: Option<ViewInfo>, space: Option<Rc<RefCell<dyn ObjectSpace>>>) {
341        let id = view.as_ref().map(|v| v.id.clone());
342        self.tell(&FrameEvent::ViewChanging(id.clone()));
343        for slot in &self.0.views {
344            self.turn(slot, false);
345        }
346        if let Some(space) = space {
347            *self.0.space.borrow_mut() = space;
348        }
349        self.0.view.set(view);
350        self.0.selected.set(Vec::new());
351        self.refresh_controllers();
352        self.refresh_actions();
353        self.tell(&FrameEvent::ViewChanged(id));
354    }
355
356    fn turn(&self, slot: &ViewSlot, on: bool) {
357        if slot.on.replace(on) != on {
358            if on {
359                slot.controller.borrow_mut().activated(self);
360            } else {
361                slot.controller.borrow_mut().deactivated(self);
362            }
363        }
364    }
365
366    fn refresh_controllers(&self) {
367        let view = self.0.view.peek().clone();
368        for slot in &self.0.views {
369            let fits = slot.controller.borrow().scope().covers(view.as_ref(), self.0.context);
370            let on = fits && view.is_some() && slot.reasons.borrow().all();
371            for action in &slot.actions {
372                action.set_active(REASON_CONTROLLER, on);
373            }
374            self.turn(slot, on);
375        }
376    }
377
378    fn refresh_actions(&self) {
379        let view = self.0.view.peek().clone();
380        let selected = self.0.selected.peek().clone();
381        let entities = self.0.entities.borrow().clone();
382        let environment = crate::environment();
383        let entity = view
384            .as_ref()
385            .and_then(|view| view.entity.as_deref())
386            .and_then(|name| entities.iter().find(|entity| entity.name == name));
387        for action in self.actions() {
388            action.update(view.as_ref(), self.0.context, &selected, &environment);
389            let (mut enabled, mut visible) = (true, true);
390            if let (Some(entity), Some(view)) = (entity, view.as_ref()) {
391                // One record that does not fit is enough to turn it off.
392                let records: Vec<Option<&Object>> =
393                    if selected.is_empty() { vec![None] } else { selected.iter().map(Some).collect() };
394                for record in records {
395                    let found =
396                        entity.appearance_of(crate::AppearanceTarget::Action, action.id(), view, record);
397                    enabled &= found.enabled.unwrap_or(true);
398                    visible &= found.visible.unwrap_or(true);
399                }
400            }
401            // Among many records, what fits changes from row to row: taking the button away
402            // would move its neighbors, so it only fades.
403            if view.as_ref().is_some_and(|view| view.kind == ViewKind::List) && !visible {
404                enabled = false;
405                visible = true;
406            }
407            action.set_enabled(crate::REASON_APPEARANCE, enabled);
408            action.set_active(crate::REASON_APPEARANCE, visible);
409            if let Some(security) = self.0.security.borrow().as_ref() {
410                let (exists, can) = security.with(|permissions| {
411                    let can = match (action.required_operation(), entity) {
412                        (Some(operation), Some(entity)) if selected.is_empty() => {
413                            permissions.can(operation, &entity.name, None)
414                        }
415                        (Some(operation), Some(entity)) => selected
416                            .iter()
417                            .all(|record| permissions.can(operation, &entity.name, Some(record))),
418                        _ => true,
419                    };
420                    (permissions.can_use_action(action.id()), can)
421                });
422                action.set_active(crate::REASON_SECURITY, exists);
423                action.set_enabled(crate::REASON_SECURITY, can);
424            }
425        }
426    }
427
428    /// Tells the controllers that are on that the view's controls exist now.
429    pub fn view_controls_created(&self) {
430        for slot in self.0.views.iter().filter(|slot| slot.on.get()) {
431            slot.controller.borrow_mut().view_controls_created(self);
432        }
433    }
434
435    /// Gives a reason for a controller being on or off. Off, it turns off all its actions.
436    pub fn set_controller_active(&self, id: &str, reason: &str, on: bool) {
437        if let Some(slot) = self.0.views.iter().find(|slot| slot.id == id) {
438            slot.reasons.borrow_mut().set(reason, on);
439            self.refresh_controllers();
440        }
441    }
442
443    /// Whether a controller is on.
444    pub fn is_controller_active(&self, id: &str) -> bool {
445        self.0.views.iter().any(|slot| slot.id == id && slot.on.get())
446    }
447
448    /// Sets what is selected, which turns actions on and off.
449    pub fn set_selected(&self, selected: Vec<Object>) {
450        self.0.selected.set(selected);
451        self.refresh_actions();
452    }
453
454    /// Every action of its controllers, whether on or off, in the order they came.
455    pub fn actions(&self) -> Vec<Action> {
456        let window = self.0.windows.iter().flat_map(|slot| slot.actions.iter());
457        let view = self.0.views.iter().flat_map(|slot| slot.actions.iter());
458        window.chain(view).cloned().collect()
459    }
460
461    /// The action with this id.
462    pub fn action(&self, id: &str) -> Option<Action> {
463        self.actions().into_iter().find(|action| action.id() == id)
464    }
465
466    fn context_for(&self, input: ActionInput) -> ActionContext {
467        ActionContext::new(self.clone(), self.space(), self.0.selected.get(), self.0.view.get(), input)
468    }
469
470    /// Runs an action, if it is on: asks whoever guards it, passes its validation contexts, runs
471    /// it, and says what comes next. Asking the question first is up to whoever shows it.
472    pub fn execute(&self, id: &str, input: ActionInput) -> Result<ActionResult, ActionError> {
473        let action = self.action(id).ok_or_else(|| ActionError::Unknown(id.to_owned()))?;
474        let against = |reasons: crate::action::Reasons| reasons.against().map(str::to_owned).collect();
475        if !action.is_active() {
476            return Err(ActionError::Inactive(against(action.active_reasons())));
477        }
478        if !action.is_enabled() {
479            return Err(ActionError::Disabled(against(action.enabled_reasons())));
480        }
481        action.check_input(&input)?;
482        let cx = self.context_for(input);
483        if !action.run_guard(&cx) {
484            return Err(ActionError::Canceled);
485        }
486        match action.kind() {
487            ActionKind::Popup { view } => {
488                self.0.popup.set(Some((action.id().to_owned(), view.clone())));
489                return Ok(ActionResult::Popup(view.clone()));
490            }
491            ActionKind::Url(template) => {
492                let address = fill_url(template, cx.selected.first());
493                action.run(&cx).map_err(ActionError::Failed)?;
494                action.run_notice(&cx);
495                return Ok(ActionResult::OpenUrl(address));
496            }
497            _ => {}
498        }
499        self.finish(&action, &cx)
500    }
501
502    fn finish(&self, action: &Action, cx: &ActionContext) -> Result<ActionResult, ActionError> {
503        if !action.contexts().is_empty() {
504            let validator = self.0.validator.borrow().clone();
505            if let Some(validator) = validator {
506                validator(action.contexts(), cx).map_err(ActionError::Failed)?;
507            }
508        }
509        action.run(cx).map_err(ActionError::Failed)?;
510        if let Some(work) = action.work() {
511            let waker = self.0.job_waker.borrow().clone();
512            let job = crate::Job::start(
513                action.id(),
514                action.caption(),
515                work,
516                cx.selected.clone(),
517                cx.input.clone(),
518                waker,
519            );
520            let id = job.id();
521            action.set_enabled(crate::REASON_RUNNING, false);
522            self.0.jobs.borrow_mut().push(job);
523            return Ok(ActionResult::Started(id));
524        }
525        action.run_notice(cx);
526        let next = cx.take_next().unwrap_or(ActionResult::Done);
527        if let ActionResult::ShowView(id) = &next {
528            self.show_view(id).map_err(|e| ActionError::Failed(e.0))?;
529        }
530        Ok(next)
531    }
532
533    /// Wakes whoever shows the frame, from any thread, when background work has something to show.
534    pub fn set_job_waker(&self, waker: crate::JobWaker) {
535        for job in self.0.jobs.borrow().iter() {
536            job.set_waker(Some(waker.clone()));
537        }
538        *self.0.job_waker.borrow_mut() = Some(waker);
539    }
540
541    /// The background work that runs now, in the order it started.
542    pub fn jobs(&self) -> Vec<crate::Job> {
543        self.0.jobs.borrow().clone()
544    }
545
546    /// Keeps what background work handed back, tells what it asked to, and ends the work that
547    /// ended: its action turns on again and says it ran. Says whether records changed, so what
548    /// shows them reads again. Whoever shows the frame calls it once it is woken.
549    pub fn apply_jobs(&self) -> bool {
550        let jobs = self.jobs();
551        let mut changed = false;
552        for job in &jobs {
553            changed |= self.keep_changes(job.take_changes());
554            let Some(end) = job.take_end() else { continue };
555            self.0.jobs.borrow_mut().retain(|running| running.id() != job.id());
556            let action = self.action(job.action());
557            if let Some(action) = &action {
558                action.set_enabled(crate::REASON_RUNNING, true);
559            }
560            match end {
561                Ok(()) => {
562                    if let Some(action) = action {
563                        action.run_notice(&self.context_for(ActionInput::None));
564                    }
565                }
566                Err(error) => self.show_message(crate::Message::new(error, crate::MessageKind::Error)),
567            }
568        }
569        changed
570    }
571
572    fn keep_changes(&self, changes: Vec<crate::JobChange>) -> bool {
573        let space = self.space();
574        let mut wrote = false;
575        let mut failed = None;
576        for change in changes {
577            let done = match change {
578                crate::JobChange::Save(record) => space.borrow_mut().save(record).map_err(|e| e.to_string()),
579                crate::JobChange::Delete(record) => {
580                    let entities = self.0.entities.borrow().clone();
581                    let key = entities
582                        .iter()
583                        .find(|entity| entity.name == record.entity())
584                        .and_then(|entity| entity.key_field())
585                        .map(|field| record.get(&field.name));
586                    match key {
587                        Some(key) => {
588                            space.borrow_mut().delete(record.entity(), &key).map_err(|e| e.to_string())
589                        }
590                        None => Err(format!("{} has no key to remove it by", record.entity())),
591                    }
592                }
593                crate::JobChange::Tell(text, kind) => {
594                    self.show_message(crate::Message::new(text, kind));
595                    continue;
596                }
597            };
598            match done {
599                Ok(()) => wrote = true,
600                Err(error) => {
601                    failed = Some(error);
602                    break;
603                }
604            }
605        }
606        if let Some(error) = failed {
607            space.borrow_mut().rollback();
608            self.show_message(crate::Message::new(error, crate::MessageKind::Error));
609            return false;
610        }
611        if wrote && let Err(error) = space.borrow_mut().commit() {
612            space.borrow_mut().rollback();
613            self.show_message(crate::Message::new(error.to_string(), crate::MessageKind::Error));
614            return false;
615        }
616        wrote
617    }
618
619    /// Opens a popup on this view for this action, for when the view depends on what it is run
620    /// on. OK runs the action again, with what was picked.
621    pub fn open_popup_on(&self, action: &str, view: &str) {
622        self.0.popup.set(Some((action.to_owned(), view.to_owned())));
623    }
624
625    /// Fills in the record the form of the open popup starts with.
626    pub fn prepare_popup(&self, object: &mut Object) {
627        let Some((id, _)) = self.0.popup.get() else { return };
628        if let Some(action) = self.action(&id) {
629            action.run_prepare(&self.context_for(ActionInput::None), object);
630        }
631    }
632
633    /// Ends the open popup with OK, which runs its action.
634    pub fn accept_popup(&self, input: ActionInput) -> Result<ActionResult, ActionError> {
635        let (id, _) = self.0.popup.get().ok_or_else(|| ActionError::Unknown("popup".to_owned()))?;
636        let action = self.action(&id).ok_or(ActionError::Unknown(id))?;
637        let cx = self.context_for(input);
638        let done = self.finish(&action, &cx);
639        // A failure leaves it open, with the reason, to try again.
640        if done.is_ok() {
641            self.0.popup.set(None);
642        }
643        done
644    }
645
646    /// Ends the open popup with Cancel, and nothing runs but what cancels.
647    pub fn cancel_popup(&self) {
648        if let Some((id, _)) = self.0.popup.get()
649            && let Some(action) = self.action(&id)
650        {
651            action.run_cancel(&self.context_for(ActionInput::None));
652        }
653        self.0.popup.set(None);
654    }
655
656    /// Ends it: turns off its controllers, the window's last.
657    pub fn close(&self) {
658        self.set_view(None);
659        for slot in &self.0.views {
660            self.turn(slot, false);
661        }
662        for slot in self.0.windows.iter().rev() {
663            if slot.controller.borrow().contexts().contains(&self.0.context) {
664                slot.controller.borrow_mut().deactivated(self);
665            }
666        }
667    }
668}