forked from meli/meli
1
Fork 0

Compare commits

...

1 Commits

Author SHA1 Message Date
Manos Pitsidianakis f5fd051a2c WIP 2022-11-11 15:09:28 +02:00
6 changed files with 276 additions and 32 deletions

View File

@ -2294,7 +2294,9 @@ impl Component for MailView {
UIEvent::Input(ref key)
if !self.cmd_buf.is_empty()
&& self.mode == ViewMode::Url
&& shortcut!(key == shortcuts[MailView::DESCRIPTION]["go_to_url"]) =>
&& (shortcut!(key == shortcuts[MailView::DESCRIPTION]["go_to_url"])
|| shortcut!(key == shortcuts[MailView::DESCRIPTION]["copy_url"])
|| shortcut!(key == shortcuts[MailView::DESCRIPTION]["pipe_url"])) =>
{
let lidx = self.cmd_buf.parse::<usize>().unwrap();
self.cmd_buf.clear();
@ -2330,40 +2332,144 @@ impl Component for MailView {
return true;
}
};
let url_launcher = mailbox_settings!(
context[self.coordinates.0][&self.coordinates.1]
.pager
.url_launcher
)
.as_ref()
.map(|s| s.as_str())
.unwrap_or(
#[cfg(target_os = "macos")]
{
"open"
},
#[cfg(not(target_os = "macos"))]
{
"xdg-open"
},
eprintln!("key is {:?}", key);
eprintln!(
"shotrctu key is {:?}",
shortcuts[MailView::DESCRIPTION]["pipe_url"]
);
match Command::new(url_launcher)
.arg(url)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
{
Ok(child) => {
context.children.push(child);
if shortcut!(key == shortcuts[MailView::DESCRIPTION]["go_to_url"]) {
let url_launcher = mailbox_settings!(
context[self.coordinates.0][&self.coordinates.1]
.pager
.url_launcher
)
.as_ref()
.map(|s| s.as_str())
.unwrap_or(
#[cfg(target_os = "macos")]
{
"open"
},
#[cfg(not(target_os = "macos"))]
{
"xdg-open"
},
);
match Command::new(url_launcher)
.arg(url)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
{
Ok(child) => {
context.children.push(child);
}
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some(format!("Failed to launch {:?}", url_launcher)),
err.to_string(),
Some(NotificationType::Error(melib::ErrorKind::External)),
));
}
}
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some(format!("Failed to launch {:?}", url_launcher)),
err.to_string(),
Some(NotificationType::Error(melib::ErrorKind::External)),
));
} else if shortcut!(key == shortcuts[MailView::DESCRIPTION]["copy_url"]) {
let pipe_to_clipboard = context
.settings
.terminal
.pipe_to_clipboard
.as_ref()
.map(|s| s.as_str())
.unwrap_or(
#[cfg(target_os = "macos")]
{
"pbcopy"
},
#[cfg(not(target_os = "macos"))]
{
"xclip -selection clipboard"
},
);
match Command::new(pipe_to_clipboard)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
{
Ok(mut child) => {
let mut stdin = child.stdin.as_mut().unwrap();
stdin
.write_all(url.as_bytes())
.chain_err_summary(|| {
format!(
"Failed to write to stdin of `{}`",
pipe_to_clipboard
)
})
.unwrap();
stdin.flush().unwrap();
context.children.push(child);
}
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some(format!("Failed to launch {:?}", pipe_to_clipboard)),
err.to_string(),
Some(NotificationType::Error(melib::ErrorKind::External)),
));
}
}
} else if shortcut!(key == shortcuts[MailView::DESCRIPTION]["pipe_url"]) {
let input_window = InputWindow::new("test".into(), context);
//context.replies.push_back(UIEvent::StatusEvent(
// StatusEvent::DisplayMessage(format!("Running input_window")),
//));
context
.replies
.push_back(UIEvent::GlobalUIDialog(input_window));
return true;
/*let pipe_to_clipboard = context
.settings
.terminal
.pipe_to_clipboard
.as_ref()
.map(|s| s.as_str())
.unwrap_or(
#[cfg(target_os = "macos")]
{
"pbcopy"
},
#[cfg(not(target_os = "macos"))]
{
"xclip -selection clipboard"
},
);
match Command::new(pipe_to_clipboard)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
{
Ok(mut child) => {
let mut stdin = child.stdin.as_mut().unwrap();
stdin
.write_all(url.as_bytes())
.chain_err_summary(|| {
format!(
"Failed to write to stdin of `{}`",
pipe_to_clipboard
)
})
.unwrap();
stdin.flush().unwrap();
context.children.push(child);
}
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some(format!("Failed to launch {:?}", pipe_to_clipboard)),
err.to_string(),
Some(NotificationType::Error(melib::ErrorKind::External)),
));
}
}
*/
}
}
}

View File

@ -969,3 +969,133 @@ impl UIConfirmationDialog {
})
}
}
pub struct InputWindow {
theme_default: ThemeAttribute,
vertical_alignment: Alignment,
horizontal_alignment: Alignment,
title: String,
mode: UIMode,
field: Field,
/// If true, user has finished their selection
done: bool,
done_fn:
Option<Box<dyn FnOnce(ComponentId, String) -> Option<UIEvent> + 'static + Sync + Send>>,
dirty: bool,
id: ComponentId,
}
impl InputWindow {
pub fn new(title: String, context: &Context) -> Box<dyn Component> {
Box::new(Self {
theme_default: crate::conf::value(context, "theme_default"),
vertical_alignment: Alignment::Center,
horizontal_alignment: Alignment::Center,
title,
mode: UIMode::Normal,
field: Field::default(),
/// If true, user has finished their selection
done: false,
done_fn: Some(Box::new(
|id: ComponentId, result: String| -> Option<UIEvent> {
let _ = id;
let _ = result;
None
},
)),
dirty: true,
id: ComponentId::new_v4(),
})
}
}
impl Component for InputWindow {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
std::dbg!(area);
if self.dirty {
let height = height!(area);
if height < 4 {
return;
}
let height = std::cmp::max(4, height / 3);
let dialog_area = align_area(
area,
/* add box perimeter padding */
pos_inc((width!(area), height), (2, 2)),
/* vertical */
Alignment::Center,
/* horizontal */
Alignment::Center,
);
std::dbg!(dialog_area);
clear_area(grid, dialog_area, self.theme_default);
let inner_area = create_box(grid, dialog_area);
self.field.draw(grid, inner_area, context);
context.dirty_areas.push_back(dialog_area);
self.dirty = false;
}
}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
match (self.mode, &event) {
(_, UIEvent::Resize) => {
self.set_dirty(true);
false
}
(UIMode::Normal, UIEvent::Input(Key::Char('\n'))) => {
context
.replies
.push_back(UIEvent::ChangeMode(UIMode::Insert));
true
}
(UIMode::Insert, UIEvent::InsertInput(Key::Esc)) => {
context
.replies
.push_back(UIEvent::ChangeMode(UIMode::Normal));
true
}
(UIMode::Insert, _) => self.field.process_event(event, context),
_ => false,
}
}
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
let mut map = ShortcutMaps::default();
map.insert("general", context.settings.shortcuts.general.key_values());
map
}
fn is_dirty(&self) -> bool {
self.dirty || self.field.is_dirty()
}
fn set_dirty(&mut self, value: bool) {
self.dirty = value;
self.field.set_dirty(value);
}
fn id(&self) -> ComponentId {
self.id
}
fn set_id(&mut self, id: ComponentId) {
self.id = id;
}
}
impl fmt::Debug for InputWindow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt("InputWindow", f)
}
}
impl fmt::Display for InputWindow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt("InputWindow", f)
}
}

View File

@ -225,6 +225,7 @@ impl Component for Field {
}
}
}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
match *event {
UIEvent::InsertInput(Key::Char('\t')) => {

View File

@ -222,6 +222,8 @@ shortcut_key_values! { "envelope-view",
add_addresses_to_contacts |> "Select addresses from envelope to add to contacts." |> Key::Char('c'),
edit |> "Open envelope in composer." |> Key::Char('e'),
go_to_url |> "Go to url of given index" |> Key::Char('g'),
copy_url |> "Copy url of given index to system clipboard" |> Key::Char('G'),
pipe_url |> "Pipe url of given index to specific command" |> Key::Char('w'),
open_attachment |> "Opens selected attachment with xdg-open." |> Key::Char('a'),
open_mailcap |> "Opens selected attachment according to its mailcap entry." |> Key::Char('m'),
reply |> "Reply to envelope." |> Key::Char('R'),

View File

@ -52,6 +52,8 @@ pub struct TerminalSettings {
/// Default: 0
#[serde(default)]
pub progress_spinner_sequence: Option<ProgressSpinnerSequence>,
#[serde(deserialize_with = "non_empty_string")]
pub pipe_to_clipboard: Option<String>,
}
impl Default for TerminalSettings {
@ -66,6 +68,7 @@ impl Default for TerminalSettings {
window_title: Some("meli".to_string()),
file_picker_command: None,
progress_spinner_sequence: None,
pipe_to_clipboard: None,
}
}
}
@ -99,6 +102,7 @@ impl DotAddressable for TerminalSettings {
"progress_spinner_sequence" => {
self.progress_spinner_sequence.lookup(field, tail)
}
"pipe_to_clipboard" => self.pipe_to_clipboard.lookup(field, tail),
other => Err(MeliError::new(format!(
"{} has no field named {}",
parent_field, other

View File

@ -1168,6 +1168,7 @@ impl State {
return;
}
UIEvent::GlobalUIDialog(dialog) => {
eprint!("global ui dialog push");
self.overlay.push(dialog);
return;
}