Compare commits

..

22 Commits

Author SHA1 Message Date
Benedikt Peetz 84c13fd6f8
Fix(treewide): Resolve merge conflicts 2023-07-26 22:25:08 +02:00
Benedikt Peetz 855d487693
Refactor(treewide): Remove the useless `tui_app` directory 2023-07-26 22:15:54 +02:00
Benedikt Peetz 258f784098
Fix(handlers::main): Don't close the cli after an entered command 2023-07-26 22:15:49 +02:00
Benedikt Peetz a80245c523
Fix(handlers::command): Only send success on real success
The `room_message_send()` function told the user about a successful sent
room message, even if that was not the case.
2023-07-26 22:15:18 +02:00
Benedikt Peetz f7b161fb55
Fix(handlers::command): Add the missing displayOutput handler
This handler is called by the lua executor, to show the final output of
a lua execution.
2023-07-26 22:15:17 +02:00
Benedikt Peetz 909fc01a48
Fix(app::command_interface): Add a `greet_multiple()` func to test tables
This is, like the `greet` function only here to debug the lua api. It
outputs a table.
2023-07-26 22:15:15 +02:00
Benedikt Peetz c0a1fc0a02
Fix(app::command_interface): Add a (workaround) print function
The default print function prints to stdout, which obviously
doesn't work with a tui application. This wrapper however is a
rather poor workaround, as it only works with strings (lua `print`
calls `tostring` to turn non string values in something printable).
2023-07-26 22:15:14 +02:00
Benedikt Peetz 1a35bb152c
Feat(treewide): Add a way for Commands to return more than just strings 2023-07-26 22:15:07 +02:00
Benedikt Peetz 7489f06a7c
Fix(app::command_interface): Use new language_macro api 2023-07-26 22:10:19 +02:00
Benedikt Peetz 3ca01912b9
Build(flake): Switch back to stable 2023-07-26 22:08:11 +02:00
Benedikt Peetz fbcf572f47
Build(flake+Cargo): Update lockfiles 2023-07-26 22:08:10 +02:00
Benedikt Peetz 27ad48c5e9
Refactor(language_macros): Complete rewrite
This **should** have everything the other implementation had, but the
api is implemented in a way, which is more in line with the expectation
raised at the lua functions (being that they are only wrappers over the
command api, and nothing more).

Aside of that, this version is actually documented!
2023-07-26 22:08:09 +02:00
Benedikt Peetz a3b49b17f4
Fix(handlers::main): Close ci after a command input 2023-07-26 22:08:07 +02:00
Benedikt Peetz 0288bdb0ad
Feat(ui): Add status panel, which shows command statuses and errors 2023-07-26 22:08:00 +02:00
Benedikt Peetz 1fe04ca5c6
Fix(lua_command::handle): Move lua_command handler to separate thread
This makes it possible to have lua code execute commands and receive
their output value, without risking a deadlock.
2023-07-26 22:06:42 +02:00
Benedikt Peetz c7a4d5a8ab
Refactor(treewide): Remove the repl, reuse of e. handling is hard
The event handling is deeply ingrained in the ui code, the commands are
focused around the ui code, in short splitting of the event handling and
command system from the ui is intentionally hard and in my opinion not
really worth it right now.
2023-07-26 22:06:40 +02:00
Benedikt Peetz 189ae509f8
Fix(app::command_interface): Provide pre-generated file, to check syntax 2023-07-26 22:06:33 +02:00
Benedikt Peetz ebb16a20de
Feat(treewide): Add a feature based layout and repl subcommand
Compiling the whole tui stack, just to debug the lua command line seems
counterproductive to me. This allows to just compile the needed parts
for a basic lua repl.

As of yet the repl is just a mock-up, as the event handling can, as of
right now, not easily be separated from the tui.

To activate specific features add specify the on the cargo command line
like this:
```
cargo run --features "cli tui"
```
or add them to the `default` feature set in the `Cargo.toml`.
2023-07-26 22:04:42 +02:00
antifallobst 993ef6af89
Fix(insert_modes): added the `Setup` insert mode as a workaround to log in until command based login is implemented 2023-07-22 16:28:47 +02:00
antifallobst d447eb2312
Feat(App): implemented vim like modes (Normal/Insert) 2023-07-22 16:00:52 +02:00
antifallobst 4856ecc582
Fix(event_handlers): removed outdated reference to the ci_output module 2023-07-21 21:34:46 +02:00
antifallobst dcf87f257d Merge: PR-11 (commands) -> master 2023-07-21 18:57:03 +00:00
8 changed files with 126 additions and 53 deletions

View File

@ -40,6 +40,11 @@ struct Commands {
/// Go to the previous plane
cycle_planes_rev: fn(),
/// Sets the current app mode to Normal / navigation mode
set_mode_normal: fn(),
/// Sets the current app mode to Insert / editing mode
set_mode_insert: fn(),
/// Send a message to the current room
/// The sent message is interpreted literally.
room_message_send: fn(String),

View File

@ -4,14 +4,14 @@ use anyhow::{Error, Result};
use cli_log::{trace, warn};
use tokio::sync::oneshot;
use crate::app::{
use crate::{app::{
command_interface::{
lua_command_manager::{CommandTransferValue, Table},
Command,
},
events::event_types::EventStatus,
App,
};
App, status::State,
}, ui::central::InputPosition};
pub async fn handle(
app: &mut App<'_>,
@ -97,6 +97,18 @@ pub async fn handle(
EventStatus::Ok
}
Command::SetModeNormal => {
app.status.set_state(State::Normal);
send_status_output!("Set input mode to Normal");
EventStatus::Ok
}
Command::SetModeInsert => {
app.status.set_state(State::Insert);
app.ui.set_input_position(InputPosition::MessageCompose);
send_status_output!("Set input mode to Insert");
EventStatus::Ok
}
Command::RoomMessageSend(msg) => {
if let Some(room) = app.status.room_mut() {
room.send(msg.clone()).await?;

View File

@ -10,7 +10,7 @@ use crate::{
ui::central,
};
pub async fn handle(
pub async fn handle_normal(
app: &mut App<'_>,
input_event: &CrosstermEvent,
) -> Result<EventStatus> {
@ -38,37 +38,22 @@ pub async fn handle(
.await?;
}
CrosstermEvent::Key(KeyEvent {
code: KeyCode::Char('c'),
modifiers: KeyModifiers::CONTROL,
code: KeyCode::Char(':'),
..
}) => {
app.tx
.send(Event::CommandEvent(Command::CommandLineShow, None))
.await?;
}
CrosstermEvent::Key(KeyEvent {
code: KeyCode::Char('i'),
..
}) => {
app.tx
.send(Event::CommandEvent(Command::SetModeInsert, None))
.await?;
}
input => match app.ui.input_position() {
central::InputPosition::MessageCompose => {
match input {
CrosstermEvent::Key(KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::ALT,
..
}) => {
app.tx
.send(Event::CommandEvent(
Command::RoomMessageSend(app.ui.message_compose.lines().join("\n")),
None,
))
.await?;
app.ui.message_compose_clear();
}
_ => {
app.ui
.message_compose
.input(tui_textarea::Input::from(input.to_owned()));
}
};
}
central::InputPosition::Rooms => {
match input {
CrosstermEvent::Key(KeyEvent {
@ -189,3 +174,35 @@ pub async fn handle(
};
Ok(EventStatus::Ok)
}
pub async fn handle_insert(app: &mut App<'_>, event: &CrosstermEvent) -> Result<EventStatus> {
match event {
CrosstermEvent::Key(KeyEvent {
code: KeyCode::Esc, ..
}) => {
app.tx
.send(Event::CommandEvent(Command::SetModeNormal, None))
.await?;
}
CrosstermEvent::Key(KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::ALT,
..
}) => {
app.tx
.send(Event::CommandEvent(
Command::RoomMessageSend(app.ui.message_compose.lines().join("\n")),
None,
))
.await?;
app.ui.message_compose_clear();
}
_ => {
app.ui
.message_compose
.input(tui_textarea::Input::from(event.to_owned()));
}
};
Ok(EventStatus::Ok)
}

View File

@ -5,7 +5,11 @@ use cli_log::trace;
use crossterm::event::Event as CrosstermEvent;
use tokio::sync::oneshot;
use crate::app::{command_interface::{Command, lua_command_manager::CommandTransferValue}, status::State, App};
use crate::app::{
command_interface::{lua_command_manager::CommandTransferValue, Command},
status::State,
App,
};
use self::handlers::{command, lua_command, main, matrix, setup};
@ -35,15 +39,15 @@ impl Event {
.with_context(|| format!("Failed to handle lua code: `{}`", lua_code)),
Event::InputEvent(event) => match app.status.state() {
State::None => unreachable!(
"This state should not be available, when we are in the input handling"
),
State::Main => main::handle(app, &event)
.await
.with_context(|| format!("Failed to handle input event: `{:#?}`", event)),
State::Setup => setup::handle(app, &event)
.await
.with_context(|| format!("Failed to handle input event: `{:#?}`", event)),
State::Normal => main::handle_normal(app, &event).await.with_context(|| {
format!("Failed to handle input (normal) event: `{:#?}`", event)
}),
State::Insert => main::handle_insert(app, &event).await.with_context(|| {
format!("Failed to handle input (insert) event: `{:#?}`", event)
}),
State::Setup => setup::handle(app, &event).await.with_context(|| {
format!("Failed to handle input (setup) event: `{:#?}`", event)
}),
},
}
}

View File

@ -75,8 +75,9 @@ impl App<'_> {
self.init_account().await?;
}
self.status.set_state(State::Normal);
loop {
self.status.set_state(State::Main);
self.ui.update(&self.status).await?;
let event = self.rx.recv().await.context("Failed to get next event")?;
@ -94,8 +95,9 @@ impl App<'_> {
async fn setup(&mut self) -> Result<()> {
self.ui.setup_ui = Some(setup::UI::new());
self.status.set_state(State::Setup);
loop {
self.status.set_state(State::Setup);
self.ui.update_setup().await?;
let event = self.rx.recv().await.context("Failed to get next event")?;

View File

@ -1,3 +1,5 @@
use core::fmt;
use anyhow::{Error, Result};
use cli_log::warn;
use indexmap::IndexMap;
@ -11,8 +13,9 @@ use matrix_sdk::{
};
pub enum State {
None,
Main,
Normal,
Insert,
/// Temporary workaround until command based login is working
Setup,
}
@ -49,6 +52,16 @@ pub struct Status {
status_messages: Vec<StatusMessage>,
}
impl fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Normal => write!(f, "Normal"),
Self::Insert => write!(f, "Insert"),
Self::Setup => write!(f, "Setup (!! workaround !!)"),
}
}
}
impl Room {
pub fn new(matrix_room: matrix_sdk::room::Joined) -> Self {
Self {
@ -138,10 +151,10 @@ impl Status {
};
Self {
state: State::None,
state: State::Normal,
account_name: "".to_owned(),
account_user_id: "".to_owned(),
client,
client,
rooms,
current_room_id: "".to_owned(),
status_messages: vec![StatusMessage {

View File

@ -226,6 +226,10 @@ impl UI<'_> {
};
}
pub fn set_input_position(&mut self, position: InputPosition) {
self.input_position = position;
}
pub fn input_position(&self) -> &InputPosition {
&self.input_position
}

View File

@ -1,7 +1,7 @@
use std::cmp;
use anyhow::{Context, Result};
use tui::layout::{Constraint, Direction, Layout};
use tui::{layout::{Constraint, Direction, Layout}, widgets::{Paragraph, Block, Borders}, style::{Style, Color}};
use crate::app::status::Status;
@ -13,13 +13,21 @@ pub mod widgets;
impl UI<'_> {
pub async fn update(&mut self, status: &Status) -> Result<()> {
let chunks = match self.cli {
Some(_) => Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(10), Constraint::Length(3)].as_ref())
.split(self.terminal.size()?),
None => vec![self.terminal.size()?],
};
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(10), Constraint::Length(3)].as_ref())
.split(self.terminal.size()?);
let bottom_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Length((status.state().to_string().len() + 2) as u16),
Constraint::Min(16),
]
.as_ref(),
)
.split(chunks[1]);
let main_chunks = Layout::default()
.direction(Direction::Horizontal)
@ -59,6 +67,13 @@ impl UI<'_> {
.colors(&mut self.cli, &mut self.message_compose);
// initiate the widgets
let mode_indicator = Paragraph::new(status.state().to_string())
.block(
Block::default()
.borders(Borders::ALL)
.style(Style::default().fg(Color::DarkGray)),
)
.style(Style::default().fg(Color::LightYellow));
let status_panel = status::init(status, &colors);
let rooms_panel = rooms::init(status, &colors);
let (messages_panel, mut messages_state) = messages::init(status.room(), &colors)
@ -72,8 +87,9 @@ impl UI<'_> {
frame.render_stateful_widget(rooms_panel, left_chunks[1], &mut self.rooms_state);
frame.render_stateful_widget(messages_panel, middle_chunks[0], &mut messages_state);
frame.render_widget(self.message_compose.widget(), middle_chunks[1]);
frame.render_widget(mode_indicator, bottom_chunks[0]);
match &self.cli {
Some(cli) => frame.render_widget(cli.widget(), chunks[1]),
Some(cli) => frame.render_widget(cli.widget(), bottom_chunks[1]),
None => (),
};
frame.render_widget(room_info_panel, right_chunks[0]);