use crate::core::geometry::{Point, Rect};
use crate::core::event::{Event, EventType};
use crate::core::state::StateFlags;
use crate::terminal::Terminal;
use super::window::Window;
use super::editor::Editor;
use super::scrollbar::ScrollBar;
use super::indicator::Indicator;
use super::view::View;
use std::rc::Rc;
use std::cell::RefCell;
struct SharedScrollBar(Rc<RefCell<ScrollBar>>);
impl View for SharedScrollBar {
fn bounds(&self) -> Rect {
self.0.borrow().bounds()
}
fn set_bounds(&mut self, bounds: Rect) {
self.0.borrow_mut().set_bounds(bounds);
}
fn draw(&mut self, terminal: &mut Terminal) {
self.0.borrow_mut().draw(terminal);
}
fn handle_event(&mut self, event: &mut Event) {
self.0.borrow_mut().handle_event(event);
}
fn get_palette(&self) -> Option<crate::core::palette::Palette> {
self.0.borrow().get_palette()
}
fn get_owner_type(&self) -> super::view::OwnerType {
self.0.borrow().get_owner_type()
}
fn set_owner_type(&mut self, owner_type: super::view::OwnerType) {
self.0.borrow_mut().set_owner_type(owner_type);
}
}
struct SharedIndicator(Rc<RefCell<Indicator>>);
impl View for SharedIndicator {
fn bounds(&self) -> Rect {
self.0.borrow().bounds()
}
fn set_bounds(&mut self, bounds: Rect) {
self.0.borrow_mut().set_bounds(bounds);
}
fn draw(&mut self, terminal: &mut Terminal) {
self.0.borrow_mut().draw(terminal);
}
fn handle_event(&mut self, _event: &mut Event) {
}
fn get_palette(&self) -> Option<crate::core::palette::Palette> {
self.0.borrow().get_palette()
}
fn get_owner_type(&self) -> super::view::OwnerType {
self.0.borrow().get_owner_type()
}
fn set_owner_type(&mut self, owner_type: super::view::OwnerType) {
self.0.borrow_mut().set_owner_type(owner_type);
}
}
struct SharedEditor(Rc<RefCell<Editor>>);
impl View for SharedEditor {
fn bounds(&self) -> Rect {
self.0.borrow().bounds()
}
fn set_bounds(&mut self, bounds: Rect) {
self.0.borrow_mut().set_bounds(bounds);
}
fn draw(&mut self, terminal: &mut Terminal) {
self.0.borrow_mut().draw(terminal);
}
fn handle_event(&mut self, event: &mut Event) {
self.0.borrow_mut().handle_event(event);
}
fn can_focus(&self) -> bool {
self.0.borrow().can_focus()
}
fn set_focus(&mut self, focused: bool) {
self.0.borrow_mut().set_focus(focused);
}
fn is_focused(&self) -> bool {
self.0.borrow().is_focused()
}
fn options(&self) -> u16 {
self.0.borrow().options()
}
fn set_options(&mut self, options: u16) {
self.0.borrow_mut().set_options(options);
}
fn state(&self) -> StateFlags {
self.0.borrow().state()
}
fn set_state(&mut self, state: StateFlags) {
self.0.borrow_mut().set_state(state);
}
fn update_cursor(&self, terminal: &mut Terminal) {
self.0.borrow().update_cursor(terminal);
}
fn get_palette(&self) -> Option<crate::core::palette::Palette> {
self.0.borrow().get_palette()
}
fn get_owner_type(&self) -> super::view::OwnerType {
self.0.borrow().get_owner_type()
}
fn set_owner_type(&mut self, owner_type: super::view::OwnerType) {
self.0.borrow_mut().set_owner_type(owner_type);
}
}
pub struct EditWindow {
window: Window,
editor: Rc<RefCell<Editor>>, #[allow(dead_code)] h_scrollbar: Rc<RefCell<ScrollBar>>,
#[allow(dead_code)] v_scrollbar: Rc<RefCell<ScrollBar>>,
#[allow(dead_code)] indicator: Rc<RefCell<Indicator>>,
h_scrollbar_idx: usize,
v_scrollbar_idx: usize,
indicator_idx: usize,
}
impl EditWindow {
pub fn new(bounds: Rect, title: &str) -> Self {
let mut window = Window::new(bounds, title);
let window_width = bounds.width();
let window_height = bounds.height();
let interior_width = window_width - 2; let interior_height = window_height - 2;
let h_bounds = Rect::new(18, window_height - 1, window_width - 2, window_height);
let h_scrollbar = Rc::new(RefCell::new(ScrollBar::new_horizontal(h_bounds)));
let v_bounds = Rect::new(window_width - 1, 1, window_width, window_height - 2);
let v_scrollbar = Rc::new(RefCell::new(ScrollBar::new_vertical(v_bounds)));
let ind_bounds = Rect::new(2, window_height - 1, 16, window_height);
let indicator = Rc::new(RefCell::new(Indicator::new(ind_bounds)));
let editor_bounds = Rect::new(0, 0, interior_width, interior_height);
let editor = Rc::new(RefCell::new(Editor::with_scrollbars(
editor_bounds,
Some(Rc::clone(&h_scrollbar)),
Some(Rc::clone(&v_scrollbar)),
Some(Rc::clone(&indicator)),
)));
window.add(Box::new(SharedEditor(Rc::clone(&editor))));
let h_scrollbar_idx = window.add_frame_child(Box::new(SharedScrollBar(Rc::clone(&h_scrollbar))));
let v_scrollbar_idx = window.add_frame_child(Box::new(SharedScrollBar(Rc::clone(&v_scrollbar))));
let indicator_idx = window.add_frame_child(Box::new(SharedIndicator(Rc::clone(&indicator))));
indicator.borrow_mut().set_value(
Point::new(1, 1),
false,
);
let mut edit_window = Self {
window,
editor,
h_scrollbar,
v_scrollbar,
indicator,
h_scrollbar_idx,
v_scrollbar_idx,
indicator_idx,
};
edit_window.window.set_focus(true);
edit_window
}
pub fn load_file(&mut self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
self.editor.borrow_mut().load_file(path)
}
pub fn save_file(&mut self) -> std::io::Result<()> {
self.editor.borrow_mut().save_file()
}
pub fn save_as(&mut self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
self.editor.borrow_mut().save_as(path)
}
pub fn get_filename(&self) -> Option<String> {
self.editor.borrow().get_filename().map(|s| s.to_string())
}
pub fn is_modified(&self) -> bool {
self.editor.borrow().is_modified()
}
pub fn editor_rc(&self) -> Rc<RefCell<Editor>> {
Rc::clone(&self.editor)
}
pub fn set_title(&mut self, title: &str) {
self.window.set_title(title);
}
fn sync_frame_children_positions(&mut self) {
let bounds = self.window.bounds();
let window_width = bounds.width();
let window_height = bounds.height();
if window_height >= 3 {
let h_bounds = Rect::new(
bounds.a.x + 18.min(window_width.saturating_sub(2)),
bounds.a.y + window_height - 1,
bounds.a.x + window_width - 2,
bounds.a.y + window_height,
);
self.window.update_frame_child(self.h_scrollbar_idx, h_bounds);
}
if window_width >= 3 && window_height >= 4 {
let v_bounds = Rect::new(
bounds.a.x + window_width - 1,
bounds.a.y + 1,
bounds.a.x + window_width,
bounds.a.y + window_height - 2,
);
self.window.update_frame_child(self.v_scrollbar_idx, v_bounds);
}
if window_height >= 3 {
let ind_bounds = Rect::new(
bounds.a.x + 2,
bounds.a.y + window_height - 1,
bounds.a.x + 16.min(window_width - 2),
bounds.a.y + window_height,
);
self.window.update_frame_child(self.indicator_idx, ind_bounds);
}
}
}
impl View for EditWindow {
fn bounds(&self) -> Rect {
self.window.bounds()
}
fn set_bounds(&mut self, bounds: Rect) {
self.window.set_bounds(bounds);
let window_width = bounds.width();
let window_height = bounds.height();
let h_bounds = Rect::new(
bounds.a.x + 18,
bounds.a.y + window_height - 1,
bounds.a.x + window_width - 2,
bounds.a.y + window_height,
);
self.window.update_frame_child(self.h_scrollbar_idx, h_bounds);
let v_bounds = Rect::new(
bounds.a.x + window_width - 1,
bounds.a.y + 1,
bounds.a.x + window_width,
bounds.a.y + window_height - 2,
);
self.window.update_frame_child(self.v_scrollbar_idx, v_bounds);
let ind_bounds = Rect::new(
bounds.a.x + 2,
bounds.a.y + window_height - 1,
bounds.a.x + 16,
bounds.a.y + window_height,
);
self.window.update_frame_child(self.indicator_idx, ind_bounds);
}
fn draw(&mut self, terminal: &mut Terminal) {
self.sync_frame_children_positions();
self.window.frame_mut().draw(terminal);
self.window.interior_mut().draw(terminal);
let editor = self.editor.borrow();
let needs_h_scrollbar = editor.needs_horizontal_scrollbar();
let needs_v_scrollbar = editor.needs_vertical_scrollbar();
drop(editor);
if needs_h_scrollbar {
if let Some(child) = self.window.get_frame_child_mut(self.h_scrollbar_idx) {
child.draw(terminal);
}
}
if needs_v_scrollbar {
if let Some(child) = self.window.get_frame_child_mut(self.v_scrollbar_idx) {
child.draw(terminal);
}
}
if let Some(child) = self.window.get_frame_child_mut(self.indicator_idx) {
child.draw(terminal);
}
if self.window.has_shadow() {
self.window.draw_shadow(terminal);
}
}
fn handle_event(&mut self, event: &mut Event) {
let old_bounds = self.window.bounds();
if event.what == EventType::MouseDown || event.what == EventType::MouseMove || event.what == EventType::MouseUp {
let editor = self.editor.borrow();
let needs_h_scrollbar = editor.needs_horizontal_scrollbar();
let needs_v_scrollbar = editor.needs_vertical_scrollbar();
drop(editor);
let mut scrollbar_handled = false;
if needs_h_scrollbar {
if let Some(child) = self.window.get_frame_child_mut(self.h_scrollbar_idx) {
child.handle_event(event);
if event.what == EventType::Nothing {
scrollbar_handled = true;
}
}
}
if !scrollbar_handled && needs_v_scrollbar {
if let Some(child) = self.window.get_frame_child_mut(self.v_scrollbar_idx) {
child.handle_event(event);
if event.what == EventType::Nothing {
scrollbar_handled = true;
}
}
}
if scrollbar_handled {
self.editor.borrow_mut().sync_from_scrollbars();
return;
}
}
self.window.handle_event(event);
let new_bounds = self.window.bounds();
if old_bounds != new_bounds {
let window_width = new_bounds.width();
let window_height = new_bounds.height();
let interior_width = window_width.saturating_sub(2); let interior_height = window_height.saturating_sub(2);
if interior_width > 0 && interior_height > 0 {
let interior_a = Point::new(new_bounds.a.x + 1, new_bounds.a.y + 1); let editor_bounds = Rect::new(
interior_a.x,
interior_a.y,
interior_a.x + interior_width,
interior_a.y + interior_height,
);
self.editor.borrow_mut().set_bounds(editor_bounds);
}
}
}
fn can_focus(&self) -> bool {
true
}
fn options(&self) -> u16 {
self.window.options()
}
fn set_options(&mut self, options: u16) {
self.window.set_options(options);
}
fn state(&self) -> StateFlags {
self.window.state()
}
fn set_state(&mut self, state: StateFlags) {
self.window.set_state(state);
}
fn get_palette(&self) -> Option<crate::core::palette::Palette> {
self.window.get_palette()
}
fn get_end_state(&self) -> crate::core::command::CommandId {
self.window.get_end_state()
}
fn set_end_state(&mut self, command: crate::core::command::CommandId) {
self.window.set_end_state(command);
}
fn set_owner(&mut self, owner: *const dyn View) {
self.window.set_owner(owner);
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::NamedTempFile;
use std::io::Write;
#[test]
fn test_edit_window_creation() {
let bounds = Rect::new(0, 0, 80, 25);
let window = EditWindow::new(bounds, "Test Editor");
assert_eq!(window.bounds(), bounds);
assert!(!window.is_modified());
}
#[test]
fn test_edit_window_file_operations() {
let bounds = Rect::new(0, 0, 80, 25);
let mut window = EditWindow::new(bounds, "Test Editor");
let mut file = NamedTempFile::new().unwrap();
writeln!(file, "Test content").unwrap();
file.flush().unwrap();
let path = file.path().to_str().unwrap();
window.load_file(path).unwrap();
assert_eq!(window.get_filename(), Some(path.to_string()));
assert!(!window.is_modified());
let file2 = NamedTempFile::new().unwrap();
let path2 = file2.path().to_str().unwrap();
window.save_as(path2).unwrap();
assert_eq!(window.get_filename(), Some(path2.to_string()));
}
#[test]
fn test_edit_window_editor_access() {
let bounds = Rect::new(0, 0, 80, 25);
let window = EditWindow::new(bounds, "Test Editor");
let editor = window.editor_rc();
editor.borrow_mut().set_text("Hello, World!");
assert_eq!(editor.borrow().get_text(), "Hello, World!");
}
}
pub struct EditWindowBuilder {
bounds: Option<Rect>,
title: Option<String>,
}
impl EditWindowBuilder {
pub fn new() -> Self {
Self { bounds: None, title: None }
}
#[must_use]
pub fn bounds(mut self, bounds: Rect) -> Self {
self.bounds = Some(bounds);
self
}
#[must_use]
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
pub fn build(self) -> EditWindow {
let bounds = self.bounds.expect("EditWindow bounds must be set");
let title = self.title.expect("EditWindow title must be set");
EditWindow::new(bounds, &title)
}
pub fn build_boxed(self) -> Box<EditWindow> {
Box::new(self.build())
}
}
impl Default for EditWindowBuilder {
fn default() -> Self {
Self::new()
}
}