mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
split commitComponent to have generic reusable textInputComponent
This commit is contained in:
parent
37aa67f8a4
commit
b34deddb0e
3 changed files with 146 additions and 58 deletions
|
|
@ -1,56 +1,27 @@
|
||||||
use super::{
|
use super::{
|
||||||
visibility_blocking, CommandBlocking, CommandInfo, Component,
|
textinput::TextInputComponent, visibility_blocking,
|
||||||
DrawableComponent,
|
CommandBlocking, CommandInfo, Component, DrawableComponent,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
components::dialog_paragraph,
|
|
||||||
queue::{InternalEvent, NeedsUpdate, Queue},
|
queue::{InternalEvent, NeedsUpdate, Queue},
|
||||||
strings, ui,
|
strings,
|
||||||
ui::style::Theme,
|
ui::style::Theme,
|
||||||
};
|
};
|
||||||
use asyncgit::{sync, CWD};
|
use asyncgit::{sync, CWD};
|
||||||
use crossterm::event::{Event, KeyCode};
|
use crossterm::event::{Event, KeyCode};
|
||||||
use log::error;
|
use log::error;
|
||||||
use std::borrow::Cow;
|
|
||||||
use strings::commands;
|
use strings::commands;
|
||||||
use sync::HookResult;
|
use sync::HookResult;
|
||||||
use tui::{
|
use tui::{backend::Backend, layout::Rect, Frame};
|
||||||
backend::Backend,
|
|
||||||
layout::Rect,
|
|
||||||
style::Style,
|
|
||||||
widgets::{Clear, Text},
|
|
||||||
Frame,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct CommitComponent {
|
pub struct CommitComponent {
|
||||||
msg: String,
|
input: TextInputComponent,
|
||||||
visible: bool,
|
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
theme: Theme,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawableComponent for CommitComponent {
|
impl DrawableComponent for CommitComponent {
|
||||||
fn draw<B: Backend>(&mut self, f: &mut Frame<B>, _rect: Rect) {
|
fn draw<B: Backend>(&mut self, f: &mut Frame<B>, rect: Rect) {
|
||||||
if self.visible {
|
self.input.draw(f, rect)
|
||||||
let txt = if self.msg.is_empty() {
|
|
||||||
[Text::Styled(
|
|
||||||
Cow::from(strings::COMMIT_MSG),
|
|
||||||
self.theme.text(false, false),
|
|
||||||
)]
|
|
||||||
} else {
|
|
||||||
[Text::Styled(
|
|
||||||
Cow::from(self.msg.clone()),
|
|
||||||
Style::default(),
|
|
||||||
)]
|
|
||||||
};
|
|
||||||
|
|
||||||
let area = ui::centered_rect(60, 20, f.size());
|
|
||||||
f.render_widget(Clear, area);
|
|
||||||
f.render_widget(
|
|
||||||
dialog_paragraph(strings::COMMIT_TITLE, txt.iter()),
|
|
||||||
area,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,34 +34,31 @@ impl Component for CommitComponent {
|
||||||
out.push(CommandInfo::new(
|
out.push(CommandInfo::new(
|
||||||
commands::COMMIT_ENTER,
|
commands::COMMIT_ENTER,
|
||||||
self.can_commit(),
|
self.can_commit(),
|
||||||
self.visible,
|
self.is_visible(),
|
||||||
));
|
));
|
||||||
out.push(CommandInfo::new(
|
out.push(CommandInfo::new(
|
||||||
commands::CLOSE_POPUP,
|
commands::CLOSE_POPUP,
|
||||||
true,
|
true,
|
||||||
self.visible,
|
self.is_visible(),
|
||||||
));
|
));
|
||||||
visibility_blocking(self)
|
visibility_blocking(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event(&mut self, ev: Event) -> bool {
|
fn event(&mut self, ev: Event) -> bool {
|
||||||
if self.visible {
|
if self.is_visible() {
|
||||||
|
if self.input.event(ev) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if let Event::Key(e) = ev {
|
if let Event::Key(e) = ev {
|
||||||
match e.code {
|
match e.code {
|
||||||
KeyCode::Esc => {
|
|
||||||
self.hide();
|
|
||||||
}
|
|
||||||
KeyCode::Char(c) => {
|
|
||||||
self.msg.push(c);
|
|
||||||
}
|
|
||||||
KeyCode::Enter if self.can_commit() => {
|
KeyCode::Enter if self.can_commit() => {
|
||||||
self.commit();
|
self.commit();
|
||||||
}
|
}
|
||||||
KeyCode::Backspace if !self.msg.is_empty() => {
|
|
||||||
self.msg.pop().unwrap();
|
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// stop key event propagation
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -98,15 +66,15 @@ impl Component for CommitComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_visible(&self) -> bool {
|
fn is_visible(&self) -> bool {
|
||||||
self.visible
|
self.input.is_visible()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hide(&mut self) {
|
fn hide(&mut self) {
|
||||||
self.visible = false
|
self.input.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self) {
|
fn show(&mut self) {
|
||||||
self.visible = true
|
self.input.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,15 +83,14 @@ impl CommitComponent {
|
||||||
pub fn new(queue: Queue, theme: &Theme) -> Self {
|
pub fn new(queue: Queue, theme: &Theme) -> Self {
|
||||||
Self {
|
Self {
|
||||||
queue,
|
queue,
|
||||||
msg: String::default(),
|
input: TextInputComponent::new(theme),
|
||||||
visible: false,
|
|
||||||
theme: *theme,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn commit(&mut self) {
|
fn commit(&mut self) {
|
||||||
|
let mut msg = self.input.get_text().clone();
|
||||||
if let HookResult::NotOk(e) =
|
if let HookResult::NotOk(e) =
|
||||||
sync::hooks_commit_msg(CWD, &mut self.msg).unwrap()
|
sync::hooks_commit_msg(CWD, &mut msg).unwrap()
|
||||||
{
|
{
|
||||||
error!("commit-msg hook error: {}", e);
|
error!("commit-msg hook error: {}", e);
|
||||||
self.queue.borrow_mut().push_back(
|
self.queue.borrow_mut().push_back(
|
||||||
|
|
@ -135,7 +102,7 @@ impl CommitComponent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = sync::commit(CWD, &self.msg) {
|
if let Err(e) = sync::commit(CWD, &msg) {
|
||||||
error!("commit error: {}", &e);
|
error!("commit error: {}", &e);
|
||||||
self.queue.borrow_mut().push_back(
|
self.queue.borrow_mut().push_back(
|
||||||
InternalEvent::ShowErrorMsg(format!(
|
InternalEvent::ShowErrorMsg(format!(
|
||||||
|
|
@ -158,7 +125,7 @@ impl CommitComponent {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.msg.clear();
|
self.input.clear();
|
||||||
self.hide();
|
self.hide();
|
||||||
|
|
||||||
self.queue
|
self.queue
|
||||||
|
|
@ -167,6 +134,6 @@ impl CommitComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_commit(&self) -> bool {
|
fn can_commit(&self) -> bool {
|
||||||
!self.msg.is_empty()
|
!self.input.get_text().is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ mod filetree;
|
||||||
mod help;
|
mod help;
|
||||||
mod msg;
|
mod msg;
|
||||||
mod reset;
|
mod reset;
|
||||||
|
mod textinput;
|
||||||
mod utils;
|
mod utils;
|
||||||
pub use changes::ChangesComponent;
|
pub use changes::ChangesComponent;
|
||||||
pub use command::{CommandInfo, CommandText};
|
pub use command::{CommandInfo, CommandText};
|
||||||
|
|
|
||||||
120
src/components/textinput.rs
Normal file
120
src/components/textinput.rs
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
use super::{
|
||||||
|
visibility_blocking, CommandBlocking, CommandInfo, Component,
|
||||||
|
DrawableComponent,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
components::dialog_paragraph, strings, ui, ui::style::Theme,
|
||||||
|
};
|
||||||
|
use crossterm::event::{Event, KeyCode};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use strings::commands;
|
||||||
|
use tui::{
|
||||||
|
backend::Backend,
|
||||||
|
layout::Rect,
|
||||||
|
style::Style,
|
||||||
|
widgets::{Clear, Text},
|
||||||
|
Frame,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// primarily a subcomponet for user input of text (used in `CommitComponent`)
|
||||||
|
pub struct TextInputComponent {
|
||||||
|
msg: String,
|
||||||
|
visible: bool,
|
||||||
|
theme: Theme,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TextInputComponent {
|
||||||
|
///
|
||||||
|
pub fn new(theme: &Theme) -> Self {
|
||||||
|
Self {
|
||||||
|
msg: String::default(),
|
||||||
|
visible: false,
|
||||||
|
theme: *theme,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.msg.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn get_text(&self) -> &String {
|
||||||
|
&self.msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrawableComponent for TextInputComponent {
|
||||||
|
fn draw<B: Backend>(&mut self, f: &mut Frame<B>, _rect: Rect) {
|
||||||
|
if self.visible {
|
||||||
|
let txt = if self.msg.is_empty() {
|
||||||
|
[Text::Styled(
|
||||||
|
Cow::from(strings::COMMIT_MSG),
|
||||||
|
self.theme.text(false, false),
|
||||||
|
)]
|
||||||
|
} else {
|
||||||
|
[Text::Styled(
|
||||||
|
Cow::from(self.msg.clone()),
|
||||||
|
Style::default(),
|
||||||
|
)]
|
||||||
|
};
|
||||||
|
|
||||||
|
let area = ui::centered_rect(60, 20, f.size());
|
||||||
|
f.render_widget(Clear, area);
|
||||||
|
f.render_widget(
|
||||||
|
dialog_paragraph(strings::COMMIT_TITLE, txt.iter()),
|
||||||
|
area,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for TextInputComponent {
|
||||||
|
fn commands(
|
||||||
|
&self,
|
||||||
|
out: &mut Vec<CommandInfo>,
|
||||||
|
_force_all: bool,
|
||||||
|
) -> CommandBlocking {
|
||||||
|
out.push(CommandInfo::new(
|
||||||
|
commands::CLOSE_POPUP,
|
||||||
|
true,
|
||||||
|
self.visible,
|
||||||
|
));
|
||||||
|
visibility_blocking(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event(&mut self, ev: Event) -> bool {
|
||||||
|
if self.visible {
|
||||||
|
if let Event::Key(e) = ev {
|
||||||
|
match e.code {
|
||||||
|
KeyCode::Esc => {
|
||||||
|
self.hide();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
KeyCode::Char(c) => {
|
||||||
|
self.msg.push(c);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
KeyCode::Backspace => {
|
||||||
|
self.msg.pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_visible(&self) -> bool {
|
||||||
|
self.visible
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hide(&mut self) {
|
||||||
|
self.visible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&mut self) {
|
||||||
|
self.visible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue