summaryrefslogtreecommitdiffstats
path: root/src/run.rs
blob: a2e81b35ecf26913563afe44599ce02c43fd49c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::commands::quit::QuitAction;
use crate::config::AppKeyMapping;
use crate::context::AppContext;
use crate::event::process_event;
use crate::event::AppEvent;
use crate::key_command::{AppExecute, CommandKeybind};
use crate::preview::preview_default;
use crate::tab::JoshutoTab;
use crate::traits::ToString;
use crate::ui;
use crate::ui::views;
use crate::ui::views::TuiView;

use uuid::Uuid;

use ratatui::layout::Rect;
use termion::event::Event;

pub fn run_loop(
    backend: &mut ui::AppBackend,
    context: &mut AppContext,
    keymap_t: AppKeyMapping,
) -> std::io::Result<()> {
    let curr_path = std::env::current_dir()?;

    if let Ok(area) = backend.terminal_ref().size() {
        // pre-calculate some ui attributes
        calculate_ui_context(context, area);
    }

    {
        let id = Uuid::new_v4();
        // Initialize an initial tab
        let tab = JoshutoTab::new(
            curr_path,
            context.ui_context_ref(),
            context.config_ref().display_options_ref(),
        )?;
        context.tab_context_mut().insert_tab(id, tab);

        // trigger a preview of child
        preview_default::load_preview(context, backend);
    }

    while context.quit == QuitAction::DoNot {
        // do the ui
        if let Ok(area) = backend.terminal_ref().size() {
            // pre-calculate some ui attributes
            calculate_ui_context(context, area);

            // render the ui
            backend.render(TuiView::new(context));

            // invoke preview hooks, if appropriate
            context.update_external_preview();
        }

        // wait for an event and pop it
        let event = match context.poll_event() {
            Ok(event) => event,
            Err(_) => return Ok(()), // TODO
        };

        // handle the event
        match event {
            AppEvent::Termion(Event::Mouse(event)) => {
                process_event::process_mouse(event, context, backend, &keymap_t);
                preview_default::load_preview(context, backend);
            }
            AppEvent::Termion(key) => {
                if context.message_queue_ref().current_message().is_some() {
                    context.message_queue_mut().pop_front();
                }
                match key {
                    // in the event where mouse input is not supported
                    // but we still want to register scroll
                    Event::Unsupported(s) => {
                        process_event::process_unsupported(context, backend, &keymap_t, s);
                    }
                    key => match keymap_t.default_view.get(&key) {
                        None => {
                            context
                                .message_queue_mut()
                                .push_info(format!("Unmapped input: {}", key.to_string()));
                        }
                        Some(CommandKeybind::SimpleKeybind(command)) => {
                            if let Err(e) = command.execute(context, backend, &keymap_t) {
                                context.message_queue_mut().push_error(e.to_string());
                            }
                        }
                        Some(CommandKeybind::CompositeKeybind(m)) => {
                            let cmd =
                                process_event::poll_event_until_simple_keybind(backend, context, m);

                            if let Some(command) = cmd {
                                if let Err(e) = command.execute(context, backend, &keymap_t) {
                                    context.message_queue_mut().push_error(e.to_string());
                                }
                            }
                        }
                    },
                }
                preview_default::load_preview(context, backend);
                context.flush_event();
            }
            event => process_event::process_noninteractive(event, context),
        }

        // update the file system supervisor that watches for changes in the FS
        if context.config_ref().watch_files {
            context.update_watcher();
        }
    } // end of main loop
    Ok(())
}

fn calculate_ui_context(context: &mut AppContext, area: Rect) {
    let area = Rect {
        y: area.top() + 1,
        height: area.height - 2,
        ..area
    };
    let config = context.config_ref();
    let display_options = config.display_options_ref();
    let constraints = views::get_constraints(context);
    let layout = if display_options.show_borders() {
        views::calculate_layout_with_borders(area, constraints)
    } else {
        views::calculate_layout(area, constraints)
    };
    context.ui_context_mut().layout = layout;
}