diff --git a/src/app.rs b/src/app.rs index 8ba3f1ea..308ca85d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,7 +3,7 @@ use crate::{ components::{ event_pump, CommandBlocking, CommandInfo, CommitComponent, Component, DrawableComponent, HelpComponent, MsgComponent, - ResetComponent, + ResetComponent, StashMsgComponent, }, keys, queue::{InternalEvent, NeedsUpdate, Queue}, @@ -34,6 +34,7 @@ pub struct App { msg: MsgComponent, reset: ResetComponent, commit: CommitComponent, + stashmsg_popup: StashMsgComponent, current_commands: Vec, tab: usize, revlog: Revlog, @@ -54,6 +55,10 @@ impl App { Self { reset: ResetComponent::new(queue.clone(), &theme), commit: CommitComponent::new(queue.clone(), &theme), + stashmsg_popup: StashMsgComponent::new( + queue.clone(), + &theme, + ), do_quit: false, current_commands: Vec::new(), help: HelpComponent::new(&theme), @@ -181,7 +186,16 @@ impl App { impl App { accessors!( self, - [msg, reset, commit, help, revlog, status_tab, stashing_tab] + [ + msg, + reset, + commit, + stashmsg_popup, + help, + revlog, + status_tab, + stashing_tab + ] ); fn check_quit(&mut self, ev: Event) -> bool { @@ -292,6 +306,9 @@ impl App { } InternalEvent::Update(u) => flags.insert(u), InternalEvent::OpenCommit => self.commit.show(), + InternalEvent::PopupStashing(_opts) => { + self.stashmsg_popup.show() + } }; flags @@ -341,6 +358,7 @@ impl App { let size = f.size(); self.commit.draw(f, size); + self.stashmsg_popup.draw(f, size); self.reset.draw(f, size); self.help.draw(f, size); self.msg.draw(f, size); diff --git a/src/components/commit.rs b/src/components/commit.rs index d1f2beee..5468179a 100644 --- a/src/components/commit.rs +++ b/src/components/commit.rs @@ -29,18 +29,15 @@ impl Component for CommitComponent { fn commands( &self, out: &mut Vec, - _force_all: bool, + force_all: bool, ) -> CommandBlocking { + self.input.commands(out, force_all); + out.push(CommandInfo::new( commands::COMMIT_ENTER, self.can_commit(), self.is_visible(), )); - out.push(CommandInfo::new( - commands::CLOSE_POPUP, - true, - self.is_visible(), - )); visibility_blocking(self) } diff --git a/src/components/mod.rs b/src/components/mod.rs index 41e0b6f1..254b21fa 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -6,6 +6,7 @@ mod filetree; mod help; mod msg; mod reset; +mod stashmsg; mod textinput; mod utils; pub use changes::ChangesComponent; @@ -16,6 +17,7 @@ pub use filetree::FileTreeComponent; pub use help::HelpComponent; pub use msg::MsgComponent; pub use reset::ResetComponent; +pub use stashmsg::StashMsgComponent; pub use utils::filetree::FileTreeItemKind; use crossterm::event::Event; diff --git a/src/components/stashmsg.rs b/src/components/stashmsg.rs new file mode 100644 index 00000000..5f290a58 --- /dev/null +++ b/src/components/stashmsg.rs @@ -0,0 +1,99 @@ +use super::{ + textinput::TextInputComponent, visibility_blocking, + CommandBlocking, CommandInfo, Component, DrawableComponent, +}; +use crate::{ + queue::{InternalEvent, NeedsUpdate, Queue}, + strings, + tabs::StashingOptions, + ui::style::Theme, +}; +use asyncgit::{sync, CWD}; +use crossterm::event::{Event, KeyCode}; +use strings::commands; +use tui::{backend::Backend, layout::Rect, Frame}; + +pub struct StashMsgComponent { + options: StashingOptions, + input: TextInputComponent, + queue: Queue, +} + +impl DrawableComponent for StashMsgComponent { + fn draw(&mut self, f: &mut Frame, rect: Rect) { + self.input.draw(f, rect) + } +} + +impl Component for StashMsgComponent { + fn commands( + &self, + out: &mut Vec, + force_all: bool, + ) -> CommandBlocking { + self.input.commands(out, force_all); + + out.push(CommandInfo::new( + commands::STASHING_CONFIRM_MSG, + true, + self.is_visible() || force_all, + )); + visibility_blocking(self) + } + + fn event(&mut self, ev: Event) -> bool { + if self.is_visible() { + if self.input.event(ev) { + return true; + } + + if let Event::Key(e) = ev { + if let KeyCode::Enter = e.code { + if sync::stash_save( + CWD, + if self.input.get_text().is_empty() { + None + } else { + Some(self.input.get_text().as_str()) + }, + self.options.stash_untracked, + !self.options.stash_indexed, + ) + .is_ok() + { + self.queue.borrow_mut().push_back( + InternalEvent::Update(NeedsUpdate::ALL), + ); + } + } + + // stop key event propagation + return true; + } + } + false + } + + fn is_visible(&self) -> bool { + self.input.is_visible() + } + + fn hide(&mut self) { + self.input.hide() + } + + fn show(&mut self) { + self.input.show() + } +} + +impl StashMsgComponent { + /// + pub fn new(queue: Queue, theme: &Theme) -> Self { + Self { + options: StashingOptions::default(), + queue, + input: TextInputComponent::new(theme), + } + } +} diff --git a/src/components/textinput.rs b/src/components/textinput.rs index 1c9b7806..4fe7ba1d 100644 --- a/src/components/textinput.rs +++ b/src/components/textinput.rs @@ -75,11 +75,14 @@ impl Component for TextInputComponent { out: &mut Vec, _force_all: bool, ) -> CommandBlocking { - out.push(CommandInfo::new( - commands::CLOSE_POPUP, - true, - self.visible, - )); + out.push( + CommandInfo::new( + commands::CLOSE_POPUP, + true, + self.visible, + ) + .order(1), + ); visibility_blocking(self) } diff --git a/src/queue.rs b/src/queue.rs index ec81a5fe..afe351ed 100644 --- a/src/queue.rs +++ b/src/queue.rs @@ -1,5 +1,6 @@ use bitflags::bitflags; use std::{cell::RefCell, collections::VecDeque, rc::Rc}; +use crate::tabs::StashingOptions; bitflags! { /// flags defining what part of the app need to update @@ -35,6 +36,8 @@ pub enum InternalEvent { Update(NeedsUpdate), /// OpenCommit, + /// + PopupStashing(StashingOptions), } /// diff --git a/src/strings.rs b/src/strings.rs index 8cca32c4..ee07859e 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -161,7 +161,7 @@ pub mod commands { /// pub static STASHING_SAVE: CommandText = CommandText::new( "Save [s]", - "creates a new stash", + "opens stash name input popup", CMD_GROUP_STASHING, ); /// @@ -178,4 +178,10 @@ pub mod commands { "toggle including untracked files into stash", CMD_GROUP_STASHING, ); + /// + pub static STASHING_CONFIRM_MSG: CommandText = CommandText::new( + "Stash [enter]", + "save files to stash", + CMD_GROUP_STASHING, + ); } diff --git a/src/tabs/mod.rs b/src/tabs/mod.rs index 991fbb88..501f6f37 100644 --- a/src/tabs/mod.rs +++ b/src/tabs/mod.rs @@ -3,5 +3,5 @@ mod stashing; mod status; pub use revlog::Revlog; -pub use stashing::Stashing; +pub use stashing::{Stashing, StashingOptions}; pub use status::Status; diff --git a/src/tabs/stashing.rs b/src/tabs/stashing.rs index cd1d8337..af43aec8 100644 --- a/src/tabs/stashing.rs +++ b/src/tabs/stashing.rs @@ -1,17 +1,18 @@ use crate::{ accessors, components::{ - command_pump, event_pump, CommandBlocking, CommandInfo, - Component, DrawableComponent, FileTreeComponent, + command_pump, event_pump, visibility_blocking, + CommandBlocking, CommandInfo, Component, DrawableComponent, + FileTreeComponent, }, keys, - queue::{InternalEvent, NeedsUpdate, Queue}, + queue::{InternalEvent, Queue}, strings, ui::style::Theme, }; use asyncgit::{ - sync::{self, status::StatusType}, - AsyncNotification, AsyncStatus, StatusParams, CWD, + sync::status::StatusType, AsyncNotification, AsyncStatus, + StatusParams, }; use crossbeam_channel::Sender; use crossterm::event::Event; @@ -22,15 +23,16 @@ use tui::{ widgets::{Block, Borders, Paragraph, Text}, }; -struct Options { - stash_untracked: bool, - stash_indexed: bool, +#[derive(Default, Clone, Copy)] +pub struct StashingOptions { + pub stash_untracked: bool, + pub stash_indexed: bool, } pub struct Stashing { - visible: bool, - options: Options, index: FileTreeComponent, + visible: bool, + options: StashingOptions, theme: Theme, git_status: AsyncStatus, queue: Queue, @@ -46,17 +48,17 @@ impl Stashing { theme: &Theme, ) -> Self { Self { - visible: false, - options: Options { - stash_indexed: true, - stash_untracked: true, - }, index: FileTreeComponent::new( strings::STASHING_FILES_TITLE, true, queue.clone(), theme, ), + visible: false, + options: StashingOptions { + stash_indexed: true, + stash_untracked: true, + }, theme: *theme, git_status: AsyncStatus::new(sender.clone()), queue: queue.clone(), @@ -144,8 +146,6 @@ impl DrawableComponent for Stashing { ) .split(chunks[1]); - self.index.draw(f, chunks[0]); - f.render_widget( Paragraph::new(self.get_option_text().iter()) .block( @@ -156,6 +156,8 @@ impl DrawableComponent for Stashing { .alignment(Alignment::Left), right_chunks[0], ); + + self.index.draw(f, chunks[0]); } } @@ -183,11 +185,7 @@ impl Component for Stashing { self.visible || force_all, )); - if self.visible { - CommandBlocking::Blocking - } else { - CommandBlocking::PassingOn - } + visibility_blocking(self) } fn event(&mut self, ev: crossterm::event::Event) -> bool { @@ -199,20 +197,12 @@ impl Component for Stashing { if let Event::Key(k) = ev { return match k { keys::STASHING_SAVE if !self.index.is_empty() => { - if sync::stash_save( - CWD, - None, - self.options.stash_untracked, - !self.options.stash_indexed, - ) - .is_ok() - { - self.queue.borrow_mut().push_back( - InternalEvent::Update( - NeedsUpdate::ALL, - ), - ); - } + self.queue.borrow_mut().push_back( + InternalEvent::PopupStashing( + self.options, + ), + ); + true } keys::STASHING_TOGGLE_INDEX => { diff --git a/src/tabs/status.rs b/src/tabs/status.rs index bf731a6a..41f96be4 100644 --- a/src/tabs/status.rs +++ b/src/tabs/status.rs @@ -14,7 +14,7 @@ use asyncgit::{ sync::status::StatusType, AsyncDiff, AsyncNotification, AsyncStatus, DiffParams, StatusParams, }; -use components::command_pump; +use components::{command_pump, visibility_blocking}; use crossbeam_channel::Sender; use crossterm::event::Event; use strings::commands; @@ -314,11 +314,7 @@ impl Component for Status { ); } - if self.visible { - CommandBlocking::Blocking - } else { - CommandBlocking::PassingOn - } + visibility_blocking(self) } fn event(&mut self, ev: crossterm::event::Event) -> bool {