use crate::ids::{PaneId, TabId, ViewerId};
use crate::layout::{LayoutTree, Rect};
use crate::proto::attach::ServerMessage;
use crate::terminal::ServerTerminal;
use bevy_ecs::prelude::*;
use std::collections::{BTreeMap, VecDeque};
use std::path::PathBuf;
use super::messages::{Requester, ViewerRequest};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Selection {
pub tab: Option<Entity>,
pub focus: BTreeMap<Entity, Entity>,
}
impl Selection {
#[must_use]
pub fn focused(&self) -> Option<Entity> {
self.focus.get(&self.tab?).copied()
}
pub fn set_focus(&mut self, tab: Entity, pane: Entity) {
self.focus.insert(tab, pane);
}
pub fn forget_tab(&mut self, tab: Entity) {
self.focus.remove(&tab);
if self.tab == Some(tab) {
self.tab = None;
}
}
pub fn select(&mut self, tab: Entity, pane: Option<Entity>) {
self.tab = Some(tab);
if let Some(pane) = pane {
self.set_focus(tab, pane);
}
}
pub fn retarget(&mut self, tab: Entity, next: Option<Entity>) {
match next {
Some(next) => self.set_focus(tab, next),
None => {
self.focus.remove(&tab);
}
}
}
}
#[derive(Component, Debug)]
pub struct Workspace {
pub name: String,
pub selection: Selection,
pub last_attached: u64,
pub open: bool,
pub retiring: Option<Retiring>,
pub tab_counter: u32,
}
#[derive(Component, Debug)]
#[relationship(relationship_target = Tabs)]
pub struct TabOf(pub Entity);
#[derive(Component, Debug, Default)]
#[relationship_target(relationship = TabOf)]
pub struct Tabs(Vec<Entity>);
impl std::ops::Deref for Tabs {
type Target = [Entity];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Retiring {
pub since_ms: u64,
pub exit_code: Option<u32>,
}
#[derive(Component, Debug)]
pub struct Tab {
pub id: TabId,
pub workspace: Entity,
pub label: String,
pub layout: LayoutTree<Entity>,
pub geometry: Vec<(Entity, Rect)>,
pub area: Rect,
pub layout_changed: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PaneState {
Starting,
Live {
pid: u32,
},
Eof {
pid: u32,
},
Terminating {
pid: u32,
since_ms: u64,
},
Exited {
code: u32,
},
}
impl PaneState {
#[must_use]
pub fn pid(self) -> Option<u32> {
match self {
Self::Live { pid } | Self::Eof { pid } | Self::Terminating { pid, .. } => Some(pid),
Self::Starting | Self::Exited { .. } => None,
}
}
#[must_use]
pub fn exit_code(self) -> Option<u32> {
match self {
Self::Exited { code } => Some(code),
_ => None,
}
}
#[must_use]
pub fn accepts_input(self) -> bool {
matches!(self, Self::Live { .. })
}
}
#[derive(Component)]
pub struct Pane {
pub id: PaneId,
pub workspace_name: String,
pub workspace_stream: u64,
pub tab: Entity,
pub argv: Vec<String>,
pub cwd: PathBuf,
pub state: PaneState,
pub terminal: ServerTerminal,
pub rect: Rect,
pub dirty: bool,
pub event_pending: bool,
pub last_event_seq: u64,
pub published_title: String,
pub input_sequence: u64,
pub last_output_event_ms: Option<u64>,
pub final_retain_ms: u64,
}
impl Pane {
pub fn refresh(&mut self) -> bool {
if !self.dirty {
return false;
}
self.dirty = false;
self.terminal
.refresh_grid(&self.published_title, self.state.exit_code())
}
#[must_use]
pub fn terminal_size(rect: Rect) -> (u16, u16) {
crate::terminal::clamp_dims(rect.height, rect.width)
}
}
#[derive(Component, Debug)]
pub struct Creation {
pub requesters: Vec<(Requester, u64)>,
pub kind: CreationKind,
}
#[derive(Debug)]
pub enum CreationKind {
Split {
tab: Entity,
target: Entity,
axis: crate::layout::Axis,
},
NewTab { tab: Entity },
Workspace { tab: Entity },
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Sent {
pub rows: u16,
pub columns: u16,
pub seq: u64,
}
#[derive(Component)]
pub struct Viewer {
pub id: ViewerId,
pub workspace: Entity,
pub rows: u16,
pub cols: u16,
pub selection: Selection,
pub queue: VecDeque<ViewerRequest>,
pub barrier: Option<Entity>,
pub generation: u64,
pub layout: Vec<(Entity, Rect)>,
pub sent: BTreeMap<PaneId, Sent>,
pub dirty: bool,
pub pending: bool,
pub publish_now: bool,
pub input_ms: u64,
pub last_frame_ms: u64,
pub notice: Option<String>,
pub after_frame: Vec<ServerMessage>,
pub detaching: bool,
pub exit_sent: bool,
}
impl Viewer {
#[must_use]
pub fn focused(&self) -> Option<Entity> {
self.selection.focused()
}
}