use gpui::{
App, ElementId, FocusHandle, InteractiveElement, IntoElement, ParentElement, RenderOnce,
StatefulInteractiveElement, Styled, Window, actions, div,
};
use std::sync::Arc;
actions!(
focus_trap,
[
FocusNext,
FocusPrev,
Close,
]
);
type WindowAppCallback = Arc<dyn Fn(&mut Window, &mut App)>;
pub fn focus_trap() -> FocusTrap {
FocusTrap::new()
}
#[derive(IntoElement)]
pub struct FocusTrap {
element_id: Option<ElementId>,
base: gpui::Div,
on_escape: Option<WindowAppCallback>,
on_focus_next: Option<WindowAppCallback>,
on_focus_prev: Option<WindowAppCallback>,
trap_focus: bool,
initial_focus: Option<ElementId>,
}
impl Default for FocusTrap {
fn default() -> Self {
Self::new()
}
}
impl FocusTrap {
pub fn new() -> Self {
Self {
element_id: None,
base: div(),
on_escape: None,
on_focus_next: None,
on_focus_prev: None,
trap_focus: true,
initial_focus: None,
}
}
pub fn id(mut self, id: impl Into<ElementId>) -> Self {
self.element_id = Some(id.into());
self
}
pub fn key(self, key: impl Into<ElementId>) -> Self {
self.id(key)
}
pub fn on_escape<F>(mut self, handler: F) -> Self
where
F: 'static + Fn(&mut Window, &mut App),
{
self.on_escape = Some(Arc::new(handler));
self
}
pub fn on_focus_next<F>(mut self, handler: F) -> Self
where
F: 'static + Fn(&mut Window, &mut App),
{
self.on_focus_next = Some(Arc::new(handler));
self
}
pub fn on_focus_prev<F>(mut self, handler: F) -> Self
where
F: 'static + Fn(&mut Window, &mut App),
{
self.on_focus_prev = Some(Arc::new(handler));
self
}
pub fn trap_focus(mut self, trap: bool) -> Self {
self.trap_focus = trap;
self
}
pub fn initial_focus(mut self, id: impl Into<ElementId>) -> Self {
self.initial_focus = Some(id.into());
self
}
}
impl ParentElement for FocusTrap {
fn extend(&mut self, elements: impl IntoIterator<Item = gpui::AnyElement>) {
self.base.extend(elements);
}
}
impl Styled for FocusTrap {
fn style(&mut self) -> &mut gpui::StyleRefinement {
self.base.style()
}
}
impl InteractiveElement for FocusTrap {
fn interactivity(&mut self) -> &mut gpui::Interactivity {
self.base.interactivity()
}
}
impl StatefulInteractiveElement for FocusTrap {}
impl RenderOnce for FocusTrap {
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
let element_id = self.element_id;
self.base
.id(element_id.unwrap_or_else(|| "focus-trap".into()))
}
}
pub struct FocusTrapState {
pub previous_focus: Option<FocusHandle>,
pub is_active: bool,
}
impl FocusTrapState {
pub fn new() -> Self {
Self {
previous_focus: None,
is_active: false,
}
}
pub fn activate(&mut self, window: &mut Window, cx: &App) {
self.previous_focus = window.focused(cx);
self.is_active = true;
}
pub fn deactivate(&mut self, window: &mut Window, _cx: &mut App) {
if let Some(handle) = &self.previous_focus {
handle.focus(window);
}
self.is_active = false;
}
}
impl Default for FocusTrapState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FocusDirection {
Next,
Previous,
First,
Last,
}
pub mod focus_nav {
use super::*;
pub fn find_next(_container_id: &ElementId, _current_id: &ElementId) -> Option<ElementId> {
None
}
pub fn find_previous(_container_id: &ElementId, _current_id: &ElementId) -> Option<ElementId> {
None
}
pub fn move_focus_to(_element_id: &ElementId, _window: &mut Window) -> bool {
false
}
}