summaryrefslogtreecommitdiff
path: root/platforms/tortuise-script-compiled/src/buffer.rs
blob: c60ee40cfa1689a0ea80c699f973940130055b21 (plain)
use std::time::Duration;

use crossterm::style::ContentStyle;
use serde::{Deserialize, Serialize};
use tortuise::script::Operation;

use crate::ScriptContext;

#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct Cell {
	pub character: char,
	pub foreground_color: Color,
	pub background_color: Color,
	pub underline_color: Color,
	pub attributes: Attributes,
	pub x: u16,
	pub y: u16,
	pub z: u8,
}

#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct WindowSize {
	pub rows: u16,
	pub columns: u16,
}

#[derive(
	Copy, Clone, Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize,
)]
pub enum Color {
	/// Resets the terminal color.
	#[default]
	Reset,

	/// Black color.
	Black,

	/// Dark grey color.
	DarkGrey,

	/// Light red color.
	Red,

	/// Dark red color.
	DarkRed,

	/// Light green color.
	Green,

	/// Dark green color.
	DarkGreen,

	/// Light yellow color.
	Yellow,

	/// Dark yellow color.
	DarkYellow,

	/// Light blue color.
	Blue,

	/// Dark blue color.
	DarkBlue,

	/// Light magenta color.
	Magenta,

	/// Dark magenta color.
	DarkMagenta,

	/// Light cyan color.
	Cyan,

	/// Dark cyan color.
	DarkCyan,

	/// White color.
	White,

	/// Grey color.
	Grey,

	/// An RGB color. See [RGB color model](https://en.wikipedia.org/wiki/RGB_color_model) for more info.
	///
	/// Most UNIX terminals and Windows 10 supported only.
	/// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info.
	Rgb { r: u8, g: u8, b: u8 },

	/// An ANSI color. See [256 colors - cheat sheet](https://jonasjacek.github.io/colors/) for more info.
	///
	/// Most UNIX terminals and Windows 10 supported only.
	/// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info.
	AnsiValue(u8),
}

#[derive(
	Copy, Clone, Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize,
)]
pub struct Attributes(u32);

pub enum Attribute {
	/// Increases the text intensity.
	Bold = 1,
	/// Decreases the text intensity.
	Dim = 2,
	/// Emphasises the text.
	Italic = 4,
	/// Underlines the text.
	Underlined = 8,

	// Other types of underlining
	/// Double underlines the text.
	DoubleUnderlined = 16,
	/// Undercurls the text.
	Undercurled = 32,
	/// Underdots the text.
	Underdotted = 64,
	/// Underdashes the text.
	Underdashed = 128,

	/// Makes the text blinking (< 150 per minute).
	SlowBlink = 256,
	/// Makes the text blinking (>= 150 per minute).
	RapidBlink = 512,
	/// Swaps foreground and background colors.
	Reverse = 1024,
	/// Hides the text (also known as Conceal).
	Hidden = 2048,
	/// Crosses the text.
	CrossedOut = 4096,
	/// Sets the [Fraktur](https://en.wikipedia.org/wiki/Fraktur) typeface.
	///
	/// Mostly used for [mathematical alphanumeric symbols](https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols).
	Fraktur = 8192,
	/// Makes the text framed.
	Framed = 16384,
	/// Makes the text encircled.
	Encircled = 32768,
	/// Draws a line at the top of the text.
	OverLined = 65536,
}

impl Attributes {
	pub fn none() -> Self {
		Self(0)
	}

	pub fn with(self, attribute: Attribute) -> Self {
		Self(self.0 | attribute as u32)
	}

	pub fn without(self, attribute: Attribute) -> Self {
		Self(self.0 & !(attribute as u32))
	}

	pub fn set(&mut self, attribute: Attribute) {
		self.0 |= attribute as u32;
	}

	pub fn unset(&mut self, attribute: Attribute) {
		self.0 &= !(attribute as u32)
	}

	pub fn has(self, attribute: Attribute) -> bool {
		self.0 & (attribute as u32) != 0
	}

	pub fn extend(&mut self, attributes: Attributes) {
		self.0 |= attributes.0
	}

	pub fn is_empty(self) -> bool {
		self.0 == 0
	}
}

impl From<Color> for crossterm::style::Color {
	fn from(value: Color) -> Self {
		match value {
			Color::Reset => crossterm::style::Color::Reset,
			Color::Black => crossterm::style::Color::Black,
			Color::DarkGrey => crossterm::style::Color::DarkGrey,
			Color::Red => crossterm::style::Color::Red,
			Color::DarkRed => crossterm::style::Color::DarkRed,
			Color::Green => crossterm::style::Color::Green,
			Color::DarkGreen => crossterm::style::Color::DarkGreen,
			Color::Yellow => crossterm::style::Color::Yellow,
			Color::DarkYellow => crossterm::style::Color::DarkYellow,
			Color::Blue => crossterm::style::Color::Blue,
			Color::DarkBlue => crossterm::style::Color::DarkBlue,
			Color::Magenta => crossterm::style::Color::Magenta,
			Color::DarkMagenta => crossterm::style::Color::DarkMagenta,
			Color::Cyan => crossterm::style::Color::Cyan,
			Color::DarkCyan => crossterm::style::Color::DarkCyan,
			Color::White => crossterm::style::Color::White,
			Color::Grey => crossterm::style::Color::Grey,
			Color::Rgb { r, g, b } => crossterm::style::Color::Rgb { r, g, b },
			Color::AnsiValue(v) => crossterm::style::Color::AnsiValue(v),
		}
	}
}

impl From<Attributes> for crossterm::style::Attributes {
	fn from(value: Attributes) -> Self {
		let mut new = crossterm::style::Attributes::none();

		if value.has(Attribute::Bold) {
			new.set(crossterm::style::Attribute::Bold);
		}
		if value.has(Attribute::Dim) {
			new.set(crossterm::style::Attribute::Dim);
		}
		if value.has(Attribute::Italic) {
			new.set(crossterm::style::Attribute::Italic);
		}
		if value.has(Attribute::Underlined) {
			new.set(crossterm::style::Attribute::Underlined);
		}
		if value.has(Attribute::DoubleUnderlined) {
			new.set(crossterm::style::Attribute::DoubleUnderlined);
		}
		if value.has(Attribute::Undercurled) {
			new.set(crossterm::style::Attribute::Undercurled);
		}
		if value.has(Attribute::Underdotted) {
			new.set(crossterm::style::Attribute::Underdotted);
		}
		if value.has(Attribute::Underdashed) {
			new.set(crossterm::style::Attribute::Underdashed);
		}
		if value.has(Attribute::SlowBlink) {
			new.set(crossterm::style::Attribute::SlowBlink);
		}
		if value.has(Attribute::RapidBlink) {
			new.set(crossterm::style::Attribute::RapidBlink);
		}
		if value.has(Attribute::Reverse) {
			new.set(crossterm::style::Attribute::Reverse);
		}
		if value.has(Attribute::Hidden) {
			new.set(crossterm::style::Attribute::Hidden);
		}
		if value.has(Attribute::CrossedOut) {
			new.set(crossterm::style::Attribute::CrossedOut);
		}
		if value.has(Attribute::Fraktur) {
			new.set(crossterm::style::Attribute::Fraktur);
		}
		if value.has(Attribute::Framed) {
			new.set(crossterm::style::Attribute::Framed);
		}
		if value.has(Attribute::Encircled) {
			new.set(crossterm::style::Attribute::Encircled);
		}
		if value.has(Attribute::OverLined) {
			new.set(crossterm::style::Attribute::OverLined);
		}

		new
	}
}

impl ScriptContext<'_> {
	pub fn add_cell(&mut self, cell: Cell) {
		puffin::profile_function!();
		self.0
			.operation_queue
			.push(Operation::AddCell(tortuise::buffer::Cell {
				character: cell.character,
				style: ContentStyle {
					foreground_color: Some(cell.foreground_color.into()),
					background_color: Some(cell.background_color.into()),
					underline_color: Some(cell.underline_color.into()),
					attributes: cell.attributes.into(),
				},
				x: cell.x,
				y: cell.y,
				z: cell.z,
			}));
	}

	pub fn window_size(&self) -> WindowSize {
		puffin::profile_function!();
		let size = &self.0.buffer.window_size;
		WindowSize {
			rows: size.rows,
			columns: size.columns,
		}
	}

	pub fn set_framerate_limit(&mut self, limit: Option<u32>) {
		puffin::profile_function!();
		let limit = limit.map(|limit| Duration::from_nanos(1_000_000_000 / limit as u64));
		self.0.operation_queue.push(Operation::SetFrameLimit(limit));
	}
}