Skip to content

Commit

Permalink
Merge pull request #135 from HKalbasi/mouse
Browse files Browse the repository at this point in the history
Add basic mouse integration
  • Loading branch information
curlpipe authored Sep 13, 2024
2 parents f329013 + a215be6 commit 732a8e4
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 15 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ Cargo.lock
*.swp
*.swo
*.swn
/.vscode
3 changes: 3 additions & 0 deletions config/.oxrc
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ Shift + <-: Previous Tab
]]


-- Configure Mouse Behaviour --
terminal.mouse_enabled = false

-- Configure Syntax Highlighting Colours --
syntax:set("string", {39, 222, 145}) -- Strings in various programming languages
syntax:set("comment", {113, 113, 169}) -- Comments in various programming languages
Expand Down
28 changes: 28 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub struct Config {
pub colors: Rc<RefCell<Colors>>,
pub status_line: Rc<RefCell<StatusLine>>,
pub greeting_message: Rc<RefCell<GreetingMessage>>,
pub terminal: Rc<RefCell<TerminalConfig>>,
}

impl Config {
Expand All @@ -81,20 +82,23 @@ impl Config {
let greeting_message = Rc::new(RefCell::new(GreetingMessage::default()));
let colors = Rc::new(RefCell::new(Colors::default()));
let status_line = Rc::new(RefCell::new(StatusLine::default()));
let terminal = Rc::new(RefCell::new(TerminalConfig::default()));

// Push in configuration globals
lua.globals().set("syntax", syntax_highlighting.clone())?;
lua.globals().set("line_numbers", line_numbers.clone())?;
lua.globals().set("greeting_message", greeting_message.clone())?;
lua.globals().set("status_line", status_line.clone())?;
lua.globals().set("colors", colors.clone())?;
lua.globals().set("terminal", terminal.clone())?;

Ok(Config {
syntax_highlighting,
line_numbers,
greeting_message,
status_line,
colors,
terminal,
})
}

Expand All @@ -115,6 +119,30 @@ impl Config {
}
}

/// For storing general configuration related to the terminal functionality
#[derive(Debug)]
pub struct TerminalConfig {
pub mouse_enabled: bool,
}

impl Default for TerminalConfig {
fn default() -> Self {
Self {
mouse_enabled: true,
}
}
}

impl LuaUserData for TerminalConfig {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("mouse_enabled", |_, this| Ok(this.mouse_enabled));
fields.add_field_method_set("mouse_enabled", |_, this, value| {
this.mouse_enabled = value;
Ok(())
});
}
}

/// For storing configuration information related to syntax highlighting
#[derive(Debug)]
pub struct SyntaxHighlighting {
Expand Down
41 changes: 34 additions & 7 deletions src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,14 @@ use std::time::Instant;
use std::io::{Write, ErrorKind};
use mlua::Lua;

mod mouse;

/// For managing all editing and rendering of cactus
pub struct Editor {
/// Interface for writing to the terminal
pub terminal: Terminal,
/// Whether to rerender the editor on the next cycle
pub needs_rerender: bool,
/// Configuration information for the editor
pub config: Config,
/// Storage of all the documents opened in the editor
Expand All @@ -43,14 +47,16 @@ pub struct Editor {
impl Editor {
/// Create a new instance of the editor
pub fn new(lua: &Lua) -> Result<Self> {
let config = Config::new(lua)?;
Ok(Self {
doc: vec![],
ptr: 0,
terminal: Terminal::new(),
config: Config::new(lua)?,
terminal: Terminal::new(config.terminal.clone()),
config,
active: true,
greet: false,
help: false,
needs_rerender: true,
highlighter: vec![],
feedback: Feedback::None,
command: None,
Expand Down Expand Up @@ -199,7 +205,15 @@ impl Editor {
// Run the editor
self.render()?;
// Wait for an event
match read()? {
let event = read()?;
self.needs_rerender = match event {
CEvent::Mouse(event) => match event.kind {
crossterm::event::MouseEventKind::Moved => false,
_ => true,
},
_ => true,
};
match event {
CEvent::Key(key) => {
// Check period of inactivity and commit events (for undo/redo) if over 10secs
let end = Instant::now();
Expand Down Expand Up @@ -234,6 +248,10 @@ impl Editor {
self.doc_mut().move_home();
}
}
CEvent::Mouse(mouse_event) => {
self.handle_mouse_event(mouse_event);
return Ok(None);
}
_ => (),
}
self.feedback = Feedback::None;
Expand All @@ -258,6 +276,10 @@ impl Editor {

/// Render a single frame of the editor in it's current state
pub fn render(&mut self) -> Result<()> {
if !self.needs_rerender {
return Ok(());
}
self.needs_rerender = false;
self.terminal.hide_cursor()?;
let Size { w, mut h } = size()?;
h = h.saturating_sub(2);
Expand Down Expand Up @@ -340,6 +362,12 @@ impl Editor {
Ok(())
}

fn render_document_tab_header(&self, document: &Document) -> String {
let file_name = document.file_name.clone().unwrap_or_else(|| "[No Name]".to_string());
let modified = if document.modified { "[+]" } else { "" };
format!(" {file_name}{modified} ")
}

/// Render the tab line at the top of the document
fn render_tab_line(&mut self, w: usize) -> Result<()> {
self.terminal.prepare_line(0)?;
Expand All @@ -350,13 +378,12 @@ impl Editor {
Bg(self.config.colors.borrow().tab_inactive_bg.to_color()?)
)?;
for (c, document) in self.doc.iter().enumerate() {
let file_name = document.file_name.clone().unwrap_or_else(|| "[No Name]".to_string());
let modified = if document.modified { "[+]" } else { "" };
let document_header = self.render_document_tab_header(document);
if c == self.ptr {
// Representing the document we're currently looking at
write!(
self.terminal.stdout,
"{}{}{} {file_name}{modified} {}{}{}│",
"{}{}{}{document_header}{}{}{}│",
Bg(self.config.colors.borrow().tab_active_bg.to_color()?),
Fg(self.config.colors.borrow().tab_active_fg.to_color()?),
SetAttribute(Attribute::Bold),
Expand All @@ -366,7 +393,7 @@ impl Editor {
)?;
} else {
// Other document that is currently open
write!(self.terminal.stdout, " {file_name}{modified} │")?;
write!(self.terminal.stdout, "{document_header}│")?;
}
}
write!(self.terminal.stdout, "{}", " ".to_string().repeat(w))?;
Expand Down
60 changes: 60 additions & 0 deletions src/editor/mouse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use crossterm::event::{MouseButton, MouseEvent, MouseEventKind};
use kaolinite::Loc;

use super::Editor;

enum MouseLocation {
File(Loc),
Tabs(usize),
Out,
}

impl Editor {
fn find_mouse_location(&mut self, event: MouseEvent) -> MouseLocation {
if event.row == 0 {
let mut c = event.column + 2;
for (i, doc) in self.doc.iter().enumerate() {
let header_len = self.render_document_tab_header(doc).len() + 1;
c = c.saturating_sub(header_len as u16);
if c == 0 {
return MouseLocation::Tabs(i);
}
}
MouseLocation::Out
} else if (event.column as usize) < self.dent() {
MouseLocation::Out
} else {
let offset = self.doc().offset;
MouseLocation::File(Loc { x: event.column as usize - self.dent() + offset.x, y: (event.row as usize) - 1 + offset.y })
}
}

pub fn handle_mouse_event(&mut self, event: MouseEvent) {
match event.kind {
MouseEventKind::Down(MouseButton::Left) => {
match self.find_mouse_location(event) {
MouseLocation::File(loc) => {
self.doc_mut().goto(&loc);
},
MouseLocation::Tabs(i) => {
self.ptr = i;
},
MouseLocation::Out => (),
}
},
MouseEventKind::ScrollDown | MouseEventKind::ScrollUp => {
match self.find_mouse_location(event) {
MouseLocation::File(_) => {
if event.kind == MouseEventKind::ScrollDown {
self.doc_mut().move_down();
} else {
self.doc_mut().move_up();
}
},
_ => (),
}
}
_ => (),
}
}
}
26 changes: 18 additions & 8 deletions src/ui.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use crate::config::Colors;
use crate::config::{Colors, TerminalConfig};
use crate::error::Result;
use crossterm::{
cursor::{Hide, MoveTo, Show},
execute,
style::{SetBackgroundColor as Bg, SetForegroundColor as Fg, SetAttribute, Attribute},
terminal::{self, Clear, ClearType as ClType, EnterAlternateScreen, LeaveAlternateScreen, EnableLineWrap, DisableLineWrap},
event::{PushKeyboardEnhancementFlags, KeyboardEnhancementFlags},
cursor::{Hide, MoveTo, Show},
event::{EnableMouseCapture, DisableMouseCapture, KeyboardEnhancementFlags, PushKeyboardEnhancementFlags},
execute,
style::{Attribute, SetAttribute, SetBackgroundColor as Bg, SetForegroundColor as Fg},
terminal::{self, Clear, ClearType as ClType, DisableLineWrap, EnableLineWrap, EnterAlternateScreen, LeaveAlternateScreen}
};
use kaolinite::utils::{Size};
use kaolinite::utils::Size;
use std::cell::RefCell;
use std::io::{stdout, Stdout, Write};
use std::rc::Rc;

/// Constant that shows the help message
pub const HELP_TEXT: &str = "
Expand Down Expand Up @@ -84,12 +86,14 @@ impl Feedback {

pub struct Terminal {
pub stdout: Stdout,
pub config: Rc<RefCell<TerminalConfig>>,
}

impl Terminal {
pub fn new() -> Self {
pub fn new(config: Rc<RefCell<TerminalConfig>>) -> Self {
Terminal {
stdout: stdout(),
config,
}
}

Expand All @@ -101,6 +105,9 @@ impl Terminal {
eprintln!("{}", e);
}));
execute!(self.stdout, EnterAlternateScreen, Clear(ClType::All), DisableLineWrap)?;
if self.config.borrow().mouse_enabled {
execute!(self.stdout, EnableMouseCapture)?;
}
terminal::enable_raw_mode()?;
execute!(
self.stdout,
Expand All @@ -115,6 +122,9 @@ impl Terminal {
pub fn end(&mut self) -> Result<()> {
terminal::disable_raw_mode()?;
execute!(self.stdout, LeaveAlternateScreen, EnableLineWrap)?;
if self.config.borrow().mouse_enabled {
execute!(self.stdout, DisableMouseCapture)?;
}
Ok(())
}

Expand Down

0 comments on commit 732a8e4

Please sign in to comment.