mod render;
use std::rc::Rc;
use gpui::{AnyElement, EventEmitter};
use crate::prelude::*;
pub trait TabContent: 'static {
fn render(&self, focused: bool, window: &mut Window, cx: &mut App) -> AnyElement;
fn title(&self) -> SharedString;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TabId(u64);
pub enum PaneEvent {
Empty,
CloseRequested,
}
struct PlaceholderTab;
impl TabContent for PlaceholderTab {
fn render(&self, _focused: bool, _window: &mut Window, _cx: &mut App) -> AnyElement {
div()
.p_4()
.child(Label::new("Empty tab").color(Color::Muted))
.into_any_element()
}
fn title(&self) -> SharedString {
"Untitled".into()
}
}
pub struct Pane {
tabs: Vec<(TabId, Box<dyn TabContent>)>,
active_idx: usize,
next_tab_id: u64,
new_tab_factory: Rc<dyn Fn() -> Box<dyn TabContent>>,
focused: bool,
}
impl Pane {
pub fn new() -> Self {
Self {
tabs: Vec::new(),
active_idx: 0,
next_tab_id: 0,
new_tab_factory: Rc::new(|| Box::new(PlaceholderTab)),
focused: true,
}
}
pub fn set_focused(&mut self, focused: bool, cx: &mut Context<Self>) {
if self.focused != focused {
self.focused = focused;
cx.notify();
}
}
pub fn with_new_tab_factory(
mut self,
factory: impl Fn() -> Box<dyn TabContent> + 'static,
) -> Self {
self.new_tab_factory = Rc::new(factory);
self
}
pub fn with_tab(mut self, content: Box<dyn TabContent>) -> Self {
let id = TabId(self.next_tab_id);
self.next_tab_id += 1;
self.tabs.push((id, content));
self.active_idx = self.tabs.len() - 1;
self
}
pub fn add_tab(&mut self, content: Box<dyn TabContent>, cx: &mut Context<Self>) -> TabId {
let id = TabId(self.next_tab_id);
self.next_tab_id += 1;
self.tabs.push((id, content));
self.active_idx = self.tabs.len() - 1;
cx.notify();
id
}
pub fn close_tab(&mut self, idx: usize, cx: &mut Context<Self>) -> bool {
if idx >= self.tabs.len() {
return self.tabs.is_empty();
}
self.tabs.remove(idx);
if self.active_idx >= self.tabs.len() {
self.active_idx = self.tabs.len().saturating_sub(1);
} else if idx < self.active_idx {
self.active_idx -= 1;
}
let now_empty = self.tabs.is_empty();
if now_empty {
cx.emit(PaneEvent::Empty);
}
cx.notify();
now_empty
}
pub fn activate(&mut self, idx: usize, cx: &mut Context<Self>) {
if idx < self.tabs.len() && idx != self.active_idx {
self.active_idx = idx;
cx.notify();
}
}
pub fn reorder(&mut self, from: usize, to: usize, cx: &mut Context<Self>) {
if from == to || from >= self.tabs.len() || to >= self.tabs.len() {
return;
}
let active_id = self.tabs.get(self.active_idx).map(|(id, _)| *id);
let tab = self.tabs.remove(from);
self.tabs.insert(to, tab);
if let Some(active_id) = active_id
&& let Some(ix) = self.tabs.iter().position(|(id, _)| *id == active_id)
{
self.active_idx = ix;
}
cx.notify();
}
pub fn tab_count(&self) -> usize {
self.tabs.len()
}
pub fn active_index(&self) -> usize {
self.active_idx
}
pub fn titles(&self) -> Vec<SharedString> {
self.tabs
.iter()
.map(|(_, content)| content.title())
.collect()
}
}
impl Default for Pane {
fn default() -> Self {
Self::new()
}
}
impl EventEmitter<PaneEvent> for Pane {}