mirror of
https://github.com/zaphar/sheetsui.git
synced 2025-07-22 13:00:22 -04:00
wip: copy/paste from system clipboard
This commit is contained in:
parent
563a885b70
commit
8a76a031cb
@ -18,6 +18,7 @@ The currently supported commands are:
|
|||||||
* `help [topic]` Display help for a given topic.
|
* `help [topic]` Display help for a given topic.
|
||||||
* `export-csv <path>` Export the current sheet to a csv file at `<path>`.
|
* `export-csv <path>` Export the current sheet to a csv file at `<path>`.
|
||||||
* `quit` Quits the application. `q` is a shorthand alias for this command.
|
* `quit` Quits the application. `q` is a shorthand alias for this command.
|
||||||
|
* `system-paste` Paste from the system clipboard
|
||||||
|
|
||||||
<aside>Note that in the case of `quit` and `edit` that we do not currently
|
<aside>Note that in the case of `quit` and `edit` that we do not currently
|
||||||
prompt you if the current spreadsheet has not been saved yet. So your changes
|
prompt you if the current spreadsheet has not been saved yet. So your changes
|
||||||
|
@ -49,3 +49,5 @@ Range selections made from navigation mode will be available to paste into a Cel
|
|||||||
<aside>Note that for `q` this will not currently prompt you if the sheet is not
|
<aside>Note that for `q` this will not currently prompt you if the sheet is not
|
||||||
saved.</aside>
|
saved.</aside>
|
||||||
|
|
||||||
|
Note also that copy paste works with the system clipboard.
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@ select mode from CellEdit mode with `CTRL-r`.
|
|||||||
|
|
||||||
* `h`, `j`, `k`, `l` will navigate around the sheet.
|
* `h`, `j`, `k`, `l` will navigate around the sheet.
|
||||||
* `Ctrl-n`, `Ctrl-p` will navigate between sheets.
|
* `Ctrl-n`, `Ctrl-p` will navigate between sheets.
|
||||||
* `Ctrl-c`, `y` Copy the cell or range contents.
|
* `Ctrl-c`, `y` Copy the cell or range formatted contents.
|
||||||
* `Ctrl-Shift-C`, 'Y' Copy the cell or range formatted content.
|
* `Ctrl-Shift-C`, 'Y' Copy the cell or range content.
|
||||||
* `The spacebar will select the start and end of the range respectively.
|
* `The spacebar will select the start and end of the range respectively.
|
||||||
* `d` will delete the contents of the range leaving any style untouched
|
* `d` will delete the contents of the range leaving any style untouched
|
||||||
* `D` will delete the contents of the range including any style
|
* `D` will delete the contents of the range including any style
|
||||||
|
@ -16,6 +16,7 @@ pub enum Cmd<'a> {
|
|||||||
Edit(&'a str),
|
Edit(&'a str),
|
||||||
Help(Option<&'a str>),
|
Help(Option<&'a str>),
|
||||||
ExportCsv(&'a str),
|
ExportCsv(&'a str),
|
||||||
|
SystemPaste,
|
||||||
Quit,
|
Quit,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +69,9 @@ pub fn parse<'cmd, 'i: 'cmd>(input: &'i str) -> Result<Option<Cmd<'cmd>>, &'stat
|
|||||||
if let Some(cmd) = try_consume_color_cell(cursor.clone())? {
|
if let Some(cmd) = try_consume_color_cell(cursor.clone())? {
|
||||||
return Ok(Some(cmd));
|
return Ok(Some(cmd));
|
||||||
}
|
}
|
||||||
|
if let Some(cmd) = try_consume_system_paste(cursor.clone())? {
|
||||||
|
return Ok(Some(cmd));
|
||||||
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,6 +320,22 @@ fn try_consume_quit<'cmd, 'i: 'cmd>(
|
|||||||
return Ok(Some(Cmd::Quit));
|
return Ok(Some(Cmd::Quit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_consume_system_paste<'cmd, 'i: 'cmd>(
|
||||||
|
mut input: StrCursor<'i>,
|
||||||
|
) -> Result<Option<Cmd<'cmd>>, &'static str> {
|
||||||
|
const LONG: &'static str = "system-paste";
|
||||||
|
|
||||||
|
if compare(input.clone(), LONG) {
|
||||||
|
input.seek(LONG.len());
|
||||||
|
} else {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
if input.remaining() > 0 {
|
||||||
|
return Err("Invalid command: system-paste does not take an argument");
|
||||||
|
}
|
||||||
|
return Ok(Some(Cmd::SystemPaste));
|
||||||
|
}
|
||||||
|
|
||||||
fn try_consume_rename_sheet<'cmd, 'i: 'cmd>(
|
fn try_consume_rename_sheet<'cmd, 'i: 'cmd>(
|
||||||
mut input: StrCursor<'i>,
|
mut input: StrCursor<'i>,
|
||||||
) -> Result<Option<Cmd<'cmd>>, &'static str> {
|
) -> Result<Option<Cmd<'cmd>>, &'static str> {
|
||||||
|
@ -513,6 +513,12 @@ impl<'ws> Workspace<'ws> {
|
|||||||
.set_cell_style(&[("fill.bg_color", &color)], &area)?;
|
.set_cell_style(&[("fill.bg_color", &color)], &area)?;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
Ok(Some(Cmd::SystemPaste)) => {
|
||||||
|
let rows = self.get_rows_from_system_cipboard()?;
|
||||||
|
self.state.clipboard = Some(ClipboardContents::Range(rows));
|
||||||
|
self.paste_range()?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
self.enter_dialog_mode(Markdown::from_str(&format!(
|
self.enter_dialog_mode(Markdown::from_str(&format!(
|
||||||
"Unrecognized commmand {}",
|
"Unrecognized commmand {}",
|
||||||
@ -607,19 +613,19 @@ impl<'ws> Workspace<'ws> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
KeyCode::Char('C') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
|
||||||
self.copy_range(true)?;
|
self.copy_range(true)?;
|
||||||
self.exit_range_select_mode()?;
|
self.exit_range_select_mode()?;
|
||||||
}
|
}
|
||||||
KeyCode::Char('Y') => {
|
|
||||||
self.copy_range(true)?;
|
|
||||||
self.exit_range_select_mode()?;
|
|
||||||
}
|
|
||||||
KeyCode::Char('c') if key.modifiers == KeyModifiers::CONTROL => {
|
|
||||||
self.copy_range(false)?;
|
|
||||||
self.exit_range_select_mode()?;
|
|
||||||
}
|
|
||||||
KeyCode::Char('y') => {
|
KeyCode::Char('y') => {
|
||||||
|
self.copy_range(true)?;
|
||||||
|
self.exit_range_select_mode()?;
|
||||||
|
}
|
||||||
|
KeyCode::Char('C') if key.modifiers == KeyModifiers::CONTROL => {
|
||||||
|
self.copy_range(false)?;
|
||||||
|
self.exit_range_select_mode()?;
|
||||||
|
}
|
||||||
|
KeyCode::Char('Y') => {
|
||||||
self.copy_range(false)?;
|
self.copy_range(false)?;
|
||||||
self.exit_range_select_mode()?;
|
self.exit_range_select_mode()?;
|
||||||
}
|
}
|
||||||
@ -662,7 +668,9 @@ impl<'ws> Workspace<'ws> {
|
|||||||
}
|
}
|
||||||
// TODO(zaphar): Rethink this a bit perhaps?
|
// TODO(zaphar): Rethink this a bit perhaps?
|
||||||
let mut cb = Clipboard::new()?;
|
let mut cb = Clipboard::new()?;
|
||||||
let (html, csv) = self.book.range_to_clipboard_content(AddressRange { start, end })?;
|
let (html, csv) = self
|
||||||
|
.book
|
||||||
|
.range_to_clipboard_content(AddressRange { start, end })?;
|
||||||
cb.set_html(html, Some(csv))?;
|
cb.set_html(html, Some(csv))?;
|
||||||
self.state.clipboard = Some(ClipboardContents::Range(rows));
|
self.state.clipboard = Some(ClipboardContents::Range(rows));
|
||||||
}
|
}
|
||||||
@ -677,6 +685,26 @@ impl<'ws> Workspace<'ws> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_rows_from_system_cipboard(&mut self) -> Result<Vec<Vec<String>>, anyhow::Error> {
|
||||||
|
use arboard::Clipboard;
|
||||||
|
let mut cb = Clipboard::new()?;
|
||||||
|
let txt = match cb.get_text() {
|
||||||
|
Ok(txt) => txt,
|
||||||
|
Err(e) => return Err(anyhow!(e)),
|
||||||
|
};
|
||||||
|
let reader = csv::Reader::from_reader(txt.as_bytes());
|
||||||
|
let mut rows = Vec::new();
|
||||||
|
for rec in reader.into_byte_records() {
|
||||||
|
let record = rec?;
|
||||||
|
let mut row = Vec::with_capacity(record.len());
|
||||||
|
for i in 0..record.len() {
|
||||||
|
row.push(String::from_utf8_lossy(record.get(i).expect("Unexpected failure to get cell row")).to_string());
|
||||||
|
};
|
||||||
|
rows.push(row);
|
||||||
|
}
|
||||||
|
Ok(rows)
|
||||||
|
}
|
||||||
|
|
||||||
fn update_range_selection(&mut self) -> Result<bool, anyhow::Error> {
|
fn update_range_selection(&mut self) -> Result<bool, anyhow::Error> {
|
||||||
Ok(if self.state.range_select.start.is_none() {
|
Ok(if self.state.range_select.start.is_none() {
|
||||||
self.state.range_select.start = Some(self.book.location.clone());
|
self.state.range_select.start = Some(self.book.location.clone());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user