use crate::_private::NonExhaustive;
use crate::event::{FocusKeys, HandleEvent, MouseOnly};
use rat_focus::{FocusFlag, HasFocusFlag};
use rat_input::event::{ReadOnly, TextOutcome};
pub use rat_input::textarea::core;
use rat_input::textarea::core::{RopeGraphemes, TextRange};
use rat_input::textarea::TextAreaStyle;
use rat_scrolled::{ScrollingState, ScrollingWidget};
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::prelude::StatefulWidget;
use ratatui::style::Style;
use ratatui::widgets::{Block, StatefulWidgetRef};
use ropey::{Rope, RopeSlice};
#[derive(Debug, Default, Clone)]
pub struct RTextArea<'a> {
widget: rat_input::textarea::TextArea<'a>,
}
#[derive(Debug, Clone)]
pub struct RTextAreaState {
pub widget: rat_input::textarea::TextAreaState,
pub non_exhaustive: NonExhaustive,
}
impl<'a> RTextArea<'a> {
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn styles(mut self, style: TextAreaStyle) -> Self {
self.widget = self.widget.styles(style);
self
}
#[inline]
pub fn style(mut self, style: Style) -> Self {
self.widget = self.widget.style(style);
self
}
#[inline]
pub fn focus_style(mut self, style: Style) -> Self {
self.widget = self.widget.focus_style(style);
self
}
#[inline]
pub fn select_style(mut self, style: Style) -> Self {
self.widget = self.widget.select_style(style);
self
}
pub fn text_style<T: IntoIterator<Item = Style>>(mut self, styles: T) -> Self {
self.widget = self.widget.text_style(styles);
self
}
#[inline]
pub fn block(mut self, block: Block<'a>) -> Self {
self.widget = self.widget.block(block);
self
}
}
impl<'a> StatefulWidgetRef for RTextArea<'a> {
type State = RTextAreaState;
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
self.widget.render_ref(area, buf, &mut state.widget)
}
}
impl<'a> StatefulWidget for RTextArea<'a> {
type State = RTextAreaState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
self.widget.render(area, buf, &mut state.widget)
}
}
impl<'a> ScrollingWidget<RTextAreaState> for RTextArea<'a> {
fn need_scroll(&self, area: Rect, state: &mut RTextAreaState) -> (bool, bool) {
let sy = state.widget.line_len() > area.height as usize;
(true, sy)
}
}
impl Default for RTextAreaState {
fn default() -> Self {
Self {
widget: Default::default(),
non_exhaustive: NonExhaustive,
}
}
}
impl HasFocusFlag for RTextAreaState {
fn focus(&self) -> &FocusFlag {
&self.widget.focus
}
fn area(&self) -> Rect {
self.widget.area
}
}
impl RTextAreaState {
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn clear(&mut self) -> bool {
self.widget.clear()
}
#[inline]
pub fn offset(&self) -> (usize, usize) {
self.widget.offset()
}
#[inline]
pub fn set_offset(&mut self, offset: (usize, usize)) -> bool {
self.widget.set_offset(offset)
}
#[inline]
pub fn cursor(&self) -> (usize, usize) {
self.widget.cursor()
}
#[inline]
pub fn set_cursor(&mut self, cursor: (usize, usize), extend_selection: bool) -> bool {
self.widget.set_cursor(cursor, extend_selection)
}
#[inline]
pub fn anchor(&self) -> (usize, usize) {
self.widget.anchor()
}
#[inline]
pub fn value(&self) -> String {
self.widget.value()
}
#[inline]
pub fn value_range(&self, range: TextRange) -> Option<RopeSlice<'_>> {
self.widget.value_range(range)
}
#[inline]
pub fn value_as_bytes(&self) -> ropey::iter::Bytes<'_> {
self.widget.value_as_bytes()
}
#[inline]
pub fn value_as_chars(&self) -> ropey::iter::Chars<'_> {
self.widget.value_as_chars()
}
#[inline]
pub fn set_value<S: AsRef<str>>(&mut self, s: S) {
self.widget.set_value(s)
}
#[inline]
pub fn set_value_rope(&mut self, s: Rope) {
self.widget.set_value_rope(s);
}
#[inline]
pub fn is_empty(&self) -> bool {
self.widget.is_empty()
}
#[inline]
pub fn line_len(&self) -> usize {
self.widget.line_len()
}
#[inline]
pub fn line_width(&self, n: usize) -> Option<usize> {
self.widget.line_width(n)
}
#[inline]
pub fn line(&self, n: usize) -> Option<RopeGraphemes<'_>> {
self.widget.line(n)
}
#[inline]
pub fn has_selection(&self) -> bool {
self.widget.has_selection()
}
#[inline]
pub fn selection(&self) -> TextRange {
self.widget.selection()
}
#[inline]
pub fn set_selection(&mut self, range: TextRange) -> bool {
self.widget.set_selection(range)
}
#[inline]
pub fn select_all(&mut self) -> bool {
self.widget.select_all()
}
#[inline]
pub fn selected_value(&self) -> Option<RopeSlice<'_>> {
self.widget.selected_value()
}
#[inline]
pub fn clear_styles(&mut self) {
self.widget.clear_styles();
}
#[inline]
pub fn add_style(&mut self, range: TextRange, style: usize) {
self.widget.add_style(range, style);
}
#[inline]
pub fn styles_at(&self, pos: (usize, usize), result: &mut Vec<usize>) {
self.widget.styles_at(pos, result)
}
#[inline]
pub fn byte_pos(&self, byte: usize) -> Option<(usize, usize)> {
self.widget.byte_pos(byte)
}
#[inline]
pub fn byte_at(&self, pos: (usize, usize)) -> Option<(usize, usize)> {
self.widget.byte_at(pos)
}
#[inline]
pub fn char_pos(&self, byte: usize) -> Option<(usize, usize)> {
self.widget.char_pos(byte)
}
#[inline]
pub fn char_at(&self, pos: (usize, usize)) -> Option<usize> {
self.widget.char_at(pos)
}
#[inline]
pub fn insert_char(&mut self, c: char) -> bool {
self.widget.insert_char(c)
}
#[inline]
pub fn insert_newline(&mut self) -> bool {
self.widget.insert_newline()
}
#[inline]
pub fn delete_range(&mut self, range: TextRange) -> bool {
self.widget.delete_range(range)
}
#[inline]
pub fn delete_next_char(&mut self) -> bool {
self.widget.delete_next_char()
}
#[inline]
pub fn delete_prev_char(&mut self) -> bool {
self.widget.delete_prev_char()
}
#[inline]
pub fn delete_next_word(&mut self) -> bool {
self.widget.delete_next_word()
}
#[inline]
pub fn delete_prev_word(&mut self) -> bool {
self.widget.delete_prev_word()
}
pub fn move_left(&mut self, n: usize, extend_selection: bool) -> bool {
self.widget.move_left(n, extend_selection)
}
pub fn move_right(&mut self, n: usize, extend_selection: bool) -> bool {
self.widget.move_right(n, extend_selection)
}
pub fn move_up(&mut self, n: usize, extend_selection: bool) -> bool {
self.widget.move_up(n, extend_selection)
}
pub fn move_down(&mut self, n: usize, extend_selection: bool) -> bool {
self.widget.move_down(n, extend_selection)
}
pub fn move_to_line_start(&mut self, extend_selection: bool) -> bool {
self.widget.move_to_line_start(extend_selection)
}
pub fn move_to_line_end(&mut self, extend_selection: bool) -> bool {
self.widget.move_to_line_end(extend_selection)
}
pub fn move_to_start(&mut self, extend_selection: bool) -> bool {
self.widget.move_to_start(extend_selection)
}
pub fn move_to_end(&mut self, extend_selection: bool) -> bool {
self.widget.move_to_end(extend_selection)
}
pub fn move_to_screen_start(&mut self, extend_selection: bool) -> bool {
self.widget.move_to_screen_start(extend_selection)
}
pub fn move_to_screen_end(&mut self, extend_selection: bool) -> bool {
self.widget.move_to_screen_end(extend_selection)
}
#[inline]
pub fn move_to_next_word(&mut self, extend_selection: bool) -> bool {
self.widget.move_to_next_word(extend_selection)
}
#[inline]
pub fn move_to_prev_word(&mut self, extend_selection: bool) -> bool {
self.widget.move_to_prev_word(extend_selection)
}
pub fn from_screen_col(&self, row: usize, x: usize) -> Option<usize> {
self.widget.from_screen_col(row, x)
}
pub fn to_screen_col(&self, pos: (usize, usize)) -> Option<u16> {
self.widget.to_screen_col(pos)
}
#[inline]
pub fn screen_cursor(&self) -> Option<(u16, u16)> {
if self.is_focused() {
self.widget.screen_cursor()
} else {
None
}
}
#[inline]
pub fn set_screen_cursor(&mut self, cursor: (i16, i16), extend_selection: bool) -> bool {
self.widget.set_screen_cursor(cursor, extend_selection)
}
}
impl ScrollingState for RTextAreaState {
fn vertical_max_offset(&self) -> usize {
self.widget.vertical_max_offset()
}
fn vertical_offset(&self) -> usize {
self.widget.vertical_offset()
}
fn vertical_page(&self) -> usize {
self.widget.vertical_page()
}
fn horizontal_max_offset(&self) -> usize {
self.widget.horizontal_max_offset()
}
fn horizontal_offset(&self) -> usize {
self.widget.horizontal_offset()
}
fn horizontal_page(&self) -> usize {
self.widget.horizontal_page()
}
fn set_vertical_offset(&mut self, offset: usize) -> bool {
self.widget.set_vertical_offset(offset)
}
fn set_horizontal_offset(&mut self, offset: usize) -> bool {
self.widget.set_horizontal_offset(offset)
}
}
impl HandleEvent<crossterm::event::Event, FocusKeys, TextOutcome> for RTextAreaState {
fn handle(&mut self, event: &crossterm::event::Event, _keymap: FocusKeys) -> TextOutcome {
if self.is_focused() {
self.widget.handle(event, FocusKeys)
} else {
TextOutcome::NotUsed
}
}
}
impl HandleEvent<crossterm::event::Event, ReadOnly, TextOutcome> for RTextAreaState {
fn handle(&mut self, event: &crossterm::event::Event, _keymap: ReadOnly) -> TextOutcome {
if self.is_focused() {
self.widget.handle(event, ReadOnly)
} else {
TextOutcome::NotUsed
}
}
}
impl HandleEvent<crossterm::event::Event, MouseOnly, TextOutcome> for RTextAreaState {
fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> TextOutcome {
if self.is_focused() {
self.widget.handle(event, MouseOnly)
} else {
TextOutcome::NotUsed
}
}
}