use rio_backend::ansi::graphics::{KittyPlacement, StoredImage, VirtualPlacement};
use rio_backend::config::colors::term::TermColors;
use rio_backend::config::CursorConfig;
use rio_backend::crosswords::grid::row::Row;
use rio_backend::crosswords::pos::CursorState;
use rio_backend::crosswords::square::Square;
use rio_backend::event::TerminalDamage;
use rio_backend::selection::SelectionRange;
use rustc_hash::FxHashMap;
use std::time::Instant;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct UIDamage {
pub island: bool,
pub search: bool,
}
impl UIDamage {
pub fn merge(self, other: Self) -> Self {
Self {
island: self.island || other.island,
search: self.search || other.search,
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum BackgroundState {
Set(wgpu::Color),
Reset,
}
#[derive(Clone, Copy, Debug)]
pub enum WindowUpdate {
Background(BackgroundState),
}
#[derive(Default, Clone, Debug)]
pub struct Cursor {
pub state: CursorState,
pub content: char,
pub content_ref: char,
pub is_ime_enabled: bool,
}
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub struct HintLabel {
pub position: rio_backend::crosswords::pos::Pos,
pub label: Vec<char>,
pub is_first: bool,
}
#[derive(Default)]
pub struct RenderableContent {
pub cursor: Cursor,
pub has_blinking_enabled: bool,
pub is_blinking_cursor_visible: bool,
pub selection_range: Option<SelectionRange>,
pub hyperlink_range: Option<SelectionRange>,
pub hint_labels: Vec<HintLabel>,
pub highlighted_hint: Option<crate::hints::HintMatch>,
pub hint_matches: Option<Vec<rio_backend::crosswords::search::Match>>,
pub last_typing: Option<Instant>,
pub last_blink_toggle: Option<Instant>,
pub pending_update: PendingUpdate,
pub background: Option<BackgroundState>,
}
impl RenderableContent {
pub fn new(cursor: Cursor) -> Self {
RenderableContent {
cursor,
has_blinking_enabled: false,
selection_range: None,
hint_labels: Vec::new(),
highlighted_hint: None,
hint_matches: None,
last_typing: None,
last_blink_toggle: None,
hyperlink_range: None,
pending_update: PendingUpdate::default(),
is_blinking_cursor_visible: false,
background: None,
}
}
pub fn from_cursor_config(config_cursor: &CursorConfig) -> Self {
let cursor = Cursor {
content: config_cursor.shape.into(),
content_ref: config_cursor.shape.into(),
state: CursorState::new(config_cursor.shape.into()),
is_ime_enabled: false,
};
Self::new(cursor)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct TerminalSnapshot {
pub colors: TermColors,
pub display_offset: usize,
pub blinking_cursor: bool,
pub visible_rows: Vec<Row<Square>>,
pub style_set: rio_backend::crosswords::style::StyleSet,
pub extras_table: rio_backend::crosswords::grid::ExtrasTable,
pub cursor: CursorState,
pub damage: TerminalDamage,
pub columns: usize,
pub screen_lines: usize,
pub history_size: usize,
pub kitty_virtual_placements: FxHashMap<(u32, u32), VirtualPlacement>,
pub kitty_images: FxHashMap<u32, StoredImage>,
pub kitty_placements: Vec<KittyPlacement>,
pub kitty_graphics_dirty: bool,
}
#[derive(Debug, Default)]
pub struct PendingUpdate {
dirty: bool,
terminal_damage: Option<TerminalDamage>,
ui_damage: UIDamage,
}
impl PendingUpdate {
#[inline]
pub fn is_dirty(&self) -> bool {
self.dirty
}
pub fn set_dirty(&mut self) {
self.dirty = true;
}
pub fn set_terminal_damage(&mut self, damage: TerminalDamage) {
self.dirty = true;
self.terminal_damage = Some(match self.terminal_damage.take() {
None => damage,
Some(existing) => Self::merge_terminal_damages(existing, damage),
});
}
pub fn set_ui_damage(&mut self, damage: UIDamage) {
self.dirty = true;
self.ui_damage = self.ui_damage.merge(damage);
}
pub fn take_terminal_damage(&mut self) -> Option<TerminalDamage> {
self.terminal_damage.take()
}
pub fn take_ui_damage(&mut self) -> UIDamage {
std::mem::take(&mut self.ui_damage)
}
pub fn reset(&mut self) {
self.dirty = false;
}
pub fn merge_terminal_damages(
existing: TerminalDamage,
new: TerminalDamage,
) -> TerminalDamage {
match (existing, new) {
(_, TerminalDamage::Full) | (TerminalDamage::Full, _) => TerminalDamage::Full,
(TerminalDamage::Partial(mut lines1), TerminalDamage::Partial(lines2)) => {
lines1.extend(lines2);
TerminalDamage::Partial(lines1)
}
(TerminalDamage::CursorOnly, TerminalDamage::Partial(lines))
| (TerminalDamage::Partial(lines), TerminalDamage::CursorOnly) => {
TerminalDamage::Partial(lines)
}
(TerminalDamage::CursorOnly, TerminalDamage::CursorOnly) => {
TerminalDamage::CursorOnly
}
(TerminalDamage::Noop, other) | (other, TerminalDamage::Noop) => other,
}
}
}